// File: buffer.hpp // // Copyright (c) 2003 // Kevin Atkinson // // Permission to use, copy, modify, distribute and sell this software // and its documentation for any purpose is hereby granted without // fee, provided that the above copyright notice appear in all copies // and that both that copyright notice and this permission notice // appear in supporting documentation. Kevin Atkinson makes no // representations about the suitability of this software for any // purpose. It is provided "as is" without express or implied // warranty. #ifndef DISTRIBNET_BUFFER__HPP #define DISTRIBNET_BUFFER__HPP #include namespace distribnet { typedef unsigned char byte; class Buffer { public: typedef byte value; typedef byte & reference; typedef const byte & const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; // Unlike a vector iterators are guaranteed to be pointers. This // is to make using vector with C functions convenient typedef byte * iterator; typedef const byte * const_iterator; size_type size() const {return end_ - begin_;} bool empty() const {return begin_ == end_;} size_type capacity() const {return capacity_ & own_mask_;} bool own() const {return ~capacity_ & ~own_mask_;} size_type max_size() const {return own() ? own_mask_ : capacity();} private: // The bit to store if the memory is user owned is // in the high bit of capacity_ to avoid making sizeof(Buffer) // just over a even power of 2. The performace impact is // minimial since capacity_ is not used that often. // Setting capacity_ will reset own() to true. static const size_type own_mask_ = ~(1ul << (sizeof(size_type)*8-1)); // FIXME: ^ This is unportable for odd ball machines in a // byte of memory is not 8 bits. byte * storage_begin() {return storage_end_ - capacity();} byte * storage_end () {return storage_end_;} void own(bool v) {capacity_ = v ? capacity_ & own_mask_ : capacity_ | ~own_mask_;} public: void clear() {begin_ = end_ = storage_begin();} void reset(); // erase all elements and release memory void swap(Buffer & other); void assign(const Buffer & other); Buffer() : begin_(0), end_(0), storage_end_(0), capacity_(0) {} Buffer(const Buffer &); Buffer & operator= (const Buffer & other) {assign(other); return *this;} ~Buffer() {reset();} byte * begin() {return begin_;} byte * end() {return end_;} const byte * begin() const {return begin_;} const byte * end() const {return end_;} byte * data(size_type pos = 0) {return begin_ + pos;} byte * data_end() {return end_;} const byte * data(size_type pos = 0) const {return begin_ + pos;} const byte * data_end() const {return end_;} byte & operator[] (size_type n) {return begin_[n];} const byte & operator[] (size_type n) const {return begin_[n];} byte & at (size_type n) {return begin_[n];} const byte & at (size_type n) const {return begin_[n];} byte & front() {return *begin_;} byte & back() {return *end_;} const byte & front() const {return *begin_;} const byte & back() const {return *end_;} template U * datap(size_type pos = 0) {return reinterpret_cast(begin_) + pos;} template const U * datap(size_type pos = 0) const {return reinterpret_cast(begin_) + pos;} size_type alloc(size_type s) { size_type old_size = size(); resize(old_size + s); return old_size;} void resize(size_type s); void resize(size_type s, byte v) { difference_type diff = s - size(); resize(s); if (s > 0) memset(end_ - diff, v, diff);} void reserve(size_type s); void reserve_towards_back(size_type s); // Shrink the allocated memory so that no space is wasted bool shrink(); // Set the buffer to point to a specific area of memory void set(byte * new_begin, byte * new_end); void set(void * d, size_type s) {set(static_cast(d), static_cast(d) + s);} // Set up the buffer to use a specific region of memory for its // storage. The results are undefined if a buffer operation needs // more memory than is provided. void set_storage(byte * new_begin, byte * new_end); void set_storage(void * d, size_type s) {set_storage(static_cast(d), static_cast(d) + s);} void align_front(); void align_back(); void prepend (byte d) { if (storage_begin() < begin_) {begin_ -= 1;} else {adj_range(begin_, begin_, 1);} *begin_ = d;} void prepend (const byte * start, const byte * stop) {prepend(start, stop - start);} void prepend (const void * d, size_type s) { if (storage_begin() + s <= begin_) {begin_ -= s;} else {adj_range(begin_, begin_, s);} std::memcpy(begin_, d, s);} void prepend (size_type s, byte v) { if (storage_begin() + s <= begin_) {begin_ -= s;} else {adj_range(begin_, begin_, s);} std::memset(begin_, v, s);} void append (byte d) { if (end_ < storage_end()) {end_ += 1;} else {adj_range(end_, end_, 1);} end_[-1] = d;} void append (size_type s, byte v) { if (end_ + s <= storage_end()) {end_ += s;} else {adj_range(end_, end_, s);} std::memset(end_ - s, v, s);} void append (const byte * start, const byte * stop) {append(start, stop - start);} void append (const void * d, size_type s) { if (end_ + s <= storage_end()) {end_ += s;} else {adj_range(end_, end_, s);} std::memcpy(end_ - s, d, s);} void assign (const void * d, size_type s) { reserve(s); begin_ = storage_begin(); end_ = begin_ + s; std::memcpy(begin_, d, s);} void assign (const byte * start, const byte * stop) {assign(start, stop - start);} void assign (size_type n, byte v) { reserve(n); begin_ = storage_begin(); end_ = begin_ + n; std::memset(begin_, v, n);} void push_front (byte d) {prepend(d);} void push_back (byte d) {append(d);} void pop_front(size_type s = 1) {begin_ += s;} void pop_back(size_type s = 1) {end_ -= s;} void insert (size_type pos, byte val) { adj_range(begin_ + pos, begin_ + pos, 1); begin_[pos] = val;} void insert (byte * pos, byte val) {insert(pos - begin_, val);} void insert (size_type pos, const void * d, size_type s) { adj_range(begin_ + pos, begin_ + pos, s); std::memcpy(begin_ + pos, d, s);} void insert (size_type pos, const byte * start, const byte * stop) {insert(pos, start, stop - start);} void insert (byte * pos, const byte * start, const byte * stop) {insert(pos - begin_, start, stop - start);} void insert (size_type pos, size_type s, byte val) { adj_range(begin_ + pos, begin_ + pos, s); std::memset(begin_ + pos, val, s);} void insert (byte * pos, size_type s, byte val) {insert(pos - begin_, s, val);} // TODO generic template insert void erase (void * d, size_type s = 1) {adj_range(static_cast(d), static_cast(d) + s, 0);} void erase (byte * start, byte * stop) {adj_range(start, stop, 0);} void replace(size_type pos, size_type s, const void * d, size_type ds) { adj_range(begin_ + pos, begin_ + pos + s, ds); std::memcpy(begin_ + pos, d, ds);} void replace(byte * start, byte * stop, const byte * rstart, const byte * rstop) {replace(start - begin_, stop - start, rstart, rstop - rstart);} void replace(byte * start, byte * stop, const void * d, size_type ds) {replace(start - begin_, stop - start, d, ds);} private: // adj_range: // change the size of the range from start to stop to new_size // precond: begin() <= start <= stop <= end() // size() + (intv_size - (stop - start)) <= max_size() // (ie the new size after the adjustment <= max_size()) void adj_range(byte * start, byte * stop, size_type intv_size); byte * begin_; byte * end_; byte * storage_end_; size_type capacity_; }; template class TypedBuffer : public Buffer { public: typedef T Type; T * operator-> () {return datap();} const T * operator-> () const {return datap();} T * ptr () {return datap();} const T * ptr () const {return datap();} void * data_block() {return static_cast(data(sizeof(T)));} void * data_block() const {return static_cast(data(sizeof(T)));} unsigned int data_block_size() const {int s = size() - sizeof(T); return s < 0 ? 0 : s;} int construct() {alloc(sizeof(T)); new(data()) T(); return sizeof(T);} }; } #endif