/* * Copyright 2001-2008 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT license * * Authors: * Stefano Ceccherini, burton666@libero.it */ #include #include #include #include BBufferIO::BBufferIO(BPositionIO* stream, size_t bufferSize, bool ownsStream) : fBufferStart(0), fStream(stream), fBuffer(NULL), fBufferUsed(0), fBufferIsDirty(false), fOwnsStream(ownsStream) { fBufferSize = max_c(bufferSize, 512); fPosition = stream->Position(); // What can we do if this malloc fails ? // I think R5 uses new, but doesn't catch the thrown exception // (if you specify a very big buffer, the application just // terminates with abort). fBuffer = (char*)malloc(fBufferSize); } BBufferIO::~BBufferIO() { if (fBufferIsDirty) { // Write pending changes to the stream Flush(); } free(fBuffer); if (fOwnsStream) delete fStream; } ssize_t BBufferIO::ReadAt(off_t pos, void* buffer, size_t size) { // We refuse to crash, even if // you were lazy and didn't give a valid // stream on construction. if (fStream == NULL) return B_NO_INIT; if (buffer == NULL) return B_BAD_VALUE; // If the amount of data we want doesn't fit in the buffer, just // read it directly from the disk (and don't touch the buffer). if (size > fBufferSize || fBuffer == NULL) { if (fBufferIsDirty) Flush(); return fStream->ReadAt(pos, buffer, size); } // If the data we are looking for is not in the buffer... if (size > fBufferUsed || pos < fBufferStart || pos > fBufferStart + (off_t)fBufferUsed || pos + size > fBufferStart + fBufferUsed) { if (fBufferIsDirty) { // If there are pending writes, do them. Flush(); } // ...cache as much as we can from the stream ssize_t sizeRead = fStream->ReadAt(pos, fBuffer, fBufferSize); if (sizeRead < 0) return sizeRead; fBufferUsed = sizeRead; if (fBufferUsed > 0) { // The data is buffered starting from this offset fBufferStart = pos; } } size = min_c(size, fBufferUsed); // copy data from the cache to the given buffer memcpy(buffer, fBuffer + pos - fBufferStart, size); return size; } ssize_t BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size) { if (fStream == NULL) return B_NO_INIT; if (buffer == NULL) return B_BAD_VALUE; // If data doesn't fit into the buffer, write it directly to the stream if (size > fBufferSize || fBuffer == NULL) return fStream->WriteAt(pos, buffer, size); // If we have cached data in the buffer, whose offset into the stream // is > 0, and the buffer isn't dirty, drop the data. if (!fBufferIsDirty && fBufferStart > pos) { fBufferStart = 0; fBufferUsed = 0; } // If we want to write beyond the cached data... if (pos > fBufferStart + (off_t)fBufferUsed || pos < fBufferStart) { ssize_t read; off_t where = pos; // Can we just cache from the beginning? if (pos + size <= fBufferSize) where = 0; // ...cache more. read = fStream->ReadAt(where, fBuffer, fBufferSize); if (read > 0) { fBufferUsed = read; fBufferStart = where; } } memcpy(fBuffer + pos - fBufferStart, buffer, size); fBufferIsDirty = true; fBufferUsed = max_c((size + pos), fBufferUsed); return size; } off_t BBufferIO::Seek(off_t position, uint32 seekMode) { if (fStream == NULL) return B_NO_INIT; off_t newPosition = fPosition; switch (seekMode) { case SEEK_CUR: newPosition += position; break; case SEEK_SET: newPosition = position; break; case SEEK_END: { off_t size; status_t status = fStream->GetSize(&size); if (status != B_OK) return status; newPosition = size - position; break; } } if (newPosition < 0) return B_BAD_VALUE; fPosition = newPosition; return newPosition; } off_t BBufferIO::Position() const { return fPosition; } status_t BBufferIO::SetSize(off_t size) { if (fStream == NULL) return B_NO_INIT; return fStream->SetSize(size); } status_t BBufferIO::Flush() { if (!fBufferIsDirty) return B_OK; // Write the cached data to the stream ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed); if (bytesWritten > 0) fBufferIsDirty = false; return (bytesWritten < 0) ? bytesWritten : B_OK; } BPositionIO* BBufferIO::Stream() const { return fStream; } size_t BBufferIO::BufferSize() const { return fBufferSize; } bool BBufferIO::OwnsStream() const { return fOwnsStream; } void BBufferIO::SetOwnsStream(bool ownsStream) { fOwnsStream = ownsStream; } void BBufferIO::PrintToStream() const { printf("stream %p\n", fStream); printf("buffer %p\n", fBuffer); printf("start %" B_PRId64 "\n", fBufferStart); printf("used %ld\n", fBufferUsed); printf("phys %ld\n", fBufferSize); printf("dirty %s\n", (fBufferIsDirty) ? "true" : "false"); printf("owns %s\n", (fOwnsStream) ? "true" : "false"); } // #pragma mark - FBC padding // These functions are here to maintain future binary // compatibility. status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; } status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; } status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; } status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; } status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }