Skip to content

Instantly share code, notes, and snippets.

@jeffhung
Created August 13, 2011 10:19
Show Gist options
  • Save jeffhung/1143708 to your computer and use it in GitHub Desktop.
Save jeffhung/1143708 to your computer and use it in GitHub Desktop.
Two buffer classes I wrote before, for slightly different purpose.
/**
* The auto_buffer class acts as a buffer of @p T, but allocate statically or
* dynamically according to requested buffer size. If requested buffer size
* is greater than @p StaticBytes, auto_buffer will dynamically allocate
* required memory space for the buffer, instead of statically as a member
* array.
*
* Usually, dynamic allocation gets more overheads, but if requested size is
* too large, we may exceed stack limit too soon. So, a smarter strategy is
* to allocate statically if required size is small, and turn to dynamic
* allocation if required size is big.
*
* Below is a typical code example of the described strategy:
*
* @code
* // calls wcstombs(), but throw if wcstombs() failed instead of returning
* // (size_t)-1.
* size_t wcstombs_ex(char* mbs, const wchar_t* wcs, size_t nbytes);
*
* // Convert from wide-character string to multi-byte character string.
* std::string wcs_to_mbs(const wchar_t* wcs)
* {
* char buffer[128]; // may not big enough
* char* pbuf = buffer; // point to statically allocated buffer by default
* size_t buf_nbytes = sizeof(buffer); // size of buffer pointed by pbuf
*
* // Get number of bytes required to store the converted multi-byte
* // string. The value returned by wcstombs do not include the
* // terminating null, so we add 1 by our self.
* size_t need_nbytes = wcstombs_ex(NULL, wcs, 0) + 1;
* if (need_nbytes > buf_nbytes) {
* // the buffer pointed by pbuf is not big enough,
* // dynamically allocate a new one instead:
* buf_nbytes = need_nbytes;
* pbuf = (char*)malloc(buf_nbytes);
* }
*
* // Do the real convertion.
* need_nbytes = wcstombs_ex(pbuf, wcs, buf_nbytes);
* pbuf[need_nbytes] = 0; // null terminate the string
* std::string mbs(pbuf); // save the std::string object for return
*
* // If pbuf was dynamically allocated, we need to free it before return.
* if (pbuf != buffer) {
* free(pbuf);
* }
*
* return mbs;
* }
* @endcode
*
* The above implementation may have memory leak, since we may throw in any
* point before releasing dynamically allocated memory. The auto_buffer class
* encapsulate the same strategy, and is exception-safe because the destructor
* will be called even if we throw in the middle.
*
* @attention The auto_buffer simply allocate required memory buffer, no
* object will be constructed.
*
* @attention Note that @p StaticBytes counts by number of bytes, not number
* of @p T elements. However, all member functions counts by
* number of @p T elements, to ease programming.
*
* @attention Since get() will get the internal buffer, we do not provide
* thread-safety guarantees. It is the caller's responsibility to
* properly protect the buffer.
*
* @todo Make allocation customizable by adding Allocator template parameter.
*
* @tparam T the type of objects to be holded in the buffer
* @tparam StaticBytes the buffer size, in number of bytes
*
* @internal
* This class is going to replace static_first_buffer.
*/
template < typename T
, int StaticBytes = 1024 // in bytes
>
class auto_buffer : private noncopyable
{
friend class auto_buffer_test; // for white-box testing
public:
/**
* Construct an auto_buffer object, with given required buffer size, @p
* nelems, in number of elements.
*
* @note Note that the unit of @p nelems is different with @p StaticBytes.
* The former is number of @p T elements while an element may occupy
* more than one bytes; and the later is number of bytes, so we can
* control the memory usage without dynamic allocation precisely.
*
* @param[in] nelems required buffer size, in number of elements
*/
auto_buffer(size_t nelems = (StaticBytes / sizeof(T)));
/**
* Destruct the auto_buffer object. If the memory was allocated
* dynamically, it will be released automatically. This provide extra
* exception-safety since code may throw in any point within a function,
* and may not be able to reach the point of releasing memory at the end
* of function.
*/
~auto_buffer();
/**
* Resize the buffer to @p nelems, in number of elements. Like realloc(),
* data in original buffer will be copied to new buffer bit-wisely. That
* is, the contents of the memory are unchanged up to the lesser of the
* new and old sizes. If the new size is larger, the contents of the
* newly allocated portion of the memory are undefined.
*
* @param[in] nelems required buffer size, in number of elements
*/
void resize(size_t nelems);
/**
* Reset to initial size, the size specified in constructor or a default
* size if not given.
*/
void reset_size();
/**
* Get pointer to the actual buffer.
* @{
*/
T* get();
const T* get() const;
/** @} */
/**
* Get size of the buffer, in number of elements.
*/
size_t size() const;
/**
* Get size of the buffer, in number of bytes.
*/
size_t nbytes() const;
private:
size_t size_; //!< buffer size of @p pbuf_, in number of elements
byte_t* pbuf_; //!< pointer point to the actural buffer
byte_t sbuf_[StaticBytes]; //!< static allocated buffer, used when size_ is small enough
};
/**
* The byte_buffer class wraps common buffer operations for easy accessing.
* The size of the buffer will grow/shrink automatically.
*/
class byte_buffer
{
public:
byte_buffer();
byte_buffer(const byte_buffer& x);
~byte_buffer();
byte_buffer& operator = (const byte_buffer& x);
void swap(byte_buffer& x);
bool empty() const;
size_t size() const;
size_t capacity() const;
size_t push_back(const byte_t* buf, size_t nbytes);
size_t push_back(const char* buf, size_t nbytes);
size_t push_back(const void* buf, size_t nbytes);
size_t pop_front(byte_t* buf, size_t nbytes);
size_t pop_front(char* buf, size_t nbytes);
size_t pop_front(void* buf, size_t nbytes);
const byte_t* data() const;
byte_t at(size_t i) const;
private:
size_t size_;
byte_t* data_;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment