TPIE

2362a60
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/iterator/iterator_facade.hpp>
28 #include <tpie/memory.h>
29 #include <tpie/array_view_base.h>
30 #include <type_traits>
31 #include <cassert>
32 #include <ostream>
33 
34 namespace tpie {
35 
40 template <typename TT, bool forward>
41 class array_iter_base: public boost::iterator_facade<
42  array_iter_base<TT, forward>,
43  TT , boost::random_access_traversal_tag> {
44 private:
45  template <typename, typename> friend class array;
46  friend class boost::iterator_core_access;
47  template <typename, bool> friend class array_iter_base;
48 
49  explicit array_iter_base(TT * e): elm(e) {}
50 
51  TT & dereference() const {return * elm;}
52  template <class U>
53  bool equal(array_iter_base<U, forward> const& o) const {return elm == o.elm;}
54  void increment() {elm += forward?1:-1;}
55  void decrement() {elm += forward?-1:1;}
56  void advance(size_t n) {if (forward) elm += n; else elm -= n;}
57  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>
72  typename std::enable_if<
73  std::is_convertible<U*,TT*>::value>::type* = 0)
74  : elm(o.elm) {}
75 };
76 
77 #pragma pack(push, 1)
78 template <typename C>
80  char c[sizeof(C)];
81 };
82 #pragma pack(pop)
83 
84 namespace bits {
85  template <typename T, typename Allocator> struct allocator_usage;
86 } // namespace bits
87 
97 // ASIDE about the C++ language: A type is said to be trivially constructible
98 // if it has no user-defined constructors and all its members are trivially
99 // constructible. `new T` will allocate memory for a T-element, and if T is
100 // trivially constructible, the memory will be left uninitialized. This
101 // goes for arrays (T[]) as well, meaning an array initialization of a
102 // trivially constructible type takes practically no time.
103 //
104 // IMPLEMENTATION NOTE. We have three cases for how we want the item buffer
105 // `array::m_elements` to be initialized:
106 //
107 // 1. Default constructed. tpie_new_array<T> does this
108 // allocation+initialization.
109 //
110 // 2. Copy constructed from a single element. Cannot be done with
111 // tpie_new_array<T>.
112 //
113 // 3. Copy constructed from elements in another array. Cannot be done with
114 // tpie_new_array<T> either.
115 //
116 // For cases 2 and 3, we must keep `new`-allocation separate from item
117 // initialization. Thus, for these cases we instead allocate an array of
118 // trivial_same_size<T>. This is a struct that has the same size as T, but is
119 // trivially constructible, meaning no memory initialization is done.
120 //
121 // Then, for case 2, we use std::uninitialized_fill, and for case 3 we use
122 // std::uninitialized_copy.
123 //
124 // Unfortunately, although we have the choice in case 1 of allocating a
125 // trivial_same_size<T> and then calling the default constructors afterwards,
126 // this turns out in some cases to be around 10% slower than allocating a
127 // T-array directly.
128 //
129 // Also, in order to have the same initialization semantics with regards to
130 // trivially constructible types as C++ new[], we need to check if the type is
131 // trivially constructible (using SFINAE/boost::enable_if/boost::type_traits)
132 // to avoid zero-initializing those (a speed penalty we cannot afford).
133 //
134 // Now it is clear that we must sometimes use tpie_new_array<T>, other times
135 // tpie_new_array<trivial_same_size<T> >. The TPIE memory manager checks that
136 // buffers allocated as one type are not deallocated as another type, so when
137 // the buffer is allocated as a trivial_same_size, we must remember this fact
138 // for later destruction and deallocation.
139 //
140 // We remember this fact in array::m_tss_used.
142 
143 template <typename T, typename Allocator = allocator<T> >
144 class array : public linear_memory_base<array<T> > {
145 public:
148 
151 
154 
157 
159  typedef T value_type;
160 
167  iterator find(size_t idx) throw () {
168  assert(idx <= size());
169  return get_iter(idx);
170  }
171 
178  const_iterator find(size_t idx) const throw () {
179  assert(idx <= size());
180  return get_iter(idx);
181  }
182 
188  T & at(size_t i) throw() {
189  assert(i < size());
190  return m_elements[i];
191  }
192 
196  const T & at(size_t i) const throw() {
197  assert(i < size());
198  return m_elements[i];
199  }
200 
209  array & operator=(const array & other) {
210  resize(other.size());
211  for (size_t i=0; i < size(); ++i) m_elements[i] = other[i];
212  return *this;
213  }
214 
223  array & operator=(array && other) {
224  resize(0);
225  std::swap(m_allocator, other.m_allocator);
226  std::swap(m_elements, other.m_elements);
227  std::swap(m_size, other.m_size);
228  std::swap(m_tss_used, other.m_tss_used);
229  return *this;
230  }
231 
241  template <typename OtherAllocator>
243  resize(other.size());
244  for (size_t i=0; i < size(); ++i) m_elements[i] = other[i];
245  return *this;
246  }
247 
253  bool empty() const {return size() == 0;}
254 
261  const T & operator[](size_t i) const {
262  assert(i < size());
263  return at(i);
264  }
265 
272  T & operator[](size_t i) {
273  assert(i < size());
274  return at(i);
275  }
276 
284  bool operator==(const array & other) const {
285  if (size() != other.size()) return false;
286  for (size_t i=0;i<size();++i) if (*get_iter(i) != *other.get_iter(i)) return false;
287  return true;
288  }
289 
296  bool operator!=(const array & other) const {
297  if (size() != other.size()) return true;
298  for (size_t i=0; i<size(); ++i) if (*get_iter(i) != *other.get_iter(i)) return true;
299  return false;
300  }
301 
307  iterator begin() {return get_iter(0);}
308 
314  const_iterator begin() const {return get_iter(0);}
315 
321  iterator end() {return get_iter(size());}
322 
328  const_iterator end() const {return get_iter(size());}
329 
333  const T & front() const {return at(0);}
334 
338  T & front() {return at(0);}
339 
343  const T & back() const {return at(size()-1);}
344 
348  T & back() {return at(size()-1);}
349 
353  reverse_iterator rbegin() {return get_rev_iter(0);}
354 
358  const_reverse_iterator rbegin() const {return get_rev_iter(0);}
359 
363  reverse_iterator rend() {return get_rev_iter(size());}
364 
368  const_reverse_iterator rend() const {return get_rev_iter(size());}
369 
370 private:
371  T * m_elements;
372  size_t m_size;
373 
374  iterator get_iter(size_t idx) {
375  return iterator(m_elements+idx);
376  }
377 
378  const_iterator get_iter(size_t idx) const {
379  return const_iterator(m_elements+idx);
380  }
381 
382  reverse_iterator get_rev_iter(size_t idx) {
383  return reverse_iterator(m_elements+m_size-idx-1);
384  }
385 
386  const_reverse_iterator get_rev_iter(size_t idx) const {
387  return const_reverse_iterator(m_elements+m_size-idx-1);
388  }
389 public:
393  static double memory_coefficient() {
394  return (double)sizeof(T);
395  }
396 
400  static double memory_overhead() {return sizeof(array);}
401 
408  array(size_type s, const T & value,
409  const Allocator & alloc=Allocator()
410  ): m_elements(0), m_size(0), m_tss_used(false), m_allocator(alloc)
411  {resize(s, value);}
412 
413  array(size_type s, memory_bucket_ref bucket):
414  m_elements(0), m_size(0), m_tss_used(false), m_allocator(bucket)
415  {resize(s);}
416 
417  array(memory_bucket_ref bucket):
418  m_elements(0), m_size(0), m_tss_used(false), m_allocator(bucket) {}
419 
425  array(size_type s=0, const Allocator & alloc=
426  Allocator()): m_elements(0), m_size(0), m_tss_used(false),
427  m_allocator(alloc) {resize(s);}
428 
433  array(const array & other): m_elements(0), m_size(other.m_size), m_tss_used(false), m_allocator(other.m_allocator) {
434  if (other.size() == 0) return;
435  alloc_copy(other.m_elements);
436  }
437 
442  array(array && other)
443  : m_elements(other.m_elements)
444  , m_size(other.m_size)
445  , m_tss_used(other.m_tss_used)
446  , m_allocator(other.m_allocator) {
447  other.m_elements = nullptr;
448  other.m_size = 0;
449  other.m_tss_used = false;
450  }
451 
452  array(const array_view_base<T> & view)
453  : m_elements(0)
454  , m_size(view.size())
455  , m_tss_used(false)
456  {
457  if (view.size() == 0) return;
458  alloc_copy(&*view.begin());
459  }
460 
461  array(const array_view_base<const T> & view)
462  : m_elements(0)
463  , m_size(view.size())
464  , m_tss_used(false)
465  {
466  if (view.size() == 0) return;
467  alloc_copy(&*view.begin());
468  }
469 
473  ~array() {resize(0);}
474 
485  void resize(size_t size, const T & elm) {
486  if (size != m_size) {
487  destruct_and_dealloc();
488  m_size = size;
489 
490  alloc_fill(elm);
491  } else {
492  std::fill(m_elements+0, m_elements+m_size, elm);
493  }
494  }
495 
499  void swap(array & other) {
500  std::swap(m_allocator, other.m_allocator);
501  std::swap(m_elements, other.m_elements);
502  std::swap(m_size, other.m_size);
503  std::swap(m_tss_used, other.m_tss_used);
504  }
505 
515  void resize(size_t s) {
516  destruct_and_dealloc();
517  m_size = s;
518  alloc_dfl();
519  }
520 
526  size_type size() const {return m_size;}
527 
531  T * get() {return m_elements;}
532 
536  const T * get() const {return m_elements;}
537 
541  Allocator get_allocator() const {return m_allocator;}
542 private:
543  friend struct bits::allocator_usage<T, Allocator>;
544 
552  void alloc_copy(const T * copy_from) { bits::allocator_usage<T, Allocator>::alloc_copy(*this, copy_from); }
553 
561  void alloc_fill(const T & elm) { bits::allocator_usage<T, Allocator>::alloc_fill(*this, elm); }
562 
568  void alloc_dfl() { bits::allocator_usage<T, Allocator>::alloc_dfl(*this); }
569 
575  void destruct_and_dealloc() { bits::allocator_usage<T, Allocator>::destruct_and_dealloc(*this); }
576 
579  bool m_tss_used;
580 
581  Allocator m_allocator;
582 };
583 
584 namespace bits {
585 
586 template <typename T>
587 struct allocator_usage<T, allocator<T> > {
588  static void alloc_copy(array<T, allocator<T> > & host, const T * copy_from) {
589  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
590  host.m_tss_used = true;
591 
592  if (host.m_allocator.bucket)
593  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
594 
595  std::uninitialized_copy(copy_from+0, copy_from+host.m_size, host.m_elements+0);
596  }
597 
598  static void alloc_fill(array<T, allocator<T> > & host, const T & elm) {
599  host.m_elements = host.m_size ? reinterpret_cast<T*>(tpie_new_array<trivial_same_size<T> >(host.m_size)) : 0;
600  host.m_tss_used = true;
601 
602  if (host.m_allocator.bucket)
603  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
604 
605  // call copy constructors manually
606  std::uninitialized_fill(host.m_elements+0, host.m_elements+host.m_size, elm);
607  }
608 
609  static void alloc_dfl(array<T, allocator<T> > & host) {
610  host.m_elements = host.m_size ? tpie_new_array<T>(host.m_size) : 0;
611  host.m_tss_used = false;
612 
613  if (host.m_allocator.bucket)
614  host.m_allocator.bucket->count += sizeof(T) * host.m_size;
615  }
616 
617  static void destruct_and_dealloc(array<T, allocator<T> > & host) {
618  if (host.m_allocator.bucket)
619  host.m_allocator.bucket->count -= sizeof(T) * host.m_size;
620 
621  if (!host.m_tss_used) {
622  // calls destructors
623  tpie_delete_array(host.m_elements, host.m_size);
624  return;
625  }
626 
627  // call destructors manually
628  for (size_t i = 0; i < host.m_size; ++i) {
629  host.m_elements[i].~T();
630  }
631  tpie_delete_array(reinterpret_cast<trivial_same_size<T>*>(host.m_elements), host.m_size);
632  }
633 };
634 
635 template <typename T, typename Allocator>
636 struct allocator_usage {
637  static void alloc_copy(array<T, Allocator> & host, const T * copy_from) {
638  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
639  for (size_t i = 0; i < host.m_size; ++i) {
640  host.m_allocator.construct(host.m_elements+i, copy_from[i]);
641  }
642  }
643 
644  static void alloc_fill(array<T, Allocator> & host, const T & elm) {
645  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
646  for (size_t i = 0; i < host.m_size; ++i) {
647  host.m_allocator.construct(host.m_elements+i, elm);
648  }
649  }
650 
651  static void alloc_dfl(array<T, Allocator> & host) {
652  host.m_elements = host.m_size ? host.m_allocator.allocate(host.m_size) : 0;
653  for (size_t i = 0; i < host.m_size; ++i) {
654  host.m_allocator.construct(host.m_elements+i);
655  }
656  }
657 
658  static void destruct_and_dealloc(array<T, Allocator> & host) {
659  for (size_t i = 0; i < host.m_size; ++i) {
660  host.m_allocator.destroy(host.m_elements+i);
661  }
662  host.m_allocator.deallocate(host.m_elements, host.m_size);
663  }
664 };
665 
666 } // namespace bits
667 
668 template <typename T>
669 std::ostream & operator<<(std::ostream & o, const array<T> & a) {
670  o << "[";
671  bool first=true;
672  for(size_t i=0; i < a.size(); ++i) {
673  if (first) first = false;
674  else o << ", ";
675  o << a[i];
676  }
677  return o << "]";
678 }
679 
680 } // namespace tpie
681 
682 namespace std {
683 
702 
703 template <typename T>
704 void swap(tpie::array<T> & a, tpie::array<T> & b) {
705  a.swap(b);
706 }
707 
711 template <typename TT1, bool forward1, typename TT2, bool forward2>
716 
717  ptrdiff_t dist = copy(&*first, &*last, &*d_first) - &*d_first;
718  return d_first + dist;
719 }
720 
724 template <typename TT, bool forward, typename OutputIterator>
725 OutputIterator
728  OutputIterator d_first) {
729 
730  return copy(&*first, &*last, d_first);
731 }
732 
736 template <typename TT, bool forward, typename InputIterator>
738 copy(InputIterator first,
739  InputIterator last,
741 
742  ptrdiff_t dist = copy(first, last, &*d_first) - &*d_first;
743  return d_first + dist;
744 }
745 
746 } // namespace std
747 
748 #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:328
array_iter_base< T const, false > const_reverse_iterator
Reverse iterator over a const array.
Definition: array.h:150
T & front()
Return the first element in the array.
Definition: array.h:338
array(size_type s=0, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:425
array(const array &other)
Construct a copy of another array.
Definition: array.h:433
iterator find(size_t idx)
Return an iterator to the i'th element of the array.
Definition: array.h:167
array & operator=(const array &other)
Copy elements from another array into this.
Definition: array.h:209
T & at(size_t i)
Return the element located at the given index.
Definition: array.h:188
Memory management subsystem.
T value_type
Type of values containd in the array.
Definition: array.h:159
Base class for array_view.
Base class of data structures with linear memory usage.
Definition: util.h:73
reverse_iterator rend()
Reverse iterator to end of reverse sequence.
Definition: array.h:363
A generic array with a fixed size.
Definition: array.h:144
const T & at(size_t i) const
Definition: array.h:196
const_reverse_iterator rbegin() const
Const reverse iterator to beginning of reverse sequence.
Definition: array.h:358
array_iter_base()
Default constructor.
Definition: array.h:63
const T & front() const
Return the first element in the array.
Definition: array.h:333
const_iterator begin() const
Return a const iterator to the beginning of the array.
Definition: array.h:314
void resize(size_t s)
Change the size of the array.
Definition: array.h:515
Allocator get_allocator() const
Return copy of the allocator.
Definition: array.h:541
const_iterator find(size_t idx) const
Return a const iterator to the i'th element of the array.
Definition: array.h:178
Shared implementation of array iterators.
Definition: array.h:41
array_iter_base(array_iter_base< U, forward > const &o, typename std::enable_if< std::is_convertible< U *, TT * >::value >::type *=0)
Copy constructor.
Definition: array.h:71
array & operator=(array &&other)
Move elements from another array into this.
Definition: array.h:223
T & operator[](size_t i)
Return a reference to an array entry.
Definition: array.h:272
const T & back() const
Return the last element in the array.
Definition: array.h:343
array_iter_base< T, false > reverse_iterator
Reverse iterator over an array.
Definition: array.h:156
A allocator object usable in STL containers, using the TPIE memory manager.
Definition: memory.h:390
Class storring a reference to a memory bucket.
Definition: memory.h:366
array(array &&other)
Move construct from another array.
Definition: array.h:442
Miscellaneous utility functions.
static double memory_coefficient()
Return the memory coefficient of the structure.
Definition: array.h:393
array_iter_base< T const, true > const_iterator
Iterator over a const array.
Definition: array.h:147
reverse_iterator rbegin()
Reverse iterator to beginning of reverse sequence.
Definition: array.h:353
size_t size() const
Get number of elements in the array.
array & operator=(const array< T, OtherAllocator > &other)
Copy elements from another array with any allocator into this.
Definition: array.h:242
void resize(size_t size, const T &elm)
Change the size of the array.
Definition: array.h:485
array_iter_base< T, true > iterator
Iterator over an array.
Definition: array.h:153
static double memory_overhead()
Return the memory overhead of the structure.
Definition: array.h:400
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:368
const T & operator[](size_t i) const
Return a const reference to an array entry.
Definition: array.h:261
void swap(array &other)
Swap two arrays.
Definition: array.h:499
T & back()
Return the last element in the array.
Definition: array.h:348
iterator end()
Return an iterator to the end of the array.
Definition: array.h:321
bool operator==(const array &other) const
Compare if the other array has the same elements in the same order as this.
Definition: array.h:284
iterator begin()
Return an iterator to the beginning of the array.
Definition: array.h:307
size_type size() const
Return the size of the array.
Definition: array.h:526
bool empty() const
Check if the array is empty.
Definition: array.h:253
bool operator!=(const array &other) const
Check if two arrays differ.
Definition: array.h:296
array(size_type s, const T &value, const Allocator &alloc=Allocator())
Construct array of given size.
Definition: array.h:408
~array()
Free up all memory used by the array.
Definition: array.h:473
void tpie_delete_array(T *a, size_t size)
Delete an array allocated with tpie_new_array.
Definition: memory.h:319