1/*
2 *	Copyright 2001-2008 Haiku, Inc. All rights reserved.
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
17BBufferIO::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
38BBufferIO::~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
52ssize_t
53BBufferIO::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 + (off_t)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
102ssize_t
103BBufferIO::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 + (off_t)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
148off_t
149BBufferIO::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
183off_t
184BBufferIO::Position() const
185{
186	return fPosition;
187}
188
189
190status_t
191BBufferIO::SetSize(off_t size)
192{
193	if (fStream == NULL)
194		return B_NO_INIT;
195
196	return fStream->SetSize(size);
197}
198
199
200status_t
201BBufferIO::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
215BPositionIO*
216BBufferIO::Stream() const
217{
218	return fStream;
219}
220
221
222size_t
223BBufferIO::BufferSize() const
224{
225	return fBufferSize;
226}
227
228
229bool
230BBufferIO::OwnsStream() const
231{
232	return fOwnsStream;
233}
234
235
236void
237BBufferIO::SetOwnsStream(bool ownsStream)
238{
239	fOwnsStream = ownsStream;
240}
241
242
243void
244BBufferIO::PrintToStream() const
245{
246	printf("stream %p\n", fStream);
247	printf("buffer %p\n", fBuffer);
248	printf("start  %" B_PRId64 "\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 - FBC padding
257
258
259// These functions are here to maintain future binary
260// compatibility.
261status_t BBufferIO::_Reserved_BufferIO_0(void*) { return B_ERROR; }
262status_t BBufferIO::_Reserved_BufferIO_1(void*) { return B_ERROR; }
263status_t BBufferIO::_Reserved_BufferIO_2(void*) { return B_ERROR; }
264status_t BBufferIO::_Reserved_BufferIO_3(void*) { return B_ERROR; }
265status_t BBufferIO::_Reserved_BufferIO_4(void*) { return B_ERROR; }
266