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 
33 namespace 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  */
63 DynamicBuffer::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  */
86 DynamicBuffer::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  */
119 void
120 DynamicBuffer::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  */
154 void *
156 {
157  return _buffer;
158 }
159 
160 /** Increase buffer size.
161  * Internal usage only.
162  */
163 void
164 DynamicBuffer::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  */
194 size_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  */
205 size_t
207 {
208  return _buffer_size;
209 }
210 
211 /** Get number of elements.
212  * @return number of elements in list
213  */
214 unsigned int
216 {
217  return ntohl(_db->num_elements);
218 }
219 
220 /** Reset iterator.
221  */
222 void
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  */
234 bool
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  */
246 void *
247 DynamicBuffer::next(size_t *size)
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
fawkes::IllegalArgumentException
Expected parameter is missing.
Definition: software.h:80
fawkes::DynamicBuffer::append
void append(const void *data, size_t data_size)
Append data.
Definition: dynamic_buffer.cpp:120
fawkes::dynamic_list_t::num_elements
uint32_t num_elements
number of elements in list
Definition: dynamic_buffer.h:43
fawkes::DynamicBuffer::num_elements
unsigned int num_elements()
Get number of elements.
Definition: dynamic_buffer.cpp:215
fawkes::DynamicBuffer::has_next
bool has_next()
Check if another element is available.
Definition: dynamic_buffer.cpp:235
fawkes::dynamic_list_t::size
uint32_t size
total size of list buffer
Definition: dynamic_buffer.h:42
fawkes::AccessViolationException
Access violates policy.
Definition: software.h:93
fawkes::DynamicBuffer::buffer
void * buffer()
Get pointer to buffer.
Definition: dynamic_buffer.cpp:155
fawkes::OutOfBoundsException
Index out of bounds.
Definition: software.h:86
fawkes::DynamicBuffer::~DynamicBuffer
virtual ~DynamicBuffer()
Destructor.
Definition: dynamic_buffer.cpp:104
fawkes::DynamicBuffer::buffer_size
size_t buffer_size()
Get buffer size.
Definition: dynamic_buffer.cpp:195
fawkes
Fawkes library namespace.
fawkes::dynamic_list_t
Dynamic list type.
Definition: dynamic_buffer.h:41
fawkes::DynamicBuffer::real_buffer_size
size_t real_buffer_size()
Get real buffer size.
Definition: dynamic_buffer.cpp:206
fawkes::DynamicBuffer::reset_iterator
void reset_iterator()
Reset iterator.
Definition: dynamic_buffer.cpp:223
fawkes::OutOfMemoryException
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
fawkes::DynamicBuffer::next
void * next(size_t *size)
Get next buffer.
Definition: dynamic_buffer.cpp:247
fawkes::DynamicBuffer::DynamicBuffer
DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size=1024)
Write constructor.
Definition: dynamic_buffer.cpp:63