1/*
2 * Copyright 2003-2008, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors :
6 *		Michael Wilber
7 *		J��r��me Duval
8 */
9
10#include <stdio.h>
11#include <string.h>
12#include "StreamBuffer.h"
13
14#ifndef min
15#define min(x,y) (((x) < (y)) ? (x) : (y))
16#endif
17
18#ifndef max
19#define max(x,y) (((x) > (y)) ? (x) : (y))
20#endif
21
22// ---------------------------------------------------------------
23// Constructor
24//
25// Initializes the StreamBuffer to read from pstream, buffering
26// nbuffersize bytes of data at a time. Note that if nbuffersize
27// is smaller than MIN_BUFFER_SIZE, MIN_BUFFER_SIZE is used
28// as the buffer size.
29//
30// Preconditions:
31//
32// Parameters: pstream,	the stream to be buffered
33//
34//             nbuffersize,	number of bytes to be read from
35//			                pstream at a time
36//
37// Postconditions:
38//
39// Returns:
40// ---------------------------------------------------------------
41StreamBuffer::StreamBuffer(BPositionIO *pstream, size_t nbuffersize, bool toRead)
42{
43	fStream = pstream;
44	fBuffer = NULL;
45	fBufferSize = 0;
46	fLen = 0;
47	fPos = 0;
48	fToRead = toRead;
49
50	if (!pstream)
51		return;
52
53	fBufferSize = max(nbuffersize, MIN_BUFFER_SIZE);
54	fBuffer = new uint8[fBufferSize];
55}
56
57// ---------------------------------------------------------------
58// Destructor
59//
60// Destroys data allocated for this object
61//
62// Preconditions:
63//
64// Parameters:
65//
66// Postconditions:
67//
68// Returns:
69// ---------------------------------------------------------------
70StreamBuffer::~StreamBuffer()
71{
72	if (!fToRead && fLen > 0)
73		fStream->Write(fBuffer, fLen);
74	delete[] fBuffer;
75	fBuffer = NULL;
76}
77
78// ---------------------------------------------------------------
79// InitCheck
80//
81// Determines whether the constructor failed or not
82//
83// Preconditions:
84//
85// Parameters:
86//
87// Postconditions:
88//
89// Returns: B_OK if object has been initialized successfully,
90// B_ERROR if not
91// ---------------------------------------------------------------
92status_t
93StreamBuffer::InitCheck()
94{
95	if (fStream && fBuffer)
96		return B_OK;
97	else
98		return B_ERROR;
99}
100
101// ---------------------------------------------------------------
102// Read
103//
104// Copies up to nbytes of data from the stream into pinto
105//
106// Preconditions: ReadStream() must be called once before this
107// function is called (the constructor does this)
108//
109// Parameters:	pinto,	the buffer to be copied to
110//
111//				nbytes,	the maximum number of bytes to copy
112//
113// Postconditions:
114//
115// Returns: the number of bytes successfully read or an
116// error code returned by BPositionIO::Read()
117// ---------------------------------------------------------------
118ssize_t
119StreamBuffer::Read(void *_pinto, size_t nbytes)
120{
121	if (_pinto == NULL)
122		return B_BAD_VALUE;
123	if (nbytes == 0)
124		return 0;
125
126	ssize_t result = B_ERROR;
127	uint8 *pinto = (uint8 *)_pinto;
128
129	size_t totalRead = min(nbytes, fLen - fPos);
130	memcpy(pinto, fBuffer + fPos, totalRead);
131	fPos += totalRead;
132	pinto += totalRead;
133	nbytes -= totalRead;
134
135	while (nbytes > 0) {
136		result = _ReadStream();
137		if (result <= 0)
138			return result;
139		if (result > 0) {
140			size_t left = min(nbytes, fLen - fPos);
141			memcpy(pinto, fBuffer + fPos, left);
142			fPos += left;
143			pinto += left;
144			nbytes -= left;
145			totalRead += left;
146		}
147	}
148
149	return totalRead;
150}
151
152
153// ---------------------------------------------------------------
154// Write
155//
156// Copies up to nbytes of data from pinto into the stream
157//
158// Parameters:	pinto,	the buffer to be copied from
159//				nbytes,	the maximum number of bytes to copy
160//
161// Returns: the number of bytes successfully read or an
162// error code returned by BPositionIO::Read()
163// ---------------------------------------------------------------
164void
165StreamBuffer::Write(void *pinto, size_t nbytes)
166{
167	if (nbytes < fBufferSize - fLen) {
168		memcpy(fBuffer + fLen, pinto, nbytes);
169		fLen += nbytes;
170	} else {
171		if (fLen > 0) {
172			fStream->Write(fBuffer, fLen);
173			fLen = 0;
174		}
175		fStream->Write(pinto, nbytes);
176	}
177}
178
179
180// ---------------------------------------------------------------
181// Seek
182//
183// Seeks the stream to the given position. If the seek operation fails,
184// the read buffer will be reset.
185//
186// Preconditions: fBuffer must be allocated and fBufferSize
187// must be valid
188//
189// Parameters:
190//
191// Postconditions:
192//
193// Returns: the new position
194// ---------------------------------------------------------------
195off_t
196StreamBuffer::Seek(off_t position, uint32 seekMode)
197{
198	// just seek in the current buffer if the new position is in it
199	if (seekMode == SEEK_CUR) {
200		if (fToRead
201			&& (fPos + position < fLen)
202			&& (fPos + position >= 0)) {
203			fPos += position;
204			return Position();
205		} else if (!fToRead
206			&& (fLen + position < fBufferSize)
207			&& (fLen + position >= 0)) {
208			fLen += position;
209			return Position();
210		}
211	}
212
213	// flush if something to write
214	if (!fToRead
215		&& fLen > 0) {
216			fStream->Write(fBuffer, fLen);
217	}
218
219	fLen = 0;
220	fPos = 0;
221
222	return fStream->Seek(position, seekMode);
223}
224
225
226// ---------------------------------------------------------------
227// Position
228//
229// Returns the current position in the stream.
230//
231// Preconditions: fBuffer must be allocated and fBufferSize
232// must be valid
233//
234// Parameters:
235//
236// Postconditions:
237//
238// Returns: the position
239// ---------------------------------------------------------------
240off_t
241StreamBuffer::Position()
242{
243	off_t position = fStream->Position();
244	if (fToRead)
245		position -= (fLen - fPos);
246	else
247		position += fLen;
248	return position;
249}
250
251
252// ---------------------------------------------------------------
253// _ReadStream
254//
255// Fills the stream buffer with data read in from the stream
256//
257// Preconditions: fBuffer must be allocated and fBufferSize
258// must be valid
259//
260// Parameters:
261//
262// Postconditions:
263//
264// Returns: the number of bytes successfully read or an
265// error code returned by BPositionIO::Read()
266// ---------------------------------------------------------------
267ssize_t
268StreamBuffer::_ReadStream()
269{
270	ssize_t len = fStream->Read(fBuffer, fBufferSize);
271	if (len < 0)
272		return len;
273	fLen = len;
274	fPos = 0;
275	return fLen;
276}
277