xref: /haiku/src/kits/support/BufferIO.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
1 /*
2  *	Copyright (c) 2001-2008, 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 	fBufferSize = max_c(bufferSize, 512);
28 	fPosition = stream->Position();
29 
30 	// What can we do if this malloc fails ?
31 	// I think R5 uses new, but doesn't catch the thrown exception
32 	// (if you specify a very big buffer, the application just
33 	// terminates with abort).
34 	fBuffer = (char*)malloc(fBufferSize);
35 }
36 
37 
38 BBufferIO::~BBufferIO()
39 {
40 	if (fBufferIsDirty) {
41 		// Write pending changes to the stream
42 		Flush();
43 	}
44 
45 	free(fBuffer);
46 
47 	if (fOwnsStream)
48 		delete fStream;
49 }
50 
51 
52 ssize_t
53 BBufferIO::ReadAt(off_t pos, void* buffer, size_t size)
54 {
55 	// We refuse to crash, even if
56 	// you were lazy and didn't give a valid
57 	// stream on construction.
58 	if (fStream == NULL)
59 		return B_NO_INIT;
60 	if (buffer == NULL)
61 		return B_BAD_VALUE;
62 
63 	// If the amount of data we want doesn't fit in the buffer, just
64 	// read it directly from the disk (and don't touch the buffer).
65 	if (size > fBufferSize || fBuffer == NULL) {
66 		if (fBufferIsDirty)
67 			Flush();
68 		return fStream->ReadAt(pos, buffer, size);
69 	}
70 
71 	// If the data we are looking for is not in the buffer...
72 	if (size > fBufferUsed
73 		|| pos < fBufferStart
74 		|| pos > fBufferStart + fBufferUsed
75 		|| pos + size > fBufferStart + fBufferUsed) {
76 		if (fBufferIsDirty) {
77 			// If there are pending writes, do them.
78 			Flush();
79 		}
80 
81 		// ...cache as much as we can from the stream
82 		ssize_t sizeRead = fStream->ReadAt(pos, fBuffer, fBufferSize);
83 		if (sizeRead < 0)
84 			return sizeRead;
85 
86 		fBufferUsed = sizeRead;
87 		if (fBufferUsed > 0) {
88 			// The data is buffered starting from this offset
89 			fBufferStart = pos;
90 		}
91 	}
92 
93 	size = min_c(size, fBufferUsed);
94 
95 	// copy data from the cache to the given buffer
96 	memcpy(buffer, fBuffer + pos - fBufferStart, size);
97 
98 	return size;
99 }
100 
101 
102 ssize_t
103 BBufferIO::WriteAt(off_t pos, const void* buffer, size_t size)
104 {
105 	if (fStream == NULL)
106 		return B_NO_INIT;
107 	if (buffer == NULL)
108 		return B_BAD_VALUE;
109 
110 	// If data doesn't fit into the buffer, write it directly to the stream
111 	if (size > fBufferSize || fBuffer == NULL)
112 		return fStream->WriteAt(pos, buffer, size);
113 
114 	// If we have cached data in the buffer, whose offset into the stream
115 	// is > 0, and the buffer isn't dirty, drop the data.
116 	if (!fBufferIsDirty && fBufferStart > pos) {
117 		fBufferStart = 0;
118 		fBufferUsed = 0;
119 	}
120 
121 	// If we want to write beyond the cached data...
122 	if (pos > fBufferStart + fBufferUsed
123 		|| pos < fBufferStart) {
124 		ssize_t read;
125 		off_t where = pos;
126 
127 		// Can we just cache from the beginning?
128 		if (pos + size <= fBufferSize)
129 			where = 0;
130 
131 		// ...cache more.
132 		read = fStream->ReadAt(where, fBuffer, fBufferSize);
133 		if (read > 0) {
134 			fBufferUsed = read;
135 			fBufferStart = where;
136 		}
137 	}
138 
139 	memcpy(fBuffer + pos - fBufferStart, buffer, size);
140 
141 	fBufferIsDirty = true;
142 	fBufferUsed = max_c((size + pos), fBufferUsed);
143 
144 	return size;
145 }
146 
147 
148 off_t
149 BBufferIO::Seek(off_t position, uint32 seekMode)
150 {
151 	if (fStream == NULL)
152 		return B_NO_INIT;
153 
154 	off_t newPosition = fPosition;
155 
156 	switch (seekMode) {
157 		case SEEK_CUR:
158 			newPosition += position;
159 			break;
160 		case SEEK_SET:
161 			newPosition = position;
162 			break;
163 		case SEEK_END:
164 		{
165 			off_t size;
166 			status_t status = fStream->GetSize(&size);
167 			if (status != B_OK)
168 				return status;
169 
170 			newPosition = size - position;
171 			break;
172 		}
173 	}
174 
175 	if (newPosition < 0)
176 		return B_BAD_VALUE;
177 
178 	fPosition = newPosition;
179 	return newPosition;
180 }
181 
182 
183 off_t
184 BBufferIO::Position() const
185 {
186 	return fPosition;
187 }
188 
189 
190 status_t
191 BBufferIO::SetSize(off_t size)
192 {
193 	if (fStream == NULL)
194 		return B_NO_INIT;
195 
196 	return fStream->SetSize(size);
197 }
198 
199 
200 status_t
201 BBufferIO::Flush()
202 {
203 	if (!fBufferIsDirty)
204 		return B_OK;
205 
206 	// Write the cached data to the stream
207 	ssize_t bytesWritten = fStream->WriteAt(fBufferStart, fBuffer, fBufferUsed);
208 	if (bytesWritten > 0)
209 		fBufferIsDirty = false;
210 
211 	return (bytesWritten < 0) ? bytesWritten : B_OK;
212 }
213 
214 
215 BPositionIO*
216 BBufferIO::Stream() const
217 {
218 	return fStream;
219 }
220 
221 
222 size_t
223 BBufferIO::BufferSize() const
224 {
225 	return fBufferSize;
226 }
227 
228 
229 bool
230 BBufferIO::OwnsStream() const
231 {
232 	return fOwnsStream;
233 }
234 
235 
236 void
237 BBufferIO::SetOwnsStream(bool ownsStream)
238 {
239 	fOwnsStream = ownsStream;
240 }
241 
242 
243 void
244 BBufferIO::PrintToStream() const
245 {
246 	printf("stream %p\n", fStream);
247 	printf("buffer %p\n", fBuffer);
248 	printf("start  %lld\n", fBufferStart);
249 	printf("used   %ld\n", fBufferUsed);
250 	printf("phys   %ld\n", fBufferSize);
251 	printf("dirty  %s\n", (fBufferIsDirty) ? "true" : "false");
252 	printf("owns   %s\n", (fOwnsStream) ? "true" : "false");
253 }
254 
255 
256 //	#pragma mark -
257 
258 
259 // These functions are here to maintain future binary
260 // compatibility.
261 status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; }
262 status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; }
263 status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; }
264 status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; }
265 status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }
266