1//----------------------------------------------------------------------
2//  This software is part of the OpenBeOS distribution and is covered
3//  by the MIT License.
4//
5//  Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
6//----------------------------------------------------------------------
7
8/*! \file SimulatedStream.cpp
9*/
10
11#include "SimulatedStream.h"
12
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16#include "UdfDebug.h"
17
18SimulatedStream::SimulatedStream(DataStream &stream)
19	: fPosition(0)
20	, fStream(stream)
21{
22}
23
24status_t
25SimulatedStream::InitCheck() const
26{
27	return fStream.InitCheck();
28}
29
30ssize_t
31SimulatedStream::Read(void *_buffer, size_t size)
32{
33	uint8 *buffer = reinterpret_cast<uint8*>(_buffer);
34	status_t error = buffer ? B_OK : B_BAD_VALUE;
35	ssize_t bytesTotal = 0;
36	while (error == B_OK && size > 0) {
37		data_extent extent;
38		error = _GetExtent(fPosition, size, extent);
39		if (!error) {
40			if (extent.size > 0) {
41				ssize_t bytes = fStream.ReadAt(extent.offset, buffer, extent.size);
42				if (bytes >= 0) {
43					size -= bytes;
44					fPosition += bytes;
45					buffer += bytes;
46					bytesTotal += bytes;
47				} else {
48					error = status_t(bytes);
49				}
50			} else {
51				// end of simulated stream
52				break;
53			}
54		}
55	}
56	return !error ? bytesTotal : ssize_t(error);
57}
58
59ssize_t
60SimulatedStream::ReadAt(off_t pos, void *_buffer, size_t size)
61{
62	uint8 *buffer = reinterpret_cast<uint8*>(_buffer);
63	status_t error = buffer ? B_OK : B_BAD_VALUE;
64	ssize_t bytesTotal = 0;
65	while (error == B_OK && size > 0) {
66		data_extent extent;
67		error = _GetExtent(pos, size, extent);
68		if (!error) {
69			if (extent.size > 0) {
70				ssize_t bytes = fStream.ReadAt(extent.offset, buffer, extent.size);
71				if (bytes >= 0) {
72					size -= bytes;
73					pos += bytes;
74					buffer += bytes;
75					bytesTotal += bytes;
76				} else {
77					error = status_t(bytes);
78				}
79			} else {
80				// end of simulated stream
81				break;
82			}
83		}
84	}
85	return !error ? bytesTotal : ssize_t(error);
86}
87
88/*! \brief Writes \a size bytes worth of data from \a buffer at the current
89	position in the stream, incrementing the stream's position marker as it goes.
90*/
91ssize_t
92SimulatedStream::Write(const void *_buffer, size_t size)
93{
94	const uint8 *buffer = reinterpret_cast<const uint8*>(_buffer);
95	status_t error = buffer ? B_OK : B_BAD_VALUE;
96	ssize_t bytesTotal = 0;
97	while (error == B_OK && size > 0) {
98		data_extent extent;
99		error = _GetExtent(fPosition, size, extent);
100		if (!error) {
101			if (extent.size > 0) {
102				ssize_t bytes = fStream.WriteAt(extent.offset, buffer, extent.size);
103				if (bytes >= 0) {
104					size -= bytes;
105					fPosition += bytes;
106					buffer += bytes;
107					bytesTotal += bytes;
108				} else {
109					error = status_t(bytes);
110				}
111			} else {
112				// end of simulated stream
113				break;
114			}
115		}
116	}
117	return !error ? bytesTotal : ssize_t(error);
118}
119
120/*! \brief Writes \a size bytes worth of data from \a buffer at position
121	\a pos in the stream without incrementing the stream's position marker.
122*/
123ssize_t
124SimulatedStream::WriteAt(off_t pos, const void *_buffer, size_t size)
125{
126	const uint8 *buffer = reinterpret_cast<const uint8*>(_buffer);
127	status_t error = buffer ? B_OK : B_BAD_VALUE;
128	ssize_t bytesTotal = 0;
129	while (error == B_OK && size > 0) {
130		data_extent extent;
131		error = _GetExtent(pos, size, extent);
132		if (!error) {
133			if (extent.size > 0) {
134				ssize_t bytes = fStream.WriteAt(extent.offset, buffer, extent.size);
135				if (bytes >= 0) {
136					size -= bytes;
137					pos += bytes;
138					buffer += bytes;
139					bytesTotal += bytes;
140				} else {
141					error = status_t(bytes);
142				}
143			} else {
144				// end of simulated stream
145				break;
146			}
147		}
148	}
149	return !error ? bytesTotal : ssize_t(error);
150}
151
152/*! \brief Writes \a size bytes worth of data from \a data at the current
153	position in the stream, incrementing the stream's position marker as it goes.
154*/
155ssize_t
156SimulatedStream::Write(BDataIO &data, size_t size)
157{
158	DEBUG_INIT_ETC("SimulatedStream", ("size: %ld", size));
159	status_t error = B_OK;
160	ssize_t bytesTotal = 0;
161	while (error == B_OK && size > 0) {
162		data_extent extent;
163		error = _GetExtent(fPosition, size, extent);
164		if (!error) {
165			if (extent.size > 0) {
166				PRINT(("writing to underlying stream (offset: %llu, size: %ld)\n", extent.offset, extent.size));
167				ssize_t bytes = fStream.WriteAt(extent.offset, data, extent.size);
168				if (bytes >= 0) {
169					size -= bytes;
170					fPosition += bytes;
171					bytesTotal += bytes;
172				} else {
173					error = status_t(bytes);
174				}
175			} else {
176				// end of simulated stream
177				break;
178			}
179		}
180	}
181	RETURN(!error ? bytesTotal : ssize_t(error));
182}
183
184/*! \brief Writes \a size bytes worth of data from \a data at position
185	\a pos in the stream without incrementing the stream's position marker.
186*/
187ssize_t
188SimulatedStream::WriteAt(off_t pos, BDataIO &data, size_t size)
189{
190	status_t error = B_OK;
191	ssize_t bytesTotal = 0;
192	while (error == B_OK && size > 0) {
193		data_extent extent;
194		error = _GetExtent(pos, size, extent);
195		if (!error) {
196			if (extent.size > 0) {
197				ssize_t bytes = fStream.WriteAt(extent.offset, data, extent.size);
198				if (bytes >= 0) {
199					size -= bytes;
200					pos += bytes;
201					bytesTotal += bytes;
202				} else {
203					error = status_t(bytes);
204				}
205			} else {
206				// end of simulated stream
207				break;
208			}
209		}
210	}
211	return !error ? bytesTotal : ssize_t(error);
212}
213
214/*! \brief Writes \a size bytes worth of zeros at the current position
215	in the stream, incrementing the stream's position marker as it goes.
216*/
217ssize_t
218SimulatedStream::Zero(size_t size)
219{
220	status_t error = B_OK;
221	ssize_t bytesTotal = 0;
222	while (error == B_OK && size > 0) {
223		data_extent extent;
224		error = _GetExtent(fPosition, size, extent);
225		if (!error) {
226			if (extent.size > 0) {
227				ssize_t bytes = fStream.ZeroAt(extent.offset, extent.size);
228				if (bytes >= 0) {
229					size -= bytes;
230					fPosition += bytes;
231					bytesTotal += bytes;
232				} else {
233					error = status_t(bytes);
234				}
235			} else {
236				// end of simulated stream
237				break;
238			}
239		}
240	}
241	return !error ? bytesTotal : ssize_t(error);
242}
243
244/*! \brief Writes \a size bytes worth of zeros at position \a pos
245	in the stream without incrementing the stream's position marker.
246*/
247ssize_t
248SimulatedStream::ZeroAt(off_t pos, size_t size)
249{
250	status_t error = B_OK;
251	ssize_t bytesTotal = 0;
252	while (error == B_OK && size > 0) {
253		data_extent extent;
254		error = _GetExtent(pos, size, extent);
255		if (!error) {
256			if (extent.size > 0) {
257				ssize_t bytes = fStream.ZeroAt(extent.offset, extent.size);
258				if (bytes >= 0) {
259					size -= bytes;
260					pos += bytes;
261					bytesTotal += bytes;
262				} else {
263					error = status_t(bytes);
264				}
265			} else {
266				// end of simulated stream
267				break;
268			}
269		}
270	}
271	return !error ? bytesTotal : ssize_t(error);
272}
273
274off_t
275SimulatedStream::Seek(off_t pos, uint32 seek_mode)
276{
277	off_t size = _Size();
278	switch (seek_mode) {
279		case SEEK_SET:
280			fPosition = pos;
281			break;
282		case SEEK_CUR:
283			fPosition += pos;
284			break;
285		case SEEK_END:
286			fPosition = size + pos;
287			break;
288		default:
289			break;
290	}
291	// Range check
292	if (fPosition < 0)
293		fPosition = 0;
294	else if (fPosition > size && SetSize(fPosition) != B_OK)
295		fPosition = size;
296	return fPosition;
297}
298