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 + fBufferUsed 75 || pos + size > fBufferStart + fBufferUsed) { 76 if (fBufferIsDirty) 77 Flush(); // If there are pending writes, do them. 78 79 // ...cache as much as we can from the stream 80 fBufferUsed = fStream->ReadAt(pos, fBuffer, fBufferSize); 81 82 if (fBufferUsed > 0) 83 fBufferStart = pos; // The data is buffered starting from this offset 84 } 85 86 size = min_c(size, fBufferUsed); 87 88 // copy data from the cache to the given buffer 89 memcpy(buffer, fBuffer + pos - fBufferStart, size); 90 91 return size; 92 } 93 94 95 ssize_t 96 BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size) 97 { 98 if (fStream == NULL) 99 return B_NO_INIT; 100 if (buffer == NULL) 101 return B_BAD_VALUE; 102 103 // If data doesn't fit into the buffer, write it directly to the stream 104 if (size > fBufferSize || fBuffer == NULL) 105 return fStream->WriteAt(pos, buffer, size); 106 107 // If we have cached data in the buffer, whose offset into the stream 108 // is > 0, and the buffer isn't dirty, drop the data. 109 if (!fBufferIsDirty && fBufferStart > pos) { 110 fBufferStart = 0; 111 fBufferUsed = 0; 112 } 113 114 // If we want to write beyond the cached data... 115 if (pos > fBufferStart + fBufferUsed 116 || pos < fBufferStart) { 117 ssize_t read; 118 off_t where = pos; 119 120 if (pos + size <= fBufferSize) // Can we just cache from the beginning ? 121 where = 0; 122 123 // ...cache more. 124 read = fStream->ReadAt(where, fBuffer, fBufferSize); 125 if (read > 0) { 126 fBufferUsed = read; 127 fBufferStart = where; 128 } 129 } 130 131 memcpy(fBuffer + pos - fBufferStart, buffer, size); 132 133 fBufferIsDirty = true; 134 fBufferUsed = max_c((size + pos), fBufferUsed); 135 136 return size; 137 } 138 139 140 off_t 141 BBufferIO::Seek(off_t position, uint32 seekMode) 142 { 143 if (fStream == NULL) 144 return B_NO_INIT; 145 146 off_t newPosition = fPosition; 147 148 switch (seekMode) { 149 case SEEK_CUR: 150 newPosition += position; 151 break; 152 case SEEK_SET: 153 newPosition = position; 154 break; 155 case SEEK_END: 156 { 157 off_t size; 158 status_t status = fStream->GetSize(&size); 159 if (status != B_OK) 160 return status; 161 162 newPosition = size - position; 163 break; 164 } 165 } 166 167 if (newPosition < 0) 168 return B_BAD_VALUE; 169 170 fPosition = newPosition; 171 return newPosition; 172 } 173 174 175 off_t 176 BBufferIO::Position() const 177 { 178 return fPosition; 179 } 180 181 182 status_t 183 BBufferIO::SetSize(off_t size) 184 { 185 if (fStream == NULL) 186 return B_NO_INIT; 187 188 return fStream->SetSize(size); 189 } 190 191 192 status_t 193 BBufferIO::Flush() 194 { 195 if (!fBufferIsDirty) 196 return B_OK; 197 198 // Write the cached data to the stream 199 ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed); 200 if (bytesWritten > 0) 201 fBufferIsDirty = false; 202 203 return (bytesWritten < 0) ? bytesWritten : B_OK; 204 } 205 206 207 BPositionIO* 208 BBufferIO::Stream() const 209 { 210 return fStream; 211 } 212 213 214 size_t 215 BBufferIO::BufferSize() const 216 { 217 return fBufferSize; 218 } 219 220 221 bool 222 BBufferIO::OwnsStream() const 223 { 224 return fOwnsStream; 225 } 226 227 228 void 229 BBufferIO::SetOwnsStream(bool ownsStream) 230 { 231 fOwnsStream = ownsStream; 232 } 233 234 235 void 236 BBufferIO::PrintToStream() const 237 { 238 printf("stream %p\n", fStream); 239 printf("buffer %p\n", fBuffer); 240 printf("start %lld\n", fBufferStart); 241 printf("used %ld\n", fBufferUsed); 242 printf("phys %ld\n", fBufferSize); 243 printf("dirty %s\n", (fBufferIsDirty) ? "true" : "false"); 244 printf("owns %s\n", (fOwnsStream) ? "true" : "false"); 245 } 246 247 248 // #pragma mark - 249 250 251 // These functions are here to maintain future binary 252 // compatibility. 253 status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; } 254 status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; } 255 status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; } 256 status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; } 257 status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; } 258