1 /* 2 * Copyright (c) 2001-2008, Haiku 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 17 BBufferIO::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 38 BBufferIO::~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 52 ssize_t 53 BBufferIO::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 102 ssize_t 103 BBufferIO::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 148 off_t 149 BBufferIO::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 183 off_t 184 BBufferIO::Position() const 185 { 186 return fPosition; 187 } 188 189 190 status_t 191 BBufferIO::SetSize(off_t size) 192 { 193 if (fStream == NULL) 194 return B_NO_INIT; 195 196 return fStream->SetSize(size); 197 } 198 199 200 status_t 201 BBufferIO::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 215 BPositionIO* 216 BBufferIO::Stream() const 217 { 218 return fStream; 219 } 220 221 222 size_t 223 BBufferIO::BufferSize() const 224 { 225 return fBufferSize; 226 } 227 228 229 bool 230 BBufferIO::OwnsStream() const 231 { 232 return fOwnsStream; 233 } 234 235 236 void 237 BBufferIO::SetOwnsStream(bool ownsStream) 238 { 239 fOwnsStream = ownsStream; 240 } 241 242 243 void 244 BBufferIO::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 - 257 258 259 // These functions are here to maintain future binary 260 // compatibility. 261 status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; } 262 status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; } 263 status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; } 264 status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; } 265 status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; } 266