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