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