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/system.h>
26 #include <core/exceptions/software.h>
27 #include <netcomm/utils/dynamic_buffer.h>
28 
29 #include <cstdlib>
30 #include <cstring>
31 #include <netinet/in.h>
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 
59 /** Write constructor.
60  * Use this constructor to create and write to this dynamic buffer.
61  * @param db dynamic list header in your message
62  * @param initial_buffer_size initial buffer size to use, by default 1 KB
63  */
64 DynamicBuffer::DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size)
65 {
66  _read_only = false;
67  _db = db;
68  _buffer_size = initial_buffer_size;
69  _buffer = malloc(_buffer_size);
70  _curhead = (element_header_t *)_buffer;
71  _curdata = (void *)((size_t)_buffer + sizeof(element_header_t));
72 
73  _db->size = htonl(0);
74  _db->num_elements = htonl(0);
75 
76  _it_curhead = NULL;
77  _it_curdata = NULL;
78  _it_curel = 0;
79 }
80 
81 
82 /** Read constructor.
83  * Use this constructor to read from a dynamic buffer.
84  * @param db dynamic list header in the incoming message
85  * @param buf buffer to parse
86  * @param size size of the buffer
87  */
88 DynamicBuffer::DynamicBuffer(dynamic_list_t *db, void *buf, size_t size)
89 {
90  _read_only = true;
91  _db = db;
92  _buffer_size = size;
93  _buffer = buf;
94 
95  _curhead = (element_header_t *)_buffer;
96  _curdata = (void *)((size_t)_buffer + sizeof(element_header_t));
97 
98  _it_curhead = _curhead;
99  _it_curdata = _curdata;
100  _it_curel = 0;
101 }
102 
103 
104 /** Destructor.
105  * If in writing mode frees up the buffer.
106  */
108 {
109  if (! _read_only) free(_buffer);
110 }
111 
112 
113 /** Append data.
114  * Appends data to the list. Throws an exception if there is not enough memory to
115  * hold the data or if in read-only mode.
116  * @param data data to append
117  * @param data_size size of the data in bytes
118  * @exception AccessViolationException thrown if buffer is in read-only mode
119  * @exception IllegalArgumentException thrown if data size is bigger than 64 KB - 2 bytes
120  * @exception OutOfMemoryException thrown if no memory could be allocated
121  */
122 void
123 DynamicBuffer::append(const void *data, size_t data_size)
124 {
125  if ( _read_only ) throw AccessViolationException("DynamicBuffer is read-only");
126 
127  if ( data_size > (0xFFFF - sizeof(element_header_t)) ) {
128  throw IllegalArgumentException("Buffer size too big, max 65535 bytes");
129  }
130 
131  size_t cur_size = ntohl(_db->size);
132  if ( (cur_size + data_size + sizeof(element_header_t)) > _buffer_size ) {
133  try {
134  increase();
135  } catch (OutOfMemoryException &e) {
136  throw;
137  }
138  if ( (cur_size + data_size + sizeof(element_header_t)) > _buffer_size ) {
139  throw OutOfMemoryException("Could not increase buffer far enough to hold data");
140  }
141  }
142 
143  *_curhead = htons(data_size);
144  memcpy(_curdata, data, data_size);
145 
146  _curhead = (element_header_t *)((size_t)_curhead + data_size
147  + sizeof(element_header_t));
148  _curdata = (void *)((size_t)_curdata + data_size + sizeof(element_header_t));
149  _db->size = htonl(cur_size + sizeof(element_header_t) + data_size);
150  uint16_t tmp = ntohl(_db->num_elements) + 1;
151  _db->num_elements = htonl(tmp);
152 }
153 
154 
155 /** Get pointer to buffer.
156  * @return packed buffer
157  */
158 void *
160 {
161  return _buffer;
162 }
163 
164 
165 /** Increase buffer size.
166  * Internal usage only.
167  */
168 void
169 DynamicBuffer::increase()
170 {
171  size_t new_buffer_size;
172 
173  if ( (_buffer_size) >= 0xFFFFFFFF / 2 ) {
174  if ( _buffer_size == 0xFFFFFFFF ) {
175  throw OutOfMemoryException("Dynamic buffer may not be greater than 64KB");
176  } else {
177  new_buffer_size = 0xFFFFFFFF;
178  }
179  } else {
180  new_buffer_size = _buffer_size * 2;
181  }
182 
183  void *tmp = realloc(_buffer, new_buffer_size);
184  if ( tmp == NULL ) {
185  throw OutOfMemoryException();
186  } else {
187  _buffer_size = new_buffer_size;
188  _curhead = (element_header_t *)((size_t)tmp + ((size_t)_curhead - (size_t)_buffer));
189  _curdata = (void *)((size_t)tmp + ((size_t)_curdata - (size_t)_buffer));
190  _buffer = tmp;
191  }
192 }
193 
194 
195 /** Get buffer size.
196  * Gets the size of the used part of the buffer. The size of the buffer that
197  * is really occupied by data.
198  * @return size of occupied buffer
199  */
200 size_t
202 {
203  return ntohl(_db->size);
204 }
205 
206 
207 /** Get real buffer size.
208  * Gets the real size of the buffer including yet unused parts. Meant to be
209  * used for debugging or informational usage.
210  * @return real buffer size
211  */
212 size_t
214 {
215  return _buffer_size;
216 }
217 
218 
219 /** Get number of elements.
220  * @return number of elements in list
221  */
222 unsigned int
224 {
225  return ntohl(_db->num_elements);
226 }
227 
228 
229 /** Reset iterator.
230  */
231 void
233 {
234  _it_curhead = _curhead;
235  _it_curdata = _curdata;
236  // invalid element
237  _it_curel = 0;
238 }
239 
240 
241 /** Check if another element is available.
242  * @return true if another element can be fetched with next(), false otherwise
243  */
244 bool
246 {
247  return (_read_only && (_it_curel < (ntohl(_db->num_elements))));
248 }
249 
250 
251 /** Get next buffer.
252  * @param size upon successful return contains size of the current list buffer
253  * @return the next buffer.
254  * @exception OutOfBoundsException thrown if no further element is available
255  * in the list.
256  */
257 void *
258 DynamicBuffer::next(size_t *size)
259 {
260  // advance
261  if ( ! has_next() ) {
262  throw OutOfBoundsException("No next element while iterator DynamicBuffer");
263  }
264 
265  if ( _it_curel > 0 ) {
266  size_t offset = ntohs(*_it_curhead) + sizeof(element_header_t);
267  _it_curhead = (element_header_t *)((size_t)_it_curhead + offset);
268  _it_curdata = (void *)((size_t)_it_curdata + offset);
269  }
270  ++_it_curel;
271  *size = ntohs(*_it_curhead);
272 
273  return _it_curdata;
274 }
275 
276 } // end namespace fawkes
DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size=1024)
Write constructor.
unsigned int num_elements()
Get number of elements.
uint32_t size
total size of list buffer
uint32_t num_elements
number of elements in list
Fawkes library namespace.
void * buffer()
Get pointer to buffer.
Dynamic list type.
bool has_next()
Check if another element is available.
size_t buffer_size()
Get buffer size.
virtual ~DynamicBuffer()
Destructor.
size_t real_buffer_size()
Get real buffer size.
void reset_iterator()
Reset iterator.
void append(const void *data, size_t data_size)
Append data.
Index out of bounds.
Definition: software.h:88
Expected parameter is missing.
Definition: software.h:82
Access violates policy.
Definition: software.h:96
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32
void * next(size_t *size)
Get next buffer.