1/*
2 * Copyright 2006-2007, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan Aßmus <superstippi@gmx.de>
7 */
8
9
10#include "LittleEndianBuffer.h"
11
12#include <ByteOrder.h>
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17
18
19_USING_ICON_NAMESPACE
20
21
22#define CHUNK_SIZE 256
23
24
25// constructor
26LittleEndianBuffer::LittleEndianBuffer()
27	: fBuffer((uint8*)malloc(CHUNK_SIZE)),
28	  fHandle(fBuffer),
29	  fBufferEnd(fBuffer + CHUNK_SIZE),
30	  fSize(CHUNK_SIZE),
31	  fOwnsBuffer(true)
32{
33}
34
35// constructor
36LittleEndianBuffer::LittleEndianBuffer(size_t size)
37	: fBuffer((uint8*)malloc(size)),
38	  fHandle(fBuffer),
39	  fBufferEnd(fBuffer + size),
40	  fSize(size),
41	  fOwnsBuffer(true)
42{
43}
44
45// constructor
46LittleEndianBuffer::LittleEndianBuffer(uint8* buffer, size_t size)
47	: fBuffer(buffer),
48	  fHandle(fBuffer),
49	  fBufferEnd(fBuffer + size),
50	  fSize(size),
51	  fOwnsBuffer(false)
52{
53}
54
55// destructor
56LittleEndianBuffer::~LittleEndianBuffer()
57{
58	if (fOwnsBuffer)
59		free(fBuffer);
60}
61
62// Write 8
63bool
64LittleEndianBuffer::Write(uint8 value)
65{
66	if (fHandle == fBufferEnd)
67		_SetSize(fSize + CHUNK_SIZE);
68
69	if (!fBuffer)
70		return false;
71
72	*fHandle = value;
73	fHandle++;
74
75	return true;
76}
77
78// Write 16
79bool
80LittleEndianBuffer::Write(uint16 value)
81{
82	if ((fHandle + 1) >= fBufferEnd)
83		_SetSize(fSize + CHUNK_SIZE);
84
85	if (!fBuffer)
86		return false;
87
88	*(uint16*)fHandle = B_HOST_TO_LENDIAN_INT16(value);
89	fHandle += 2;
90
91	return true;
92}
93
94// Write 32
95bool
96LittleEndianBuffer::Write(uint32 value)
97{
98	if ((fHandle + 3) >= fBufferEnd)
99		_SetSize(fSize + CHUNK_SIZE);
100
101	if (!fBuffer)
102		return false;
103
104	*(uint32*)fHandle = B_HOST_TO_LENDIAN_INT32(value);
105	fHandle += 4;
106
107	return true;
108}
109
110// Write double
111bool
112LittleEndianBuffer::Write(float value)
113{
114	if ((fHandle + sizeof(float) - 1) >= fBufferEnd)
115		_SetSize(fSize + CHUNK_SIZE);
116
117	if (!fBuffer)
118		return false;
119
120	*(float*)fHandle = B_HOST_TO_LENDIAN_FLOAT(value);
121	fHandle += sizeof(float);
122
123	return true;
124}
125
126// Write double
127bool
128LittleEndianBuffer::Write(double value)
129{
130	if ((fHandle + sizeof(double) - 1) >= fBufferEnd)
131		_SetSize(fSize + CHUNK_SIZE);
132
133	if (!fBuffer)
134		return false;
135
136	*(double*)fHandle = B_HOST_TO_LENDIAN_DOUBLE(value);
137	fHandle += sizeof(double);
138
139	return true;
140}
141
142// Write LittleEndianBuffer
143bool
144LittleEndianBuffer::Write(const LittleEndianBuffer& other)
145{
146	return Write(other.Buffer(), other.SizeUsed());
147}
148
149// Write buffer
150bool
151LittleEndianBuffer::Write(const uint8* buffer, size_t bytes)
152{
153	if (bytes == 0)
154		return true;
155
156	// figure out needed size and suitable new size
157	size_t neededSize = SizeUsed() + bytes;
158	size_t newSize = fSize;
159	while (newSize < neededSize)
160		newSize += CHUNK_SIZE;
161
162	// resize if necessary
163	if (newSize > fSize)
164		_SetSize(newSize);
165
166	if (!fBuffer)
167		return false;
168
169	// paste buffer
170	memcpy(fHandle, buffer, bytes);
171	fHandle += bytes;
172
173	return true;
174}
175
176// #pragma mark -
177
178// Read 8
179bool
180LittleEndianBuffer::Read(uint8& value)
181{
182	if (fHandle >= fBufferEnd)
183		return false;
184
185	value = *fHandle++;
186
187	return true;
188}
189
190// Read 16
191bool
192LittleEndianBuffer::Read(uint16& value)
193{
194	if ((fHandle + 1) >= fBufferEnd)
195		return false;
196
197	value = B_LENDIAN_TO_HOST_INT16(*(uint16*)fHandle);
198	fHandle += 2;
199
200	return true;
201}
202
203// Read 32
204bool
205LittleEndianBuffer::Read(uint32& value)
206{
207	if ((fHandle + 3) >= fBufferEnd)
208		return false;
209
210	value = B_LENDIAN_TO_HOST_INT32(*(uint32*)fHandle);
211	fHandle += 4;
212
213	return true;
214}
215
216// Read float
217bool
218LittleEndianBuffer::Read(float& value)
219{
220	if ((fHandle + sizeof(float) - 1) >= fBufferEnd)
221		return false;
222
223	value = B_LENDIAN_TO_HOST_FLOAT(*(float*)fHandle);
224	fHandle += sizeof(float);
225
226	return true;
227}
228
229// Read double
230bool
231LittleEndianBuffer::Read(double& value)
232{
233	if ((fHandle + sizeof(double) - 1) >= fBufferEnd)
234		return false;
235
236	value = B_LENDIAN_TO_HOST_DOUBLE(*(double*)fHandle);
237	fHandle += sizeof(double);
238
239	return true;
240}
241
242// Read LittleEndianBuffer
243bool
244LittleEndianBuffer::Read(LittleEndianBuffer& other, size_t bytes)
245{
246	if ((fHandle + bytes - 1) >= fBufferEnd)
247		return false;
248
249	if (other.Write(fHandle, bytes)) {
250		// reset other handle to beginning of pasted data
251		other.fHandle -= bytes;
252		fHandle += bytes;
253		return true;
254	}
255
256	return false;
257}
258
259// #pragma mark -
260
261// Skip
262void
263LittleEndianBuffer::Skip(size_t bytes)
264{
265	// NOTE: is ment to be used while reading!!
266	// when used while writing, the growing will not work reliably
267	fHandle += bytes;
268}
269
270// Reset
271void
272LittleEndianBuffer::Reset()
273{
274	fHandle = fBuffer;
275}
276
277// #pragma mark -
278
279// _SetSize
280void
281LittleEndianBuffer::_SetSize(size_t size)
282{
283	if (!fOwnsBuffer) {
284		// prevent user error
285		// (we are in read mode)
286		fBuffer = NULL;
287		return;
288	}
289
290	int32 pos = fHandle - fBuffer;
291	fBuffer = (uint8*)realloc((void*)fBuffer, size);
292	fHandle = fBuffer + pos;
293	fBufferEnd = fBuffer + size;
294	fSize = size;
295}
296
297