1 /* 2 * Copyright (c) 2001-2005, Haiku 3 * Distributed under the terms of the MIT license 4 * Authors: 5 Stefano Ceccherini (burton666@libero.it) 6 */ 7 8 // A buffered adapter for BPositionIO objects. 9 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #include <BufferIO.h> 15 16 /*! \class BBufferIO 17 \brief A buffered adapter for BPositionIO objects. 18 \author <a href='mailto:burton666@freemail.it>Stefano Ceccherini</a> 19 */ 20 21 22 /*! \brief Initializes a BBufferIO object. 23 24 Initializes the object, creates a buffer of the given size 25 and associates the object with the given BPositionIO stream. 26 27 \param stream A pointer to a BPositionIO object. 28 \param buf_size The size of the buffer that the object will allocate and use. 29 \param owns_stream Specifies if the object will delete the stream on destruction. 30 */ 31 BBufferIO::BBufferIO(BPositionIO *stream, size_t buf_size, bool owns_stream) 32 :m_buffer_start(0), 33 m_stream(stream), 34 m_buffer(NULL), 35 m_buffer_used(0), 36 m_buffer_dirty(false), 37 m_owns_stream(owns_stream) 38 39 { 40 if (buf_size < 512) 41 buf_size = 512; 42 43 m_buffer_phys = buf_size; 44 45 // What can we do if this malloc fails ? 46 // I think R5 uses new, but doesn't catch the thrown exception 47 // (if you specify a very big buffer, the application just 48 // terminates with abort). 49 m_buffer = (char *)malloc(m_buffer_phys * sizeof(char)); 50 } 51 52 53 /*! \brief Frees the resources allocated by the object 54 55 Flushes pending changes to the stream and frees the allocated memory. 56 If the owns_stream property is true, the destructor also 57 deletes the stream associated with the BBufferIO object. 58 */ 59 BBufferIO::~BBufferIO() 60 { 61 if (m_buffer_dirty) 62 Flush(); // Writes pending changes to the stream 63 64 free(m_buffer); 65 66 if (m_owns_stream) 67 delete m_stream; 68 } 69 70 71 /*! \brief Reads the specified amount of bytes at the given position. 72 \param pos The offset into the stream where to read. 73 \param buffer A pointer to a buffer where to copy the read data. 74 \param size The amount of bytes to read. 75 \return The amount of bytes actually read, or an error code. 76 */ 77 ssize_t 78 BBufferIO::ReadAt(off_t pos, void *buffer, size_t size) 79 { 80 // We refuse to crash, even if 81 // you were lazy and didn't give a valid 82 // stream on construction. 83 if (m_stream == NULL) 84 return B_NO_INIT; 85 86 if (buffer == NULL) 87 return B_BAD_VALUE; 88 89 // If the amount of data we want doesn't fit in the buffer, just 90 // read it directly from the disk (and don't touch the buffer). 91 if (size > m_buffer_phys) { 92 if (m_buffer_dirty) 93 Flush(); 94 return m_stream->ReadAt(pos, buffer, size); 95 } 96 97 // If the data we are looking for is not in the buffer... 98 if (size > m_buffer_used 99 || pos < m_buffer_start 100 || pos > m_buffer_start + m_buffer_used 101 || pos + size > m_buffer_start + m_buffer_used) { 102 103 if (m_buffer_dirty) 104 Flush(); // If there are pending writes, do them. 105 106 // ...cache as much as we can from the stream 107 m_buffer_used = m_stream->ReadAt(pos, m_buffer, m_buffer_phys); 108 109 if (m_buffer_used > 0) 110 m_buffer_start = pos; // The data is buffered starting from this offset 111 } 112 113 size = min_c(size, m_buffer_used); 114 115 // copy data from the cache to the given buffer 116 memcpy(buffer, m_buffer + pos - m_buffer_start, size); 117 118 return size; 119 } 120 121 122 /*! \brief Writes the specified amount of bytes at the given position. 123 \param pos The offset into the stream where to write. 124 \param buffer A pointer to a buffer which contains the data to write. 125 \param size The amount of bytes to write. 126 \return The amount of bytes actually written, or an error code. 127 */ 128 ssize_t 129 BBufferIO::WriteAt(off_t pos, const void *buffer, size_t size) 130 { 131 if (m_stream == NULL) 132 return B_NO_INIT; 133 134 if (buffer == NULL) 135 return B_BAD_VALUE; 136 137 // If data doesn't fit into the buffer, write it directly to the stream 138 if (size > m_buffer_phys) 139 return m_stream->WriteAt(pos, buffer, size); 140 141 // If we have cached data in the buffer, whose offset into the stream 142 // is > 0, and the buffer isn't dirty, drop the data. 143 if (!m_buffer_dirty && m_buffer_start > pos) { 144 m_buffer_start = 0; 145 m_buffer_used = 0; 146 } 147 148 // If we want to write beyond the cached data... 149 if (pos > m_buffer_start + m_buffer_used 150 || pos < m_buffer_start) { 151 ssize_t read; 152 off_t where = pos; 153 154 if (pos + size <= m_buffer_phys) // Can we just cache from the beginning ? 155 where = 0; 156 157 // ...cache more. 158 read = m_stream->ReadAt(where, m_buffer, m_buffer_phys); 159 if (read > 0) { 160 m_buffer_used = read; 161 m_buffer_start = where; 162 } 163 } 164 165 memcpy(m_buffer + pos - m_buffer_start, buffer, size); 166 167 m_buffer_dirty = true; 168 m_buffer_used = max_c((size + pos), m_buffer_used); 169 170 return size; 171 } 172 173 174 /*! \brief Sets the position in the stream. 175 176 Sets the position in the stream where the Read() and Write() functions 177 (inherited from BPositionIO) begin reading and writing. 178 How the position argument is understood depends on the seek_mode flag. 179 180 \param position The position where you want to seek. 181 \param seek_mode Can have three values: 182 183 - \c SEEK_SET. The position passed is an offset from the beginning of the stream; 184 in other words, the current position is set to position. 185 For this mode, position should be a positive value. 186 187 - \c SEEK_CUR. The position argument is an offset from the current position; 188 the value of the argument is added to the current position. 189 190 - \c SEEK_END. The position argument is an offset from the end of the stream. 191 In this mode the position argument should be negative (or zero). 192 193 \return The current position as an offset in bytes 194 from the beginning of the stream. 195 */ 196 off_t 197 BBufferIO::Seek(off_t position, uint32 seek_mode) 198 { 199 if (m_stream == NULL) 200 return B_NO_INIT; 201 202 return m_stream->Seek(position, seek_mode); 203 } 204 205 206 /*! \brief Return the current position in the stream. 207 \return The current position as an offset in bytes 208 from the beginning of the stream. 209 */ 210 off_t 211 BBufferIO::Position() const 212 { 213 if (m_stream == NULL) 214 return B_NO_INIT; 215 216 return m_stream->Position(); 217 } 218 219 220 /*! \brief Calls the SetSize() function of the assigned BPositionIO object. 221 \param size The new size of the BPositionIO object. 222 \return An error code. 223 */ 224 status_t 225 BBufferIO::SetSize(off_t size) 226 { 227 if (m_stream == NULL) 228 return B_NO_INIT; 229 230 return m_stream->SetSize(size); 231 } 232 233 234 /*! \brief Writes pending modifications to the stream. 235 \return An error code. 236 */ 237 status_t 238 BBufferIO::Flush() 239 { 240 if (!m_buffer_dirty) 241 return B_OK; 242 243 // Write the cached data to the stream 244 status_t err = m_stream->WriteAt(m_buffer_start, m_buffer, m_buffer_used); 245 246 if (err > 0) 247 m_buffer_dirty = false; 248 249 return (err < 0) ? err : B_OK; 250 } 251 252 253 /*! \brief Returns a pointer to the stream specified on construction 254 \return A pointer to the BPositionIO stream specified on construction. 255 */ 256 BPositionIO * 257 BBufferIO::Stream() const 258 { 259 return m_stream; 260 } 261 262 263 /*! \brief Returns the size of the internal buffer 264 \return The size of the buffer allocated by the object 265 */ 266 size_t 267 BBufferIO::BufferSize() const 268 { 269 return m_buffer_phys; 270 } 271 272 273 /*! \brief Tells if the BBufferIO object "owns" the specified stream. 274 \return A boolean value, which is true if the object "owns" 275 the stream (and so will delete it upon destruction), false if not. 276 */ 277 bool 278 BBufferIO::OwnsStream() const 279 { 280 return m_owns_stream; 281 } 282 283 284 /*! \brief Set the "owns_stream" property of the object. 285 \param owns_stream If it's true, the object will delete the stream 286 upon destruction, if it's false it will not. 287 */ 288 void 289 BBufferIO::SetOwnsStream(bool owns_stream) 290 { 291 m_owns_stream = owns_stream; 292 } 293 294 295 /*! \brief Prints the object to stdout. 296 */ 297 void 298 BBufferIO::PrintToStream() const 299 { 300 printf("stream %p\n", m_stream); 301 printf("buffer %p\n", m_buffer); 302 printf("start %lld\n", m_buffer_start); 303 printf("used %ld\n", m_buffer_used); 304 printf("phys %ld\n", m_buffer_phys); 305 printf("dirty %s\n", (m_buffer_dirty) ? "true" : "false"); 306 printf("owns %s\n", (m_owns_stream) ? "true" : "false"); 307 } 308 309 310 // These functions are here to maintain future binary 311 // compatibility. 312 status_t BBufferIO::_Reserved_BufferIO_0(void *) { return B_ERROR; } 313 status_t BBufferIO::_Reserved_BufferIO_1(void *) { return B_ERROR; } 314 status_t BBufferIO::_Reserved_BufferIO_2(void *) { return B_ERROR; } 315 status_t BBufferIO::_Reserved_BufferIO_3(void *) { return B_ERROR; } 316 status_t BBufferIO::_Reserved_BufferIO_4(void *) { return B_ERROR; } 317