xref: /haiku/src/kits/support/BufferIO.cpp (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
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