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