TPIE

v1.1rc1-6-g0c97303
array.h
Go to the documentation of this file.
1 // -*- mode: c++; tab-width: 4; indent-tabs-mode: t; eval: (progn (c-set-style "stroustrup") (c-set-offset 'innamespace 0)); -*-
2 // vi:set ts=4 sts=4 sw=4 noet :
3 // Copyright 2010, 2012, The TPIE development team
4 //
5 // This file is part of TPIE.
6 //
7 // TPIE is free software: you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License as published by the
9 // Free Software Foundation, either version 3 of the License, or (at your
10 // option) any later version.
11 //
12 // TPIE is distributed in the hope that it will be useful, but WITHOUT ANY
13 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 // License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with TPIE. If not, see <http://www.gnu.org/licenses/>
19 #ifndef __TPIE_ARRAY_H__
20 #define __TPIE_ARRAY_H__
21 
26 #include <tpie/util.h>
27 #include <boost/type_traits/is_pod.hpp>
28 #include <boost/iterator/iterator_facade.hpp>
29 #include <boost/utility/enable_if.hpp>
30 #include <tpie/memory.h>
31 #include <tpie/array_view_base.h>
32 
33 namespace tpie {
34 
39 template <typename TT, bool forward>
40 class array_iter_base: public boost::iterator_facade<
41  array_iter_base<TT, forward>,
42  TT , boost::random_access_traversal_tag> {
43 private:
44  template <typename, typename> friend class array;
45  friend class boost::iterator_core_access;
46  template <typename, bool> friend class array_iter_base;
47 
48  struct enabler {};
49  explicit array_iter_base(TT * e): elm(e) {}
50 
51  inline TT & dereference() const {return * elm;}
52  template <class U>
53  inline bool equal(array_iter_base<U, forward> const& o) const {return elm == o.elm;}
54  inline void increment() {elm += forward?1:-1;}
55  inline void decrement() {elm += forward?-1:1;}
56  inline void advance(size_t n) {if (forward) elm += n; else elm -= n;}
57  inline ptrdiff_t distance_to(array_iter_base const & o) const {return o.elm - elm;}
58  TT * elm;
59 public:
63  array_iter_base(): elm(0) {};
64 
70  template <class U>
71  array_iter_base(array_iter_base<U, forward> const& o, typename boost::enable_if<
72  boost::is_convertible<U*,TT*>, enabler>::type = enabler())
73  : elm(o.elm) {}
74 };
75 
76 #pragma pack(push, 1)
77 template <typename C>
79  char c[sizeof(C)];
80 };
81 #pragma pack(pop)
82 
83 namespace bits {
84  template <typename T, typename Allocator> struct allocator_usage;
85 } // namespace bits
86 
96 // ASIDE about the C++ language: A type is said to be trivially constructible
97 // if it has no user-defined constructors and all its members are trivially
98 // constructible. `new T` will allocate memory for a T-element, and if T is
99 // trivially constructible, the memory will be left uninitialized. This
100 // goes for arrays (T[]) as well, meaning an array initialization of a
101 // trivially constructible type takes practically no time.
102 //
103 // IMPLEMENTATION NOTE. We have three cases for how we want the item buffer
104 // `array::m_elements` to be initialized:
105 //
106 // 1. Default constructed. tpie_new_array<T> does this
107 // allocation+initialization.
108 //
109 // 2. Copy constructed from a single element. Cannot be done with
110 // tpie_new_array<T>.
111 //
112 // 3. Copy constructed from elements in another array. Cannot be done with
113 // tpie_new_array<T> either.
114 //
115 // For cases 2 and 3, we must keep `new`-allocation separate from item
116 // initialization. Thus, for these cases we instead allocate an array of
117 // trivial_same_size<T>. This is a struct that has the same size as T, but is
118 // trivially constructible, meaning no memory initialization is done.
119 //
120 // Then, for case 2, we use std::uninitialized_fill, and for case 3 we use
121 // std::uninitialized_copy.
122 //
123 // Unfortunately, although we have the choice in case 1 of allocating a
124 // trivial_same_size<T> and then calling the default constructors afterwards,
125 // this turns out in some cases to be around 10% slower than allocating a
126 // T-array directly.
127 //
128 // Also, in order to have the same initialization semantics with regards to
129 // trivially constructible types as C++ new[], we need to check if the type is
130 // trivially constructible (using SFINAE/boost::enable_if/boost::type_traits)
131 // to avoid zero-initializing those (a speed penalty we cannot afford).
132 //
133 // Now it is clear that we must sometimes use tpie_new_array<T>, other times
134 // tpie_new_array<trivial_same_size<T> >. The TPIE memory manager checks that
135 // buffers allocated as one type are not deallocated as another type, so when
136 // the buffer is allocated as a trivial_same_size, we must remember this fact
137 // for later destruction and deallocation.
138 //
139 // We remember this fact in array::m_tss_used.
141 
142 template <typename T, typename Allocator = allocator<T> >
143 class array : public linear_memory_base<array<T> > {
144 public:
147 
150 
153 
156 
158  typedef T value_type;
159 
166  iterator find(size_t idx) throw () {
167  assert(idx <= size());
168  return get_iter(idx);
169  }
170 
177  const_iterator find(size_t idx) const throw () {
178  assert(idx <= size());
179  return get_iter(idx);
180  }
181 
187  T & at(size_t i) throw() {
188  assert(i < size());
189  return m_elements[i];
190  }
191 
195  const T & at(size_t i) const throw() {
196  assert(i < size());
197  return m_elements[i];
198  }
199 
208  template <typename OtherAllocator>
210  resize(other.size());
211  for (size_t i=0; i < size(); ++i) m_elements[i] = other[i];
212  return *this;
213  }
214 
220  inline bool empty() const {return size() == 0;}
221 
228  inline const T & operator[](size_t i) const {
229  assert(i < size());
230  return at(i);
231  }
232 
239  inline T & operator[](size_t i) {
240  assert(i < size());
241  return at(i);
242  }
243 
251  inline bool operator==(const array & other) const {
252  if (size() != other.size()) return false;
253  for (size_t i=0;i<size();++i) if (*get_iter(i) != *other.get_iter(i)) return false;
254  return true;
255  }
256 
263  inline bool operator!=(const array & other) const {
264  if (size() != other.size()) return true;
265  for (size_t i=0; i<size(); ++i) if (*get_iter(i) != *other.get_iter(i)) return true;
266  return false;
267  }
268 
274  inline iterator begin() {return get_iter(0);}
275 
281  inline const_iterator begin() const {return get_iter(0);}
282 
288  inline iterator end() {return get_iter(size());}
289 
295  inline const_iterator end() const {return get_iter(size());}
296 
300  inline const T & front() const {return at(0);}
301 
305  inline T & front() {return at(0);}
306 
310  inline const T & back() const {return at(size()-1);}
311 
315  inline T & back() {return at(size()-1);}
316 
320  inline reverse_iterator rbegin() {return get_rev_iter(0);}
321 
325  inline const_reverse_iterator rbegin() const {return get_rev_iter(0);}
326 
330  inline reverse_iterator rend() {return get_rev_iter(size());}
331 
335  inline const_reverse_iterator rend() const {return get_rev_iter(size());}
336 
337 private:
338  T * m_elements;
339  size_t m_size;
340 
341  inline iterator get_iter(size_t idx) {
342  return iterator(m_elements+idx);
343  }
344 
345  inline const_iterator get_iter(size_t idx) const {
346  return const_iterator(m_elements+idx);
347  }
348 
349  inline reverse_iterator get_rev_iter(size_t idx) {
350  return reverse_iterator(m_elements+m_size-idx-1);
351  }
352 
353  inline const_reverse_iterator get_rev_iter(size_t idx) const {
354  return const_reverse_iterator(m_elements+m_size-idx-1);
355  }
356 public:
360  static double memory_coefficient() {
361  return (double)sizeof(T);
362  }
363 
367  static double memory_overhead() {return sizeof(array);}
368 
375  array(size_type s, const T & value,
376  const Allocator & alloc=Allocator()
377  ): m_elements(0), m_size(0), m_tss_used(false), m_allocator(alloc)
378  {resize(s, value);}
379 
385  array(size_type s=0, const Allocator & alloc=
386  Allocator()): m_elements(0), m_size(0), m_tss_used(false),
387  m_allocator(alloc) {resize(s);}
388 
393  array(const array & other): m_elements(0), m_size(other.m_size), m_tss_used(false), m_allocator(other.m_allocator) {
394  if (other.size() == 0) return;
395  alloc_copy(other.m_elements);
396  }
397 
398  array(const array_view_base<T> & view)
399  : m_elements(0)
400  , m_size(view.size())
401  , m_tss_used(false)
402  {
403  if (view.size() == 0) return;
404  alloc_copy(&*view.begin());
405  }
406 
407  array(const array_view_base<const T> & view)
408  : m_elements(0)
409  , m_size(view.size())
410  , m_tss_used(false)
411  {
412  if (view.size() == 0) return;
413  alloc_copy(&*view.begin());
414  }
415 
419  ~array() {resize(0);}
420 
431  void resize(size_t size, const T & elm) {
432  if (size != m_size) {
433  destruct_and_dealloc();
434  m_size = size;
435 
436  alloc_fill(elm);
437  } else {
438  std::fill(m_elements+0, m_elements+m_size, elm);
439  }
440  }
441 
445  void swap(array & other) {
446  std::swap(m_allocator, other.m_allocator);
447  std::swap(m_elements, other.m_elements);
448  std::swap(m_size, other.m_size);
449  std::swap(m_tss_used, other.m_tss_used);
450  }
451 
461  void resize(size_t s) {
462  destruct_and_dealloc();
463  m_size = s;
464  alloc_dfl();
465  }
466 
472  inline size_type size() const {return m_size;}
473 
477  inline T * get() {return m_elements;}
478 
482  inline const T * get() const {return m_elements;}
483 
484 private:
485  friend struct bits::allocator_usage<T, Allocator>;
486 
494  inline void alloc_copy(const T * copy_from) { bits::allocator_usage<T, Allocator>::alloc_copy(*this, copy_from); }
495 
503  inline void alloc_fill(const T & elm) { bits::allocator_usage<T, Allocator>::alloc_fill(*this, elm); }
504 
510  inline void alloc_dfl() { bits::allocator_usage<T, Allocator>::alloc_dfl(*this); }
511 
517  inline void destruct_and_dealloc() { bits::allocator_usage<T, Allocator>::destruct_and_dealloc(*this); }
518 
521  bool m_tss_used;
522 
523  Allocator m_allocator;
524 };
525 
526 namespace bits {
527 
528 template <typename T>
529 struct allocator_usage<T, allocator<T> > {
530  static void alloc_copy(array<T, allocator<T> > & host, const T * copy_from) {
531  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
532  host.m_tss_used = true;
533  std::uninitialized_copy(copy_from+0, copy_from+host.m_size, host.m_elements+0);
534  }
535 
536  static void alloc_fill(array<T, allocator<T> > & host, const T & elm) {
537  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
538  host.m_tss_used = true;
539 
540  // call copy constructors manually
541  std::uninitialized_fill(host.m_elements+0, host.m_elements+host.m_size, elm);
542  }
543 
544  static void alloc_dfl(array<T, allocator<T> > & host) {
545  host.m_elements = host.m_size ? tpie_new_array<T>(host.m_size) : 0;
546  host.m_tss_used = false;
547  }
548 
549  static void destruct_and_dealloc(array<T, allocator<T> > & host) {
550  if (!host.m_tss_used) {
551  // calls destructors
552  tpie_delete_array(host.m_elements, host.m_size);
553  return;
554  }
555 
556  // call destructors manually
557  for (size_t i = 0; i < host.m_size; ++i) {
558  host.m_elements[i].~T();
559  }
560  tpie_delete_array(reinterpret_cast<trivial_same_size<T>*>(host.m_elements), host.m_size);
561  }
562 };
563 
564 template <typename T, typename Allocator>
565 struct allocator_usage {
566  static void alloc_copy(array<T, Allocator> & host, const T * copy_from) {
567  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
568  for (size_t i = 0; i < host.m_size; ++i) {
569  host.m_allocator.construct(host.m_elements+i, copy_from[i]);
570  }
571  }
572 
573  static void alloc_fill(array<T, Allocator> & host, const T & elm) {
574  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
575  for (size_t i = 0; i < host.m_size; ++i) {
576  host.m_allocator.construct(host.m_elements+i, elm);
577  }
578  }
579 
580  static void alloc_dfl(array<T, Allocator> & host) {
581  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
582  for (size_t i = 0; i < host.m_size; ++i) {
583  host.m_allocator.construct(host.m_elements+i);
584  }
585  }
586 
587  static void destruct_and_dealloc(array<T, Allocator> & host) {
588  for (size_t i = 0; i < host.m_size; ++i) {
589  host.m_allocator.destroy(host.m_elements+i);
590  }
591  host.m_allocator.deallocate(host.m_elements, host.m_size);
592  }
593 };
594 
595 } // namespace bits
596 
597 template <typename T>
598 std::ostream & operator<<(std::ostream & o, const array<T> & a) {
599  o << "[";
600  bool first=true;
601  for(size_t i=0; i < a.size(); ++i) {
602  if (first) first = false;
603  else o << ", ";
604  o << a[i];
605  }
606  return o << "]";
607 }
608 
609 } // namespace tpie
610 
611 namespace std {
612 
631 
632 template <typename T>
633 void swap(tpie::array<T> & a, tpie::array<T> & b) {
634  a.swap(b);
635 }
636 
640 template <typename TT1, bool forward1, typename TT2, bool forward2>
645 
646  ptrdiff_t dist = copy(&*first, &*last, &*d_first) - &*d_first;
647  return d_first + dist;
648 }
649 
653 template <typename TT, bool forward, typename OutputIterator>
654 OutputIterator
657  OutputIterator d_first) {
658 
659  return copy(&*first, &*last, d_first);
660 }
661 
665 template <typename TT, bool forward, typename InputIterator>
667 copy(InputIterator first,
668  InputIterator last,
670 
671  ptrdiff_t dist = copy(first, last, &*d_first) - &*d_first;
672  return d_first + dist;
673 }
674 
675 } // namespace std
676 
677 #endif //__TPIE_ARRAY_H__
Base class for array_view.
const_iterator end() const
Return a const iterator to the end of the array.
Definition: array.h:295
array_iter_base< T const, false > const_reverse_iterator
Reverse iterator over a const array.
Definition: array.h:149
T & front()
Return the first element in the array.
Definition: array.h:305
array(size_type s=0, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:385
array(const array &other)
Construct a copy of another array.
Definition: array.h:393
iterator find(size_t idx)
Return an iterator to the i'th element of the array.
Definition: array.h:166
T & at(size_t i)
Return the element located at the given index.
Definition: array.h:187
Memory management subsystem.
T value_type
Type of values containd in the array.
Definition: array.h:158
Base class for array_view.
Base class of data structures with linear memory usage.
Definition: util.h:73
array_iter_base(array_iter_base< U, forward > const &o, typename boost::enable_if< boost::is_convertible< U *, TT * >, enabler >::type=enabler())
Copy constructor.
Definition: array.h:71
reverse_iterator rend()
Reverse iterator to end of reverse sequence.
Definition: array.h:330
A generic array with a fixed size.
Definition: array.h:143
const T & at(size_t i) const
Definition: array.h:195
const_reverse_iterator rbegin() const
Const reverse iterator to beginning of reverse sequence.
Definition: array.h:325
array_iter_base()
Default constructor.
Definition: array.h:63
const T & front() const
Return the first element in the array.
Definition: array.h:300
const_iterator begin() const
Return a const iterator to the beginning of the array.
Definition: array.h:281
void resize(size_t s)
Change the size of the array.
Definition: array.h:461
const_iterator find(size_t idx) const
Return a const iterator to the i'th element of the array.
Definition: array.h:177
Shared implementation of array iterators.
Definition: array.h:40
T & operator[](size_t i)
Return a reference to an array entry.
Definition: array.h:239
const T & back() const
Return the last element in the array.
Definition: array.h:310
array_iter_base< T, false > reverse_iterator
Reverse iterator over an array.
Definition: array.h:155
A allocator object usable in STL containers, using the TPIE memory manager.
Definition: memory.h:465
Miscellaneous utility functions.
static double memory_coefficient()
Return the memory coefficient of the structure.
Definition: array.h:360
array_iter_base< T const, true > const_iterator
Iterator over a const array.
Definition: array.h:146
reverse_iterator rbegin()
Reverse iterator to beginning of reverse sequence.
Definition: array.h:320
size_t size() const
Get number of elements in the array.
array & operator=(const array< T, OtherAllocator > &other)
Copy elements from another array into this.
Definition: array.h:209
void resize(size_t size, const T &elm)
Change the size of the array.
Definition: array.h:431
array_iter_base< T, true > iterator
Iterator over an array.
Definition: array.h:152
static double memory_overhead()
Return the memory overhead of the structure.
Definition: array.h:367
iterator begin() const
Return an iterator to the beginning of the array.
const_reverse_iterator rend() const
Const reverse iterator to end of reverse sequence.
Definition: array.h:335
const T & operator[](size_t i) const
Return a const reference to an array entry.
Definition: array.h:228
void swap(array &other)
Swap two arrays.
Definition: array.h:445
T & back()
Return the last element in the array.
Definition: array.h:315
iterator end()
Return an iterator to the end of the array.
Definition: array.h:288
bool operator==(const array &other) const
Compare if the other array has the same elements in the same order as this.
Definition: array.h:251
iterator begin()
Return an iterator to the beginning of the array.
Definition: array.h:274
size_type size() const
Return the size of the array.
Definition: array.h:472
bool empty() const
Check if the array is empty.
Definition: array.h:220
bool operator!=(const array &other) const
Check if two arrays differ.
Definition: array.h:263
array(size_type s, const T &value, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:375
~array()
Free up all memory used by the array.
Definition: array.h:419
void tpie_delete_array(T *a, size_t size)
Delete an array allocated with tpie_new_array.
Definition: memory.h:398