dynamic_buffer.cpp

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

Generated on Tue Feb 22 13:32:28 2011 for Fawkes API by  doxygen 1.4.7