Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * dynamic_buffer.cpp - A dynamic buffer 00004 * 00005 * Created: Fri Jun 01 13:28:46 2007 00006 * Copyright 2007-2008 Tim Niemueller [www.niemueller.de] 00007 * 2007 Daniel Beck 00008 * 00009 ****************************************************************************/ 00010 00011 /* This program is free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. A runtime exception applies to 00015 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00016 * 00017 * This program is distributed in the hope that it will be useful, 00018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 * GNU Library General Public License for more details. 00021 * 00022 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00023 */ 00024 00025 #include <core/exceptions/system.h> 00026 #include <core/exceptions/software.h> 00027 #include <netcomm/utils/dynamic_buffer.h> 00028 00029 #include <cstdlib> 00030 #include <cstring> 00031 #include <netinet/in.h> 00032 00033 namespace fawkes { 00034 00035 /** @class DynamicBuffer <netcomm/utils/dynamic_buffer.h> 00036 * Dynamically growing buffer. 00037 * This class maintains a list or arbitrary data objects stuffed into 00038 * one consecutive memory. The buffer layout is like the following: 00039 * A dynamic_list_t element is supplied and can reside anywhere you 00040 * like (in the case of the Fawkes network protocol in your static 00041 * struct). It contains information about the size and number of elements 00042 * of the list. The list itself is formed by concatenated memory regions, 00043 * each preceeded by a two byte length value. 00044 * 00045 * The list may be at most 4 GB in total size (including in-between headers) 00046 * Each list item by itself can be at most 64 KB in size. 00047 * The buffer starts with an initial size. If this initial size is exceeded 00048 * the buffer size is doubled. If the double size would exceed 4 GB it is 00049 * increased to exactly 4 GB. 00050 * 00051 * The numbers in the headers are stored in network byte order and thus are 00052 * suitable for direct sending over the network. 00053 * 00054 * @ingroup NetComm 00055 * @author Tim Niemueller 00056 */ 00057 00058 00059 /** Write constructor. 00060 * Use this constructor to create and write to this dynamic buffer. 00061 * @param db dynamic list header in your message 00062 * @param initial_buffer_size initial buffer size to use, by default 1 KB 00063 */ 00064 DynamicBuffer::DynamicBuffer(dynamic_list_t *db, size_t initial_buffer_size) 00065 { 00066 _read_only = false; 00067 _db = db; 00068 _buffer_size = initial_buffer_size; 00069 _buffer = malloc(_buffer_size); 00070 _curhead = (element_header_t *)_buffer; 00071 _curdata = (void *)((size_t)_buffer + sizeof(element_header_t)); 00072 00073 _db->size = htonl(0); 00074 _db->num_elements = htonl(0); 00075 00076 _it_curhead = NULL; 00077 _it_curdata = NULL; 00078 _it_curel = 0; 00079 } 00080 00081 00082 /** Read constructor. 00083 * Use this constructor to read from a dynamic buffer. 00084 * @param db dynamic list header in the incoming message 00085 * @param buf buffer to parse 00086 * @param size size of the buffer 00087 */ 00088 DynamicBuffer::DynamicBuffer(dynamic_list_t *db, void *buf, size_t size) 00089 { 00090 _read_only = true; 00091 _db = db; 00092 _buffer_size = size; 00093 _buffer = buf; 00094 00095 _curhead = (element_header_t *)_buffer; 00096 _curdata = (void *)((size_t)_buffer + sizeof(element_header_t)); 00097 00098 _it_curhead = _curhead; 00099 _it_curdata = _curdata; 00100 _it_curel = 0; 00101 } 00102 00103 00104 /** Destructor. 00105 * If in writing mode frees up the buffer. 00106 */ 00107 DynamicBuffer::~DynamicBuffer() 00108 { 00109 if (! _read_only) free(_buffer); 00110 } 00111 00112 00113 /** Append data. 00114 * Appends data to the list. Throws an exception if there is not enough memory to 00115 * hold the data or if in read-only mode. 00116 * @param data data to append 00117 * @param data_size size of the data in bytes 00118 * @exception AccessViolationException thrown if buffer is in read-only mode 00119 * @exception IllegalArgumentException thrown if data size is bigger than 64 KB - 2 bytes 00120 * @exception OutOfMemoryException thrown if no memory could be allocated 00121 */ 00122 void 00123 DynamicBuffer::append(const void *data, size_t data_size) 00124 { 00125 if ( _read_only ) throw AccessViolationException("DynamicBuffer is read-only"); 00126 00127 if ( data_size > (0xFFFF - sizeof(element_header_t)) ) { 00128 throw IllegalArgumentException("Buffer size too big, max 65535 bytes"); 00129 } 00130 00131 size_t cur_size = ntohl(_db->size); 00132 if ( (cur_size + data_size + sizeof(element_header_t)) > _buffer_size ) { 00133 try { 00134 increase(); 00135 } catch (OutOfMemoryException &e) { 00136 throw; 00137 } 00138 if ( (cur_size + data_size + sizeof(element_header_t)) > _buffer_size ) { 00139 throw OutOfMemoryException("Could not increase buffer far enough to hold data"); 00140 } 00141 } 00142 00143 *_curhead = htons(data_size); 00144 memcpy(_curdata, data, data_size); 00145 00146 _curhead = (element_header_t *)((size_t)_curhead + data_size 00147 + sizeof(element_header_t)); 00148 _curdata = (void *)((size_t)_curdata + data_size + sizeof(element_header_t)); 00149 _db->size = htonl(cur_size + sizeof(element_header_t) + data_size); 00150 uint16_t tmp = ntohl(_db->num_elements) + 1; 00151 _db->num_elements = htonl(tmp); 00152 } 00153 00154 00155 /** Get pointer to buffer. 00156 * @return packed buffer 00157 */ 00158 void * 00159 DynamicBuffer::buffer() 00160 { 00161 return _buffer; 00162 } 00163 00164 00165 /** Increase buffer size. 00166 * Internal usage only. 00167 */ 00168 void 00169 DynamicBuffer::increase() 00170 { 00171 size_t new_buffer_size; 00172 00173 if ( (_buffer_size) >= 0xFFFFFFFF / 2 ) { 00174 if ( _buffer_size == 0xFFFFFFFF ) { 00175 throw OutOfMemoryException("Dynamic buffer may not be greater than 64KB"); 00176 } else { 00177 new_buffer_size = 0xFFFFFFFF; 00178 } 00179 } else { 00180 new_buffer_size = _buffer_size * 2; 00181 } 00182 00183 void *tmp = realloc(_buffer, new_buffer_size); 00184 if ( tmp == NULL ) { 00185 throw OutOfMemoryException(); 00186 } else { 00187 _buffer_size = new_buffer_size; 00188 _curhead = (element_header_t *)((size_t)tmp + ((size_t)_curhead - (size_t)_buffer)); 00189 _curdata = (void *)((size_t)tmp + ((size_t)_curdata - (size_t)_buffer)); 00190 _buffer = tmp; 00191 } 00192 } 00193 00194 00195 /** Get buffer size. 00196 * Gets the size of the used part of the buffer. The size of the buffer that 00197 * is really occupied by data. 00198 * @return size of occupied buffer 00199 */ 00200 size_t 00201 DynamicBuffer::buffer_size() 00202 { 00203 return ntohl(_db->size); 00204 } 00205 00206 00207 /** Get real buffer size. 00208 * Gets the real size of the buffer including yet unused parts. Meant to be 00209 * used for debugging or informational usage. 00210 * @return real buffer size 00211 */ 00212 size_t 00213 DynamicBuffer::real_buffer_size() 00214 { 00215 return _buffer_size; 00216 } 00217 00218 00219 /** Get number of elements. 00220 * @return number of elements in list 00221 */ 00222 unsigned int 00223 DynamicBuffer::num_elements() 00224 { 00225 return ntohl(_db->num_elements); 00226 } 00227 00228 00229 /** Reset iterator. 00230 */ 00231 void 00232 DynamicBuffer::reset_iterator() 00233 { 00234 _it_curhead = _curhead; 00235 _it_curdata = _curdata; 00236 // invalid element 00237 _it_curel = 0; 00238 } 00239 00240 00241 /** Check if another element is available. 00242 * @return true if another element can be fetched with next(), false otherwise 00243 */ 00244 bool 00245 DynamicBuffer::has_next() 00246 { 00247 return (_read_only && (_it_curel < (ntohl(_db->num_elements)))); 00248 } 00249 00250 00251 /** Get next buffer. 00252 * @param size upon successful return contains size of the current list buffer 00253 * @return the next buffer. 00254 * @exception OutOfBoundsException thrown if no further element is available 00255 * in the list. 00256 */ 00257 void * 00258 DynamicBuffer::next(size_t *size) 00259 { 00260 // advance 00261 if ( ! has_next() ) { 00262 throw OutOfBoundsException("No next element while iterator DynamicBuffer"); 00263 } 00264 00265 if ( _it_curel > 0 ) { 00266 size_t offset = ntohs(*_it_curhead) + sizeof(element_header_t); 00267 _it_curhead = (element_header_t *)((size_t)_it_curhead + offset); 00268 _it_curdata = (void *)((size_t)_it_curdata + offset); 00269 } 00270 ++_it_curel; 00271 *size = ntohs(*_it_curhead); 00272 00273 return _it_curdata; 00274 } 00275 00276 } // end namespace fawkes