1/* 2 * Copyright 2001-2008 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT license 4 * 5 * Authors: 6 * Stefano Ceccherini, burton666@libero.it 7 */ 8 9 10#include <BufferIO.h> 11 12#include <stdio.h> 13#include <stdlib.h> 14#include <string.h> 15 16 17BBufferIO::BBufferIO(BPositionIO* stream, size_t bufferSize, bool ownsStream) 18 : 19 fBufferStart(0), 20 fStream(stream), 21 fBuffer(NULL), 22 fBufferUsed(0), 23 fBufferIsDirty(false), 24 fOwnsStream(ownsStream) 25 26{ 27 fBufferSize = max_c(bufferSize, 512); 28 fPosition = stream->Position(); 29 30 // What can we do if this malloc fails ? 31 // I think R5 uses new, but doesn't catch the thrown exception 32 // (if you specify a very big buffer, the application just 33 // terminates with abort). 34 fBuffer = (char*)malloc(fBufferSize); 35} 36 37 38BBufferIO::~BBufferIO() 39{ 40 if (fBufferIsDirty) { 41 // Write pending changes to the stream 42 Flush(); 43 } 44 45 free(fBuffer); 46 47 if (fOwnsStream) 48 delete fStream; 49} 50 51 52ssize_t 53BBufferIO::ReadAt(off_t pos, void* buffer, size_t size) 54{ 55 // We refuse to crash, even if 56 // you were lazy and didn't give a valid 57 // stream on construction. 58 if (fStream == NULL) 59 return B_NO_INIT; 60 if (buffer == NULL) 61 return B_BAD_VALUE; 62 63 // If the amount of data we want doesn't fit in the buffer, just 64 // read it directly from the disk (and don't touch the buffer). 65 if (size > fBufferSize || fBuffer == NULL) { 66 if (fBufferIsDirty) 67 Flush(); 68 return fStream->ReadAt(pos, buffer, size); 69 } 70 71 // If the data we are looking for is not in the buffer... 72 if (size > fBufferUsed 73 || pos < fBufferStart 74 || pos > fBufferStart + (off_t)fBufferUsed 75 || pos + size > fBufferStart + fBufferUsed) { 76 if (fBufferIsDirty) { 77 // If there are pending writes, do them. 78 Flush(); 79 } 80 81 // ...cache as much as we can from the stream 82 ssize_t sizeRead = fStream->ReadAt(pos, fBuffer, fBufferSize); 83 if (sizeRead < 0) 84 return sizeRead; 85 86 fBufferUsed = sizeRead; 87 if (fBufferUsed > 0) { 88 // The data is buffered starting from this offset 89 fBufferStart = pos; 90 } 91 } 92 93 size = min_c(size, fBufferUsed); 94 95 // copy data from the cache to the given buffer 96 memcpy(buffer, fBuffer + pos - fBufferStart, size); 97 98 return size; 99} 100 101 102ssize_t 103BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size) 104{ 105 if (fStream == NULL) 106 return B_NO_INIT; 107 if (buffer == NULL) 108 return B_BAD_VALUE; 109 110 // If data doesn't fit into the buffer, write it directly to the stream 111 if (size > fBufferSize || fBuffer == NULL) 112 return fStream->WriteAt(pos, buffer, size); 113 114 // If we have cached data in the buffer, whose offset into the stream 115 // is > 0, and the buffer isn't dirty, drop the data. 116 if (!fBufferIsDirty && fBufferStart > pos) { 117 fBufferStart = 0; 118 fBufferUsed = 0; 119 } 120 121 // If we want to write beyond the cached data... 122 if (pos > fBufferStart + (off_t)fBufferUsed 123 || pos < fBufferStart) { 124 ssize_t read; 125 off_t where = pos; 126 127 // Can we just cache from the beginning? 128 if (pos + size <= fBufferSize) 129 where = 0; 130 131 // ...cache more. 132 read = fStream->ReadAt(where, fBuffer, fBufferSize); 133 if (read > 0) { 134 fBufferUsed = read; 135 fBufferStart = where; 136 } 137 } 138 139 memcpy(fBuffer + pos - fBufferStart, buffer, size); 140 141 fBufferIsDirty = true; 142 fBufferUsed = max_c((size + pos), fBufferUsed); 143 144 return size; 145} 146 147 148off_t 149BBufferIO::Seek(off_t position, uint32 seekMode) 150{ 151 if (fStream == NULL) 152 return B_NO_INIT; 153 154 off_t newPosition = fPosition; 155 156 switch (seekMode) { 157 case SEEK_CUR: 158 newPosition += position; 159 break; 160 case SEEK_SET: 161 newPosition = position; 162 break; 163 case SEEK_END: 164 { 165 off_t size; 166 status_t status = fStream->GetSize(&size); 167 if (status != B_OK) 168 return status; 169 170 newPosition = size - position; 171 break; 172 } 173 } 174 175 if (newPosition < 0) 176 return B_BAD_VALUE; 177 178 fPosition = newPosition; 179 return newPosition; 180} 181 182 183off_t 184BBufferIO::Position() const 185{ 186 return fPosition; 187} 188 189 190status_t 191BBufferIO::SetSize(off_t size) 192{ 193 if (fStream == NULL) 194 return B_NO_INIT; 195 196 return fStream->SetSize(size); 197} 198 199 200status_t 201BBufferIO::Flush() 202{ 203 if (!fBufferIsDirty) 204 return B_OK; 205 206 // Write the cached data to the stream 207 ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed); 208 if (bytesWritten > 0) 209 fBufferIsDirty = false; 210 211 return (bytesWritten < 0) ? bytesWritten : B_OK; 212} 213 214 215BPositionIO* 216BBufferIO::Stream() const 217{ 218 return fStream; 219} 220 221 222size_t 223BBufferIO::BufferSize() const 224{ 225 return fBufferSize; 226} 227 228 229bool 230BBufferIO::OwnsStream() const 231{ 232 return fOwnsStream; 233} 234 235 236void 237BBufferIO::SetOwnsStream(bool ownsStream) 238{ 239 fOwnsStream = ownsStream; 240} 241 242 243void 244BBufferIO::PrintToStream() const 245{ 246 printf("stream %p\n", fStream); 247 printf("buffer %p\n", fBuffer); 248 printf("start %" B_PRId64 "\n", fBufferStart); 249 printf("used %ld\n", fBufferUsed); 250 printf("phys %ld\n", fBufferSize); 251 printf("dirty %s\n", (fBufferIsDirty) ? "true" : "false"); 252 printf("owns %s\n", (fOwnsStream) ? "true" : "false"); 253} 254 255 256// #pragma mark - FBC padding 257 258 259// These functions are here to maintain future binary 260// compatibility. 261status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; } 262status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; } 263status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; } 264status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; } 265status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; } 266