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 <cstdlib> // std::realloc()
8 #include <cstring> // std::memcpy(), memset(), memcmp()
9 #include <cassert> // assert()
10 #include <cstddef> // std::size_t, ptrdiff_t
17 * This is pretty much like using a plain C-style array using dynamic memory,
18 * with a STL-ish interface. Only POD should be stored, since no constructors
19 * or destructors are called.
21 * The buffer will use Allocator (which should be a function with realloc(3)
22 * semantics) for all storage management.
24 template< typename T, void* (*Allocator)(void*, size_t) = &std::realloc >
28 //////////////////////////////////////////////////////////////////////
34 typedef value_type& reference;
37 typedef const value_type& const_reference;
40 typedef value_type* iterator;
43 typedef const value_type* const_iterator;
46 typedef std::size_t size_type;
49 typedef std::ptrdiff_t difference_type;
52 typedef value_type* pointer;
55 typedef const pointer const_pointer;
58 typedef std::reverse_iterator< iterator > reverse_iterator;
61 typedef std::reverse_iterator< const_iterator > const_reverse_iterator;
64 // Construct/Copy/Destroy
65 //////////////////////////////////////////////////////////////////////
68 * Creates a buffer of length zero (the default constructor).
70 explicit basic_buffer(): _data(NULL), _size(0) {}
73 * Creates a buffer with a capacity() of n (uninitialized) elements.
75 * @throw std::bad_alloc
77 explicit basic_buffer(size_type n):
78 _data(_allocate(NULL, n * sizeof(value_type))), _size(n)
83 * Creates a buffer of length n, containing n copies of value.
85 * @throw std::bad_alloc
87 basic_buffer(size_type n, const_reference value):
91 std::memset(_data, value, n);
95 * Creates a copy of x.
97 * @throw std::bad_alloc
99 basic_buffer(const basic_buffer< T, Allocator >& x):
100 _data(_allocate(NULL, x.size() * sizeof(value_type))),
103 std::memcpy(_data, x.c_array(), x.size());
107 * Creates a buffer of length finish - start, filled with all values
108 * obtained by dereferencing the InputIterators on the range [start,
111 * @throw std::bad_alloc
113 template < typename InputIterator >
114 basic_buffer(InputIterator start, InputIterator finish):
115 _data(NULL), _size(0)
117 assign(start, finish);
121 * Erases all elements in self then inserts into self a copy of each
124 * Invalidates all references and iterators.
126 * @returns a reference to self.
128 * @throw std::bad_alloc
130 basic_buffer< T, Allocator >& operator = (
131 const basic_buffer< T, Allocator >& x)
135 std::memcpy(_data, x.c_array(), x.size());
141 * Frees the memory holded by the buffer
148 //////////////////////////////////////////////////////////////////////
151 * Returns a random access iterator that points to the first element.
157 * Returns a random access const_iterator that points to the first
160 const_iterator begin() const
164 * Returns a random access iterator that points to the past-the-end
168 { return _data ? _data + _size : NULL; }
171 * Returns a random access const_iterator that points to the
172 * past-the-end value.
174 const_iterator end() const
175 { return _data ? _data + _size : NULL; }
178 * Returns a random access reverse_iterator that points to the
179 * past-the-end value.
181 reverse_iterator rbegin()
182 { return reverse_iterator(end()); }
185 * Returns a random access const_reverse_iterator that points to the
186 * past-the-end value.
188 const_reverse_iterator rbegin() const
189 { return const_reverse_iterator(end()); }
192 * Returns a random access reverse_iterator that points to the first
195 reverse_iterator rend()
196 { return reverse_iterator(begin()); }
199 * Returns a random access const_reverse_iterator that points to the
202 const_reverse_iterator rend() const
203 { return const_reverse_iterator(begin()); }
207 //////////////////////////////////////////////////////////////////////
210 * Returns the number of elements.
212 size_type size() const
216 * Returns size() of the largest possible buffer.
218 size_type max_size() const
219 { return std::numeric_limits< size_type >::max(); }
222 * Alters the size of self.
224 * If the new size (sz) is greater than the current size, then
225 * sz-size() (uninitialized) elements are inserted at the end of the
226 * buffer. If the new size is smaller than the current capacity, then
227 * the buffer is truncated by erasing size()-sz elements off the end.
228 * If sz is equal to capacity then no action is taken.
230 * Invalidates all references and iterators.
232 * @throw std::bad_alloc
234 void resize(size_type sz)
236 pointer n_data = _allocate(_data, sz * sizeof(value_type));
242 * Alters the size of self.
244 * If the new size (sz) is greater than the current size, then
245 * sz-size() copies of value are inserted at the end of the buffer. If
246 * the new size is smaller than the current capacity, then the buffer
247 * is truncated by erasing size()-sz elements off the end. If sz is
248 * equal to capacity then no action is taken.
250 * Invalidates all references and iterators.
252 * @throw std::bad_alloc
254 void resize(size_type sz, value_type value)
256 size_type old_size = size();
259 std::memset(_data + old_size, value, sz - old_size);
265 size_type capacity() const
269 * Returns true if the size is zero.
275 * If sz > size() call resize(sz), otherwise do nothing.
277 * Invalidates all references and iterators in the first case.
279 * @throw std::bad_alloc
281 void reserve(size_type sz)
289 //////////////////////////////////////////////////////////////////////
292 * Returns a reference to element n of self.
294 * The result can be used as an lvalue. The index n must be between
295 * 0 and the size less one.
297 reference operator[](size_type n)
298 { assert(n < _size); return _data[n]; }
301 * Returns a constant reference to element n of self.
303 * The index n must be between 0 and the size less one.
305 const_reference operator[](size_type n) const
306 { assert(n < _size); return _data[n]; }
309 * Returns a reference to element n of self.
311 * The result can be used as an lvalue. If index n is not between
312 * 0 and the size less one, a std::out_of_range exception.
314 reference at(size_type n)
317 throw std::out_of_range("posixx::basic_buffer::at(n)");
322 * Returns a constant reference to element n of self.
324 * If index n is not between 0 and the size less one,
325 * a std::out_of_range exception.
327 const_reference at(size_type n) const
330 throw std::out_of_range("posixx::basic_buffer::at(n)");
335 * Returns a reference to the first element.
338 { assert(_size); return _data[0]; }
341 * Returns a constant reference to the first element.
343 const_reference front() const
344 { assert(_size); return _data[0]; }
347 * Returns a reference to the last element.
350 { assert(_size); return _data[_size - 1]; }
353 * Returns a constant reference to the last element.
355 const_reference back() const
356 { assert(_size); return _data[_size - 1]; }
359 * Returns a pointer to a C-style array.
365 * Returns a pointer to a read-only C-style array.
367 const_pointer c_array() const
372 //////////////////////////////////////////////////////////////////////
375 * Replaces elements with copies of those in the range [start, finish).
377 * The function invalidates all iterators and references to elements in
380 * Invalidates all references and iterators.
382 template < typename InputIterator >
383 void assign(InputIterator start, InputIterator finish)
385 // Check whether it's an integral type. If so, it's not an
387 typename std::tr1::is_integral< InputIterator >::type is_int;
388 _assign_dispatch(start, finish, is_int);
392 * Replaces elements in *this with n copies of value.
394 * The function invalidates all iterators and references to elements in
397 * Invalidates all references and iterators.
399 void assign(size_type n, const_reference value)
402 std::memset(_data, value, n);
406 * Efficiently swaps the contents of x and y.
408 * Invalidates all references and iterators.
410 void swap(basic_buffer< T, Allocator >& x)
412 pointer tmp_data = x._data;
413 size_type tmp_size = x._size;
421 * Deletes all elements from the buffer.
423 * Invalidates all references and iterators.
427 _data = _allocate(_data, 0);
433 // The underlying data
436 // Size in number of items
439 // Allocator wrapper for automatic casting
440 static pointer _allocate(void* ptr, std::size_t sz)
442 if (ptr == NULL && sz == 0)
444 void* new_ptr = Allocator(ptr, sz);
445 if (sz != 0 && new_ptr == NULL)
446 throw std::bad_alloc();
447 return static_cast< pointer >(new_ptr);
452 * Helper assign functions to disambiguate the Iterator based and the
453 * N-value copy assign() methods. The last arguments indicates if
454 * InputIterator is an integral type.
457 // This is the version for the integral types, we should use the
458 // "regular" N-value copy assign().
459 template < typename InputIterator >
460 void _assign_dispatch(InputIterator start, InputIterator finish,
463 assign(static_cast< size_type >(start),
464 static_cast< value_type >(finish));
467 // This is the version for the real iterators.
468 template < typename InputIterator >
469 void _assign_dispatch(InputIterator start, InputIterator finish,
470 std::tr1::false_type)
472 // TODO: provide an efficient version for random iterators
473 while (start != finish) {
475 (*this)[size() - 1] = *start;
482 // Nonmember Operators
483 //////////////////////////////////////////////////////////////////////
486 * Returns true if x hass the same contents as y.
488 template < typename T, void* (*Allocator)(void*, std::size_t) >
489 bool operator == (const basic_buffer< T, Allocator >& x,
490 const basic_buffer < T, Allocator >& y)
494 if (x.size() != y.size())
496 if (x.empty() && y.empty())
498 if (x.empty() || y.empty())
500 int r = std::memcmp(x.c_array(), y.c_array(),
501 (x.size() < y.size()) ? x.size() : y.size());
503 return x.size() == y.size();
510 template < typename T, void* (*Allocator)(void*, std::size_t) >
511 bool operator != (const basic_buffer< T, Allocator >& x,
512 const basic_buffer < T, Allocator >& y)
518 * Returns true if the elements contained in x are lexicographically less than
519 * the elements contained in y.
521 template < typename T, void* (*Allocator)(void*, std::size_t) >
522 bool operator < (const basic_buffer< T, Allocator >& x,
523 const basic_buffer< T, Allocator >& y)
527 if (x.empty() && y.empty())
533 int r = std::memcmp(x.c_array(), y.c_array(),
534 (x.size() < y.size()) ? x.size() : y.size());
536 return x.size() < y.size();
543 template < typename T, void* (*Allocator)(void*, std::size_t) >
544 bool operator > (const basic_buffer< T, Allocator >& x,
545 const basic_buffer< T, Allocator >& y)
553 template < typename T, void* (*Allocator)(void*, std::size_t) >
554 bool operator <= (const basic_buffer< T, Allocator >& x,
555 const basic_buffer< T, Allocator >& y)
563 template < typename T, void* (*Allocator)(void*, std::size_t) >
564 bool operator >= (const basic_buffer< T, Allocator >& x,
565 const basic_buffer< T, Allocator >& y)
571 // Specialized Algorithms
572 //////////////////////////////////////////////////////////////////////
575 * Exchanges self with x, by swapping all elements.
577 * Invalidates all references and iterators.
579 template < typename T, void* (*Allocator)(void*, std::size_t) >
580 void swap(basic_buffer< T, Allocator >& x, basic_buffer< T, Allocator >& y)
585 } // namespace posixx
587 #endif // POSIXX_BASIC_BUFFER_HPP_