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