/* * Copyright (c) 2001-2007, Haiku * 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) { if (bufferSize < 512) bufferSize = 512; fBufferSize = bufferSize; // 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) { 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 + fBufferUsed || pos + size > fBufferStart + fBufferUsed) { if (fBufferIsDirty) Flush(); // If there are pending writes, do them. // ...cache as much as we can from the stream fBufferUsed = fStream->ReadAt(pos, fBuffer, fBufferSize); if (fBufferUsed > 0) fBufferStart = pos; // The data is buffered starting from this offset } 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) 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 + fBufferUsed || pos < fBufferStart) { ssize_t read; off_t where = pos; if (pos + size <= fBufferSize) // Can we just cache from the beginning ? 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; return fStream->Seek(position, seekMode); } off_t BBufferIO::Position() const { if (fStream == NULL) return B_NO_INIT; return fStream->Position(); } 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 owns_stream) { fOwnsStream = owns_stream; } void BBufferIO::PrintToStream() const { printf("stream %p\n", fStream); printf("buffer %p\n", fBuffer); printf("start %lld\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 - // 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; }