1 #ifndef POSIXX_BASIC_BUFFER_HPP_
2 #define POSIXX_BASIC_BUFFER_HPP_
4 #include <stdexcept> // std::bad_alloc, std::out_of_range
5 #include <limits> // std::numeric_limits
6 #include <tr1/type_traits> // std::tr1::is_integral, true_type, false_type
7 #include <cstring> // std::memcpy(), memset(), memcmp()
8 #include <cassert> // assert()
9 #include <cstddef> // std::size_t, ptrdiff_t
16 * This is pretty much like using a plain C-style array using dynamic memory,
17 * with a STL-ish interface.
19 * The buffer will use Allocator (which should be a function with realloc(3)
20 * semantics) for all storage management.
22 template< void* (*Allocator)(void*, size_t) >
26 //////////////////////////////////////////////////////////////////////
29 typedef unsigned char value_type; // TODO: use char to be more
30 // std::string-friendly?
33 typedef value_type& reference;
36 typedef const value_type& const_reference;
39 typedef value_type* iterator;
42 typedef const value_type* const_iterator;
45 typedef std::size_t size_type;
48 typedef std::ptrdiff_t difference_type;
51 typedef value_type* pointer;
54 typedef const pointer const_pointer;
57 typedef std::reverse_iterator< iterator > reverse_iterator;
60 typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
63 // Construct/Copy/Destroy
64 //////////////////////////////////////////////////////////////////////
67 * Creates a buffer of length zero (the default constructor).
69 explicit basic_buffer(): _data(NULL), _size(0) {}
72 * Creates a buffer with a capacity() of n (uninitialized) elements.
74 * @throw std::bad_alloc
76 explicit basic_buffer(size_type n):
77 _data(_allocate(NULL, n * sizeof(value_type))), _size(n)
82 * Creates a buffer of length n, containing n copies of value.
84 * @throw std::bad_alloc
86 basic_buffer(size_type n, const_reference value):
90 std::memset(_data, value, n);
94 * Creates a copy of x.
96 * @throw std::bad_alloc
98 basic_buffer(const basic_buffer< Allocator >& x):
99 _data(_allocate(NULL, x.size() * sizeof(value_type))),
102 std::memcpy(_data, x.c_array(), x.size());
106 * Creates a buffer of length finish - start, filled with all values
107 * obtained by dereferencing the InputIterators on the range [start,
110 * @throw std::bad_alloc
112 template < typename InputIterator >
113 basic_buffer(InputIterator start, InputIterator finish):
114 _data(NULL), _size(0)
116 assign(start, finish);
120 * Erases all elements in self then inserts into self a copy of each
123 * Invalidates all references and iterators.
125 * @returns a reference to self.
127 * @throw std::bad_alloc
129 basic_buffer< Allocator >& operator = (
130 const basic_buffer< Allocator >& x)
134 std::memcpy(_data, x.c_array(), x.size());
140 * Frees the memory holded by the buffer
147 //////////////////////////////////////////////////////////////////////
150 * Returns a random access iterator that points to the first element.
156 * Returns a random access const_iterator that points to the first
159 const_iterator begin() const
163 * Returns a random access iterator that points to the past-the-end
167 { return _data ? _data + _size : NULL; }
170 * Returns a random access const_iterator that points to the
171 * past-the-end value.
173 const_iterator end() const
174 { return _data ? _data + _size : NULL; }
177 * Returns a random access reverse_iterator that points to the
178 * past-the-end value.
180 reverse_iterator rbegin()
181 { return reverse_iterator(end()); }
184 * Returns a random access const_reverse_iterator that points to the
185 * past-the-end value.
187 const_reverse_iterator rbegin() const
188 { return const_reverse_iterator(end()); }
191 * Returns a random access reverse_iterator that points to the first
194 reverse_iterator rend()
195 { return reverse_iterator(begin()); }
198 * Returns a random access const_reverse_iterator that points to the
201 const_reverse_iterator rend() const
202 { return const_reverse_iterator(begin()); }
206 //////////////////////////////////////////////////////////////////////
209 * Returns the number of elements.
211 size_type size() const
215 * Returns size() of the largest possible buffer.
217 size_type max_size() const
218 { return std::numeric_limits< size_type >::max(); }
221 * Alters the size of self.
223 * If the new size (sz) is greater than the current size, then
224 * sz-size() (uninitialized) elements are inserted at the end of the
225 * buffer. If the new size is smaller than the current capacity, then
226 * the buffer is truncated by erasing size()-sz elements off the end.
227 * If sz is equal to capacity then no action is taken.
229 * Invalidates all references and iterators.
231 * @throw std::bad_alloc
233 void resize(size_type sz)
235 pointer n_data = _allocate(_data, sz * sizeof(value_type));
241 * Alters the size of self.
243 * If the new size (sz) is greater than the current size, then
244 * sz-size() copies of value are inserted at the end of the buffer. If
245 * the new size is smaller than the current capacity, then the buffer
246 * is truncated by erasing size()-sz elements off the end. If sz is
247 * equal to capacity then no action is taken.
249 * Invalidates all references and iterators.
251 * @throw std::bad_alloc
253 void resize(size_type sz, value_type value)
255 size_type old_size = size();
258 std::memset(_data + old_size, value, sz - old_size);
264 size_type capacity() const
268 * Returns true if the size is zero.
274 * If sz > size() call resize(sz), otherwise do nothing.
276 * Invalidates all references and iterators in the first case.
278 * @throw std::bad_alloc
280 void reserve(size_type sz)
288 //////////////////////////////////////////////////////////////////////
291 * Returns a reference to element n of self.
293 * The result can be used as an lvalue. The index n must be between
294 * 0 and the size less one.
296 reference operator[](size_type n)
297 { assert(n < _size); return _data[n]; }
300 * Returns a constant reference to element n of self.
302 * The index n must be between 0 and the size less one.
304 const_reference operator[](size_type n) const
305 { assert(n < _size); return _data[n]; }
308 * Returns a reference to element n of self.
310 * The result can be used as an lvalue. If index n is not between
311 * 0 and the size less one, a std::out_of_range exception.
313 reference at(size_type n)
316 throw std::out_of_range("posixx::basic_buffer::at(n)");
321 * Returns a constant reference to element n of self.
323 * If index n is not between 0 and the size less one,
324 * a std::out_of_range exception.
326 const_reference at(size_type n) const
329 throw std::out_of_range("posixx::basic_buffer::at(n)");
334 * Returns a reference to the first element.
337 { assert(_size); return _data[0]; }
340 * Returns a constant reference to the first element.
342 const_reference front() const
343 { assert(_size); return _data[0]; }
346 * Returns a reference to the last element.
349 { assert(_size); return _data[_size - 1]; }
352 * Returns a constant reference to the last element.
354 const_reference back() const
355 { assert(_size); return _data[_size - 1]; }
358 * Returns a pointer to a C-style array.
364 * Returns a pointer to a read-only C-style array.
366 const_pointer c_array() const
371 //////////////////////////////////////////////////////////////////////
374 * Replaces elements with copies of those in the range [start, finish).
376 * The function invalidates all iterators and references to elements in
379 * Invalidates all references and iterators.
381 template < typename InputIterator >
382 void assign(InputIterator start, InputIterator finish)
384 // Check whether it's an integral type. If so, it's not an
386 typename std::tr1::is_integral< InputIterator >::type is_int;
387 _assign_dispatch(start, finish, is_int);
391 * Replaces elements in *this with n copies of value.
393 * The function invalidates all iterators and references to elements in
396 * Invalidates all references and iterators.
398 void assign(size_type n, const_reference value)
401 std::memset(_data, value, n);
405 * Efficiently swaps the contents of x and y.
407 * Invalidates all references and iterators.
409 void swap(basic_buffer< Allocator >& x)
411 pointer tmp_data = x._data;
412 size_type tmp_size = x._size;
420 * Deletes all elements from the buffer.
422 * Invalidates all references and iterators.
426 _data = _allocate(_data, 0);
432 // The underlying data
435 // Size in number of items
438 // Allocator wrapper for automatic casting
439 static pointer _allocate(void* ptr, std::size_t sz)
441 if (ptr == NULL && sz == 0)
443 void* new_ptr = Allocator(ptr, sz);
444 if (sz != 0 && new_ptr == NULL)
445 throw std::bad_alloc();
446 return static_cast< pointer >(new_ptr);
451 * Helper assign functions to disambiguate the Iterator based and the
452 * N-value copy assign() methods. The last arguments indicates if
453 * InputIterator is an integral type.
456 // This is the version for the integral types, we should use the
457 // "regular" N-value copy assign().
458 template < typename InputIterator >
459 void _assign_dispatch(InputIterator start, InputIterator finish,
462 assign(static_cast< size_type >(start),
463 static_cast< value_type >(finish));
466 // This is the version for the real iterators.
467 template < typename InputIterator >
468 void _assign_dispatch(InputIterator start, InputIterator finish,
469 std::tr1::false_type)
471 // TODO: provide an efficient version for random iterators
472 while (start != finish) {
474 (*this)[size() - 1] = *start;
481 // Nonmember Operators
482 //////////////////////////////////////////////////////////////////////
485 * Returns true if x hass the same contents as y.
487 template < void* (*Allocator)(void*, std::size_t) >
488 bool operator == (const basic_buffer< Allocator >& x,
489 const basic_buffer < Allocator >& y)
493 if (x.size() != y.size())
495 if (x.empty() && y.empty())
497 if (x.empty() || y.empty())
499 int r = std::memcmp(x.c_array(), y.c_array(),
500 (x.size() < y.size()) ? x.size() : y.size());
502 return x.size() == y.size();
509 template < void* (*Allocator)(void*, std::size_t) >
510 bool operator != (const basic_buffer<Allocator>& x,
511 const basic_buffer <Allocator>& y)
517 * Returns true if the elements contained in x are lexicographically less than
518 * the elements contained in y.
520 template < void* (*Allocator)(void*, std::size_t) >
521 bool operator < (const basic_buffer< Allocator >& x,
522 const basic_buffer< Allocator >& y)
526 if (x.empty() && y.empty())
532 int r = std::memcmp(x.c_array(), y.c_array(),
533 (x.size() < y.size()) ? x.size() : y.size());
535 return x.size() < y.size();
542 template < void* (*Allocator)(void*, std::size_t) >
543 bool operator > (const basic_buffer< Allocator >& x,
544 const basic_buffer< Allocator >& y)
552 template < void* (*Allocator)(void*, std::size_t) >
553 bool operator <= (const basic_buffer< Allocator >& x,
554 const basic_buffer< Allocator >& y)
562 template < void* (*Allocator)(void*, std::size_t) >
563 bool operator >= (const basic_buffer< Allocator >& x,
564 const basic_buffer< Allocator >& y)
570 // Specialized Algorithms
571 //////////////////////////////////////////////////////////////////////
574 * Exchanges self with x, by swapping all elements.
576 * Invalidates all references and iterators.
578 template < void* (*Allocator)(void*, std::size_t) >
579 void swap(basic_buffer< Allocator >& x, basic_buffer< Allocator >& y)
584 } // namespace posixx
586 #endif // POSIXX_BASIC_BUFFER_HPP_