Fawkes API Fawkes Development Version
dynamic_buffer.cpp
1
2/***************************************************************************
3 * dynamic_buffer.cpp - A dynamic buffer
4 *
5 * Created: Fri Jun 01 13:28:46 2007
6 * Copyright 2007-2008 Tim Niemueller [www.niemueller.de]
7 * 2007 Daniel Beck
8 *
9 ****************************************************************************/
10
11/* This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version. A runtime exception applies to
15 * this software (see LICENSE.GPL_WRE file mentioned below for details).
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
23 */
24
25#include <core/exceptions/software.h>
26#include <core/exceptions/system.h>
27#include <netcomm/utils/dynamic_buffer.h>
28#include <netinet/in.h>
29
30#include <cstdlib>
31#include <cstring>
32
33namespace fawkes {
34
35/** @class DynamicBuffer <netcomm/utils/dynamic_buffer.h>
36 * Dynamically growing buffer.
37 * This class maintains a list or arbitrary data objects stuffed into
38 * one consecutive memory. The buffer layout is like the following:
39 * A dynamic_list_t element is supplied and can reside anywhere you
40 * like (in the case of the Fawkes network protocol in your static
41 * struct). It contains information about the size and number of elements
42 * of the list. The list itself is formed by concatenated memory regions,
43 * each preceeded by a two byte length value.
44 *
45 * The list may be at most 4 GB in total size (including in-between headers)
46 * Each list item by itself can be at most 64 KB in size.
47 * The buffer starts with an initial size. If this initial size is exceeded
48 * the buffer size is doubled. If the double size would exceed 4 GB it is
49 * increased to exactly 4 GB.
50 *
51 * The numbers in the headers are stored in network byte order and thus are
52 * suitable for direct sending over the network.
53 *
54 * @ingroup NetComm
55 * @author Tim Niemueller
56 */
57
58/** Write constructor.
59 * Use this constructor to create and write to this dynamic buffer.
60 * @param db dynamic list header in your message
61 * @param initial_buffer_size initial buffer size to use, by default 1 KB
62 */
63DynamicBuffer::DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size)
64{
65 _read_only = false;
66 _db = db;
67 _buffer_size = initial_buffer_size;
68 _buffer = malloc(_buffer_size);
69 _curhead = (element_header_t *)_buffer;
70 _curdata = (void *)((size_t)_buffer + sizeof(element_header_t));
71
72 _db->size = htonl(0);
73 _db->num_elements = htonl(0);
74
75 _it_curhead = NULL;
76 _it_curdata = NULL;
77 _it_curel = 0;
78}
79
80/** Read constructor.
81 * Use this constructor to read from a dynamic buffer.
82 * @param db dynamic list header in the incoming message
83 * @param buf buffer to parse
84 * @param size size of the buffer
85 */
86DynamicBuffer::DynamicBuffer(dynamic_list_t *db, void *buf, size_t size)
87{
88 _read_only = true;
89 _db = db;
90 _buffer_size = size;
91 _buffer = buf;
92
93 _curhead = (element_header_t *)_buffer;
94 _curdata = (void *)((size_t)_buffer + sizeof(element_header_t));
95
96 _it_curhead = _curhead;
97 _it_curdata = _curdata;
98 _it_curel = 0;
99}
100
101/** Destructor.
102 * If in writing mode frees up the buffer.
103 */
105{
106 if (!_read_only)
107 free(_buffer);
108}
109
110/** Append data.
111 * Appends data to the list. Throws an exception if there is not enough memory to
112 * hold the data or if in read-only mode.
113 * @param data data to append
114 * @param data_size size of the data in bytes
115 * @exception AccessViolationException thrown if buffer is in read-only mode
116 * @exception IllegalArgumentException thrown if data size is bigger than 64 KB - 2 bytes
117 * @exception OutOfMemoryException thrown if no memory could be allocated
118 */
119void
120DynamicBuffer::append(const void *data, size_t data_size)
121{
122 if (_read_only)
123 throw AccessViolationException("DynamicBuffer is read-only");
124
125 if (data_size > (0xFFFF - sizeof(element_header_t))) {
126 throw IllegalArgumentException("Buffer size too big, max 65535 bytes");
127 }
128
129 size_t cur_size = ntohl(_db->size);
130 if ((cur_size + data_size + sizeof(element_header_t)) > _buffer_size) {
131 try {
132 increase();
133 } catch (OutOfMemoryException &e) {
134 throw;
135 }
136 if ((cur_size + data_size + sizeof(element_header_t)) > _buffer_size) {
137 throw OutOfMemoryException("Could not increase buffer far enough to hold data");
138 }
139 }
140
141 *_curhead = htons(data_size);
142 memcpy(_curdata, data, data_size);
143
144 _curhead = (element_header_t *)((size_t)_curhead + data_size + sizeof(element_header_t));
145 _curdata = (void *)((size_t)_curdata + data_size + sizeof(element_header_t));
146 _db->size = htonl(cur_size + sizeof(element_header_t) + data_size);
147 uint16_t tmp = ntohl(_db->num_elements) + 1;
148 _db->num_elements = htonl(tmp);
149}
150
151/** Get pointer to buffer.
152 * @return packed buffer
153 */
154void *
156{
157 return _buffer;
158}
159
160/** Increase buffer size.
161 * Internal usage only.
162 */
163void
164DynamicBuffer::increase()
165{
166 size_t new_buffer_size;
167
168 if ((_buffer_size) >= 0xFFFFFFFF / 2) {
169 if (_buffer_size == 0xFFFFFFFF) {
170 throw OutOfMemoryException("Dynamic buffer may not be greater than 64KB");
171 } else {
172 new_buffer_size = 0xFFFFFFFF;
173 }
174 } else {
175 new_buffer_size = _buffer_size * 2;
176 }
177
178 void *tmp = realloc(_buffer, new_buffer_size);
179 if (tmp == NULL) {
180 throw OutOfMemoryException();
181 } else {
182 _buffer_size = new_buffer_size;
183 _curhead = (element_header_t *)((size_t)tmp + ((size_t)_curhead - (size_t)_buffer));
184 _curdata = (void *)((size_t)tmp + ((size_t)_curdata - (size_t)_buffer));
185 _buffer = tmp;
186 }
187}
188
189/** Get buffer size.
190 * Gets the size of the used part of the buffer. The size of the buffer that
191 * is really occupied by data.
192 * @return size of occupied buffer
193 */
194size_t
196{
197 return ntohl(_db->size);
198}
199
200/** Get real buffer size.
201 * Gets the real size of the buffer including yet unused parts. Meant to be
202 * used for debugging or informational usage.
203 * @return real buffer size
204 */
205size_t
207{
208 return _buffer_size;
209}
210
211/** Get number of elements.
212 * @return number of elements in list
213 */
214unsigned int
216{
217 return ntohl(_db->num_elements);
218}
219
220/** Reset iterator.
221 */
222void
224{
225 _it_curhead = _curhead;
226 _it_curdata = _curdata;
227 // invalid element
228 _it_curel = 0;
229}
230
231/** Check if another element is available.
232 * @return true if another element can be fetched with next(), false otherwise
233 */
234bool
236{
237 return (_read_only && (_it_curel < (ntohl(_db->num_elements))));
238}
239
240/** Get next buffer.
241 * @param size upon successful return contains size of the current list buffer
242 * @return the next buffer.
243 * @exception OutOfBoundsException thrown if no further element is available
244 * in the list.
245 */
246void *
248{
249 // advance
250 if (!has_next()) {
251 throw OutOfBoundsException("No next element while iterator DynamicBuffer");
252 }
253
254 if (_it_curel > 0) {
255 size_t offset = ntohs(*_it_curhead) + sizeof(element_header_t);
256 _it_curhead = (element_header_t *)((size_t)_it_curhead + offset);
257 _it_curdata = (void *)((size_t)_it_curdata + offset);
258 }
259 ++_it_curel;
260 *size = ntohs(*_it_curhead);
261
262 return _it_curdata;
263}
264
265} // end namespace fawkes
Access violates policy.
Definition: software.h:93
size_t buffer_size()
Get buffer size.
void append(const void *data, size_t data_size)
Append data.
void reset_iterator()
Reset iterator.
bool has_next()
Check if another element is available.
unsigned int num_elements()
Get number of elements.
virtual ~DynamicBuffer()
Destructor.
void * next(size_t *size)
Get next buffer.
size_t real_buffer_size()
Get real buffer size.
DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size=1024)
Write constructor.
void * buffer()
Get pointer to buffer.
Expected parameter is missing.
Definition: software.h:80
Index out of bounds.
Definition: software.h:86
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
Fawkes library namespace.
Dynamic list type.
uint32_t num_elements
number of elements in list
uint32_t size
total size of list buffer