lean cpp library
A lean C++ library providing efficient utility classes for high-performance C++ applications.
|
00001 /*****************************************************/ 00002 /* lean Containers (c) Tobias Zirr 2011 */ 00003 /*****************************************************/ 00004 00005 #ifndef LEAN_CONTAINERS_SIMPLE_VECTOR 00006 #define LEAN_CONTAINERS_SIMPLE_VECTOR 00007 00008 #include "../lean.h" 00009 #include "../meta/type_traits.h" 00010 #include <memory> 00011 #include <stdexcept> 00012 00013 namespace lean 00014 { 00015 namespace containers 00016 { 00017 00019 namespace simple_vector_policies 00020 { 00022 template <bool RawMove = false, bool NoDestruct = false, bool NoConstruct = false> 00023 struct policy 00024 { 00026 static const bool raw_move = RawMove; 00028 static const bool no_destruct = NoDestruct; 00030 static const bool no_construct = NoConstruct; 00031 }; 00032 00034 typedef policy<> nonpod; 00036 typedef policy<true> semipod; 00038 typedef policy<true, true> inipod; 00040 typedef policy<true, true, true> pod; 00041 } 00042 00044 template < class Element, class Policy = simple_vector_policies::nonpod, class Allocator = std::allocator<Element> > 00045 class simple_vector 00046 { 00047 private: 00048 typedef typename Allocator::template rebind<Element>::other allocator_type_; 00049 allocator_type_ m_allocator; 00050 00051 Element *m_elements; 00052 Element *m_elementsEnd; 00053 Element *m_capacityEnd; 00054 00055 typedef typename allocator_type_::size_type size_type_; 00056 static const size_type_ s_maxSize = static_cast<size_type_>(-1); // WORKAROUND: premature evaluation / sizeof(Element); 00057 static const size_type_ s_minSize = (16 < s_maxSize) ? 16 : s_maxSize; 00058 00059 // Make sure size_type is unsigned 00060 LEAN_STATIC_ASSERT(is_unsigned<size_type_>::value); 00061 00063 LEAN_INLINE void default_construct(Element *dest) 00064 { 00065 if (!Policy::no_construct) 00066 m_allocator.construct(dest, Element()); 00067 } 00069 void default_construct(Element *dest, Element *destEnd) 00070 { 00071 if (!Policy::no_construct) 00072 { 00073 Element *destr = dest; 00074 00075 try 00076 { 00077 for (; dest != destEnd; ++dest) 00078 default_construct(dest); 00079 } 00080 catch(...) 00081 { 00082 destruct(destr, dest); 00083 throw; 00084 } 00085 } 00086 } 00088 LEAN_INLINE void copy_construct(Element *dest, const Element &source) 00089 { 00090 m_allocator.construct(dest, source); 00091 } 00093 template <class Iterator> 00094 void copy_construct(Iterator source, Iterator sourceEnd, Element *dest) 00095 { 00096 Element *destr = dest; 00097 00098 try 00099 { 00100 for (; source != sourceEnd; ++dest, ++source) 00101 copy_construct(dest, *source); 00102 } 00103 catch(...) 00104 { 00105 destruct(destr, dest); 00106 throw; 00107 } 00108 } 00110 LEAN_INLINE void move_construct(Element *dest, Element &source) 00111 { 00112 #ifndef LEAN0X_NO_RVALUE_REFERENCES 00113 m_allocator.construct(dest, std::move(source)); 00114 #else 00115 copy_construct(dest, source); 00116 #endif 00117 } 00119 void move_construct(Element *source, Element *sourceEnd, Element *dest) 00120 { 00121 Element *destr = dest; 00122 00123 try 00124 { 00125 for (; source != sourceEnd; ++dest, ++source) 00126 move_construct(dest, *source); 00127 } 00128 catch(...) 00129 { 00130 destruct(destr, dest); 00131 throw; 00132 } 00133 } 00135 LEAN_INLINE void move(Element *dest, Element &source) 00136 { 00137 #ifndef LEAN0X_NO_RVALUE_REFERENCES 00138 *dest = std::move(source); 00139 #else 00140 *dest = source; 00141 #endif 00142 } 00144 void move(Element *source, Element *sourceEnd, Element *dest) 00145 { 00146 for (; source != sourceEnd; ++dest, ++source) 00147 move(dest, *source); 00148 } 00149 00151 LEAN_INLINE void destruct(Element *destr) 00152 { 00153 if (!Policy::no_destruct) 00154 m_allocator.destroy(destr); 00155 } 00157 void destruct(Element *destr, Element *destrEnd) 00158 { 00159 if (!Policy::no_destruct) 00160 for (; destr != destrEnd; ++destr) 00161 destruct(destr); 00162 } 00163 00165 void reallocate(size_type_ newCapacity) 00166 { 00167 Element *newElements = m_allocator.allocate(newCapacity); 00168 00169 if (!empty()) 00170 try 00171 { 00172 if (Policy::raw_move) 00173 memcpy(newElements, m_elements, size() * sizeof(Element)); 00174 else 00175 move_construct(m_elements, m_elementsEnd, newElements); 00176 } 00177 catch(...) 00178 { 00179 m_allocator.deallocate(newElements, newCapacity); 00180 throw; 00181 } 00182 00183 Element *oldElements = m_elements; 00184 Element *oldElementsEnd = m_elementsEnd; 00185 size_type_ oldCapacity = capacity(); 00186 00187 // Mind the order, size() based on member variables! 00188 m_elementsEnd = newElements + size(); 00189 m_capacityEnd = newElements + newCapacity; 00190 m_elements = newElements; 00191 00192 if (oldElements) 00193 { 00194 // Do nothing on exception, resources leaking anyways! 00195 if (!Policy::raw_move) 00196 destruct(oldElements, oldElementsEnd); 00197 m_allocator.deallocate(oldElements, oldCapacity); 00198 } 00199 } 00200 00202 LEAN_INLINE void free() 00203 { 00204 if (m_elements) 00205 { 00206 // Do nothing on exception, resources leaking anyways! 00207 destruct(m_elements, m_elementsEnd); 00208 m_allocator.deallocate(m_elements, capacity()); 00209 } 00210 } 00211 00213 LEAN_INLINE void growTo(size_type_ newCount, bool checkLength = true) 00214 { 00215 // Mind overflow 00216 if (checkLength) 00217 check_length(newCount); 00218 00219 reallocate(next_capacity_hint(newCount)); 00220 } 00222 LEAN_INLINE void grow(size_type_ count) 00223 { 00224 size_type_ oldSize = size(); 00225 00226 // Mind overflow 00227 if (count > s_maxSize || s_maxSize - count < oldSize) 00228 length_exceeded(); 00229 00230 growTo(oldSize + count, false); 00231 } 00233 LEAN_INLINE Element& grow_and_relocate(Element &value) 00234 { 00235 size_type_ index = lean::addressof(value) - m_elements; 00236 grow(1); 00237 00238 // Index is unsigned, make use of wrap-around 00239 return (index < size()) 00240 ? m_elements[index] 00241 : value; 00242 } 00243 00245 LEAN_NOINLINE void growToHL(size_type_ newCount) 00246 { 00247 growTo(newCount); 00248 } 00250 LEAN_NOINLINE void growHL(size_type_ count) 00251 { 00252 grow(count); 00253 } 00255 LEAN_NOINLINE void grow_and_pushHL(const Element &value) 00256 { 00257 copy_construct(m_elementsEnd, grow_and_relocate(const_cast<Element&>(value))); 00258 ++m_elementsEnd; 00259 } 00261 LEAN_NOINLINE void grow_and_pushHL(Element &&value) 00262 { 00263 move_construct(m_elementsEnd, grow_and_relocate(value)); 00264 ++m_elementsEnd; 00265 } 00266 00268 LEAN_NOINLINE static void out_of_range() 00269 { 00270 throw std::out_of_range("simple_vector<T> out of range"); 00271 } 00273 LEAN_INLINE void check_pos(size_type_ pos) const 00274 { 00275 if (pos >= size()) 00276 out_of_range(); 00277 } 00279 LEAN_NOINLINE static void length_exceeded() 00280 { 00281 throw std::length_error("simple_vector<T> too long"); 00282 } 00284 LEAN_INLINE static void check_length(size_type_ count) 00285 { 00286 if (count > s_maxSize) 00287 length_exceeded(); 00288 } 00289 00290 public: 00292 typedef Policy construction_policy; 00293 00295 typedef allocator_type_ allocator_type; 00297 typedef size_type_ size_type; 00299 typedef typename allocator_type::difference_type difference_type; 00300 00302 typedef typename allocator_type::pointer pointer; 00304 typedef typename allocator_type::const_pointer const_pointer; 00306 typedef typename allocator_type::reference reference; 00308 typedef typename allocator_type::const_reference const_reference; 00310 typedef typename allocator_type::value_type value_type; 00311 00313 typedef pointer iterator; 00315 typedef const_pointer const_iterator; 00316 00318 simple_vector() 00319 : m_elements(nullptr), 00320 m_elementsEnd(nullptr), 00321 m_capacityEnd(nullptr) { } 00323 explicit simple_vector(allocator_type allocator) 00324 : m_allocator(allocator), 00325 m_elements(nullptr), 00326 m_elementsEnd(nullptr), 00327 m_capacityEnd(nullptr) { } 00329 simple_vector(const simple_vector &right) 00330 : m_allocator(right.m_allocator), 00331 m_elements(nullptr), 00332 m_elementsEnd(nullptr), 00333 m_capacityEnd(nullptr) 00334 { 00335 assign_disj(right.begin(), right.end()); 00336 } 00337 #ifndef LEAN0X_NO_RVALUE_REFERENCES 00338 00339 simple_vector(simple_vector &&right) 00340 : m_allocator(std::move(right.m_allocator)), 00341 m_elements(std::move(right.m_elements)), 00342 m_elementsEnd(std::move(right.m_elementsEnd)), 00343 m_capacityEnd(std::move(right.m_capacityEnd)) 00344 { 00345 right.m_elements = nullptr; 00346 right.m_elementsEnd = nullptr; 00347 right.m_capacityEnd = nullptr; 00348 } 00349 #endif 00350 00351 ~simple_vector() 00352 { 00353 free(); 00354 } 00355 00357 simple_vector& operator =(const simple_vector &right) 00358 { 00359 if (&right != this) 00360 assign_disj(right.begin(), right.end()); 00361 return *this; 00362 } 00363 #ifndef LEAN0X_NO_RVALUE_REFERENCES 00364 00365 simple_vector& operator =(simple_vector &&right) 00366 { 00367 if (&right != this) 00368 { 00369 free(); 00370 00371 m_elements = std::move(right.m_elements); 00372 m_elementsEnd = std::move(right.m_elementsEnd); 00373 m_capacityEnd = std::move(right.m_capacityEnd); 00374 00375 right.m_elements = nullptr; 00376 right.m_elementsEnd = nullptr; 00377 right.m_capacityEnd = nullptr; 00378 00379 m_allocator = std::move(right.m_allocator); 00380 } 00381 return *this; 00382 } 00383 #endif 00384 00386 template <class Iterator> 00387 void assign(Iterator source, Iterator sourceEnd) 00388 { 00389 LEAN_ASSERT(source <= sourceEnd); 00390 00391 Element *sourceElements = const_cast<Element*>( lean::addressof(*source) ); 00392 00393 // Index is unsigned, make use of wrap-around 00394 if (static_cast<size_type>(sourceElements - m_elements) < size()) 00395 { 00396 Element *sourceElementsEnd = const_cast<Element*>( lean::addressof(*sourceEnd) ); 00397 LEAN_ASSERT(sourceElementsEnd <= m_elementsEnd); 00398 00399 // Move (always back to front) 00400 LEAN_ASSERT(m_elements <= sourceElements); 00401 move(sourceElements, sourceElementsEnd, m_elements); 00402 00403 Element *oldElementsEnd = m_elementsEnd; 00404 m_elementsEnd = m_elements + (sourceElementsEnd - sourceElements); 00405 destruct(m_elementsEnd, oldElementsEnd); 00406 } 00407 else 00408 assign_disj(source, sourceEnd); 00409 } 00411 template <class Iterator> 00412 void assign_disj(Iterator source, Iterator sourceEnd) 00413 { 00414 // Clear before reallocation to prevent full-range moves 00415 clear(); 00416 00417 size_type count = sourceEnd - source; 00418 00419 if (count > capacity()) 00420 growToHL(count); 00421 00422 copy_construct(source, sourceEnd, m_elements); 00423 m_elementsEnd = m_elements + count; 00424 } 00425 00427 LEAN_INLINE reference push_back() 00428 { 00429 if (m_elementsEnd == m_capacityEnd) 00430 growHL(1); 00431 00432 default_construct(m_elementsEnd); 00433 return *(m_elementsEnd++); 00434 } 00436 LEAN_INLINE void push_back(const value_type &value) 00437 { 00438 if (m_elementsEnd == m_capacityEnd) 00439 grow_and_pushHL(value); 00440 else 00441 { 00442 copy_construct(m_elementsEnd, value); 00443 ++m_elementsEnd; 00444 } 00445 } 00446 #ifndef LEAN0X_NO_RVALUE_REFERENCES 00447 00448 LEAN_INLINE void push_back(value_type &&value) 00449 { 00450 if (m_elementsEnd == m_capacityEnd) 00451 grow_and_pushHL(std::move(value)); 00452 else 00453 { 00454 move_construct(m_elementsEnd, value); 00455 ++m_elementsEnd; 00456 } 00457 } 00458 #endif 00459 00460 LEAN_INLINE void pop_back() 00461 { 00462 LEAN_ASSERT(!empty()); 00463 00464 destruct(m_elementsEnd--); 00465 } 00466 00468 LEAN_INLINE void clear() 00469 { 00470 Element *oldElementsEnd = m_elementsEnd; 00471 m_elementsEnd = m_elements; 00472 destruct(m_elements, oldElementsEnd); 00473 } 00474 00476 LEAN_INLINE void reserve(size_type newCapacity) 00477 { 00478 // Mind overflow 00479 check_length(newCapacity); 00480 00481 if (newCapacity > capacity()) 00482 reallocate(newCapacity); 00483 } 00485 void resize(size_type newCount) 00486 { 00487 // Mind overflow 00488 check_length(newCount); 00489 00490 if (newCount > size()) 00491 { 00492 if (newCount > capacity()) 00493 growToHL(newCount); 00494 00495 Element *newElementsEnd = m_elements + newCount; 00496 default_construct(m_elementsEnd, newElementsEnd); 00497 m_elementsEnd = newElementsEnd; 00498 } 00499 else 00500 { 00501 Element *oldElementsEnd = m_elementsEnd; 00502 m_elementsEnd = m_elements + newCount; 00503 destruct(m_elementsEnd, oldElementsEnd); 00504 } 00505 } 00506 00508 LEAN_INLINE reference at(size_type pos) { check_pos(pos); return m_elements[pos]; }; 00510 LEAN_INLINE const_reference at(size_type pos) const { check_pos(pos); return m_elements[pos]; }; 00512 LEAN_INLINE reference front(void) { LEAN_ASSERT(!empty()); return *m_elements; }; 00514 LEAN_INLINE const_reference front(void) const { LEAN_ASSERT(!empty()); return *m_elements; }; 00516 LEAN_INLINE reference back(void) { LEAN_ASSERT(!empty()); return m_elementsEnd[-1]; }; 00518 LEAN_INLINE const_reference back(void) const { LEAN_ASSERT(!empty()); return m_elementsEnd[-1]; }; 00519 00521 LEAN_INLINE reference operator [](size_type pos) { return m_elements[pos]; }; 00523 LEAN_INLINE const_reference operator [](size_type pos) const { return m_elements[pos]; }; 00524 00526 LEAN_INLINE iterator begin(void) { return m_elements; }; 00528 LEAN_INLINE const_iterator begin(void) const { return m_elements; }; 00530 LEAN_INLINE iterator end(void) { return m_elementsEnd; }; 00532 LEAN_INLINE const_iterator end(void) const { return m_elementsEnd; }; 00533 00535 LEAN_INLINE allocator_type get_allocator() const { return m_allocator; }; 00536 00538 LEAN_INLINE bool empty(void) const { return (m_elements == m_elementsEnd); }; 00540 LEAN_INLINE size_type size(void) const { return m_elementsEnd - m_elements; }; 00542 LEAN_INLINE size_type capacity(void) const { return m_capacityEnd - m_elements; }; 00543 00545 size_type next_capacity_hint(size_type count) const 00546 { 00547 size_type capacity = this->capacity(); 00548 LEAN_ASSERT(capacity <= s_maxSize); 00549 size_type capacityDelta = capacity / 2; 00550 00551 // Try to increase capacity by 1.5 (mind overflow) 00552 capacity = (s_maxSize - capacityDelta < capacity) 00553 ? s_maxSize 00554 : capacity + capacityDelta; 00555 00556 if (capacity < count) 00557 capacity = count; 00558 00559 if (capacity < s_minSize) 00560 capacity = s_minSize; 00561 00562 return capacity; 00563 } 00564 00566 LEAN_INLINE size_type max_size() const 00567 { 00568 return s_maxSize; 00569 } 00570 00572 LEAN_INLINE void swap(simple_vector &right) throw() 00573 { 00574 using std::swap; 00575 00576 swap(m_allocator, right.m_allocator); 00577 swap(m_elements, right.m_elements); 00578 swap(m_elementsEnd, right.m_elementsEnd); 00579 swap(m_capacityEnd, right.m_capacityEnd); 00580 } 00581 }; 00582 00584 template <class Element, class Policy, class Allocator> 00585 LEAN_INLINE void swap(simple_vector<Element, Policy, Allocator> &left, simple_vector<Element, Policy, Allocator> &right) 00586 { 00587 left.swap(right); 00588 } 00589 00590 } // namespace 00591 00592 namespace simple_vector_policies = containers::simple_vector_policies; 00593 using containers::simple_vector; 00594 00595 } // namespace 00596 00597 #endif