1 # ifndef CPPAD_LOCAL_POD_VECTOR_HPP
2 # define CPPAD_LOCAL_POD_VECTOR_HPP
3 /* --------------------------------------------------------------------------
4 CppAD: C++ Algorithmic Differentiation: Copyright (C) 2003-20 Bradley M. Bell
5 
6 CppAD is distributed under the terms of the
7              Eclipse Public License Version 2.0.
8 
9 This Source Code may also be made available under the following
10 Secondary License when the conditions for such availability set forth
11 in the Eclipse Public License, Version 2.0 are satisfied:
12       GNU General Public License, Version 2.0 or later.
13 ---------------------------------------------------------------------------- */
14 
15 # if CPPAD_CSTDINT_HAS_8_TO_64
16 # include <cstdint>
17 # endif
18 # include <cstring>
19 # include <algorithm>
20 # include <cppad/utility/thread_alloc.hpp>
21 # include <cppad/core/cppad_assert.hpp>
22 # include <cppad/local/is_pod.hpp>
23 
24 namespace CppAD { namespace local { // BEGIN_CPPAD_LOCAL_NAMESPACE
25 /*!
26 \file pod_vector.hpp
27 File used to define pod_vector classes
28 */
29 // ---------------------------------------------------------------------------
30 /*!
31 A vector class with that does not use element constructors or destructors
32 (elements are Plain Old Data; i.e., is_pod<Type> must be true).
33 
34 */
35 template <class Type>
36 class pod_vector {
37 private:
38     /// maximum number of bytes current allocation can hold
39     size_t byte_capacity_;
40 
41     /// number of bytes currently in this vector
42     size_t byte_length_;
43 
44     /// pointer to the first type elements
45     /// (not defined and should not be used when byte_capacity_ = 0)
46     Type   *data_;
47 
48     /// do not use the copy constructor
pod_vector(const pod_vector &)49     explicit pod_vector(const pod_vector& )
50     {   CPPAD_ASSERT_UNKNOWN(false); }
51 public:
52     /// default constructor sets byte_capacity_ = byte_length_ = data_ = 0
pod_vector(void)53     pod_vector(void)
54     : byte_capacity_(0), byte_length_(0), data_(nullptr)
55     {   CPPAD_ASSERT_UNKNOWN( is_pod<Type>() );
56     }
57 
58     /// sizing constructor
pod_vector(size_t n)59     pod_vector(
60         /// number of elements in this vector
61         size_t n )
62     : byte_capacity_(0), byte_length_(0), data_(nullptr)
63     {   CPPAD_ASSERT_UNKNOWN( is_pod<Type>() );
64         extend(n);
65     }
66 
67     /// Destructor: returns allocated memory to thread_alloc;
68     /// see extend and resize.  If this is not plain old data,
69     /// the destructor for each element is called.
~pod_vector(void)70     ~pod_vector(void)
71     {   if( byte_capacity_ > 0 )
72         {
73             void* v_ptr = reinterpret_cast<void*>( data_ );
74             thread_alloc::return_memory(v_ptr);
75         }
76     }
77 
78     /*
79     Return a pointer to a pod_vector with a different type of element.
80 
81     - This vector and the other share the same memory.
82 
83     - The the other vector should not be deleted.
84 
85     - The following operations work the same for this and the other vector:
86     swap, clear, assignment.
87     */
88     template <class Other>
pod_vector_ptr(void)89     pod_vector<Other>* pod_vector_ptr(void)
90     {   return reinterpret_cast< pod_vector<Other>* >(this);
91     }
92     template <class Other>
pod_vector_ptr(void) const93     const pod_vector<Other>* pod_vector_ptr(void) const
94     {   return reinterpret_cast< const pod_vector<Other>* >(this);
95     }
96 
97     /// current number of elements in this vector.
size(void) const98     size_t size(void) const
99     {   return byte_length_ / sizeof(Type); }
100 
101     /// current capacity (amount of allocated storage) for this vector.
capacity(void) const102     size_t capacity(void) const
103     {   return byte_capacity_ / sizeof(Type); }
104 
105     /// current data pointer is no longer valid after any of the following:
106     /// extend, resize, erase, clear, assignment and destructor.
data(void)107     Type* data(void)
108     {   return data_; }
109 
110     /// const version of data pointer (see non-const documentation)
data(void) const111     const Type* data(void) const
112     {   return data_; }
113 
114     //  ----------------------------------------------------------------------
115     /// non-constant element access; i.e., we can change this element value
operator [](size_t i)116     Type& operator[](
117         /// element index, must be less than length
118         size_t i
119     )
120     {   CPPAD_ASSERT_UNKNOWN( i * sizeof(Type) < byte_length_ );
121         return data_[i];
122     }
123     /// non-constant element access; i.e., we can change this element value
124     template <class Index>
operator [](Index i)125     Type& operator[](
126         /// element index, must be less than length and convertable to size_t
127         Index i
128     )
129     {   return (*this)[size_t(i)]; }
130     //  ----------------------------------------------------------------------
131     /// constant element access; i.e., we cannot change this element value
operator [](size_t i) const132     const Type& operator[](
133         /// element index, must be less than length
134         size_t i
135     ) const
136     {   CPPAD_ASSERT_UNKNOWN( i * sizeof(Type) < byte_length_ );
137         return data_[i];
138     }
139     /// constant element access; i.e., we cannot change this element value
140     template <class Index>
operator [](Index i) const141     const Type& operator[](
142         /// element index, must be less than length and convertable to size_t
143         Index i
144     ) const
145     {   return (*this)[size_t(i)]; }
146     //  ----------------------------------------------------------------------
147 
148     /*!
149     Add an element to theh back of this vector
150 
151     \param e
152     is the element we are adding to the back of the vector.
153     */
push_back(const Type & e)154     void push_back(const Type& e)
155     {   size_t i = extend(1);
156         data_[i] = e;
157     }
158 
159     /*!
160     Swap all properties of this vector with another.
161     This is useful when moving a vector that grows after it has reached
162     its final size (without copying every element).
163 
164     \param other
165     is the other vector that we are swapping this vector with.
166     */
swap(pod_vector & other)167     void swap(pod_vector& other)
168     {   std::swap(byte_capacity_, other.byte_capacity_);
169         std::swap(byte_length_,   other.byte_length_);
170         std::swap(data_,          other.data_);
171     }
172     // ----------------------------------------------------------------------
173     /*!
174     Increase the number of elements the end of this vector
175     (existing elements are always preserved).
176 
177     \param n
178     is the number of elements to add to end of this vector.
179 
180     \return
181     is the number of elements in the vector before it was extended.
182     This is the index of the first new element added to the vector.
183 
184     - If Type is plain old data, new elements are not initialized;
185     i.e., their constructor is not called. Otherwise, the constructor
186     is called for each new element.
187 
188     - This and resize are the only routine that allocate memory for
189     pod_vector. They uses thread_alloc for this allocation.
190     */
extend(size_t n)191     size_t extend(size_t n)
192     {   size_t old_length   = byte_length_;
193         byte_length_       += n * sizeof(Type);
194 
195         // check if we can use current memory
196         if( byte_length_ <= byte_capacity_ )
197             return old_length / sizeof(Type);
198 
199         // save more old information
200         size_t old_capacity = byte_capacity_;
201         void* old_v_ptr     = reinterpret_cast<void*>(data_);
202 
203         // get new memory and set capacity
204         void* v_ptr = thread_alloc::get_memory(byte_length_, byte_capacity_);
205         data_       = reinterpret_cast<Type*>(v_ptr);
206 
207         // copy old data to new
208         if( old_length >  0 )
209             std::memcpy(v_ptr, old_v_ptr, old_length);
210 
211         // return old memory to available pool
212         if( old_capacity > 0 )
213             thread_alloc::return_memory(old_v_ptr);
214 
215         // return value for extend(n) is the old length
216         CPPAD_ASSERT_UNKNOWN( byte_length_ <= byte_capacity_ );
217         return old_length / sizeof(Type);
218     }
219     // ----------------------------------------------------------------------
220     /*!
221     resize the vector (existing elements preserved when n <= capacity() ).
222 
223     \param n
224     is the new size for this vector.
225 
226     \par
227     if n <= capacity(), no memory is freed or allocated, the capacity
228     is not changed, and existing elements are preserved.
229     If n > capacity(), new memory is allocates and all the
230     data in the vector is lost.
231 
232     - If  Type is plain old data, new elements are not initialized;
233     i.e., their constructor is not called. Otherwise, the constructor
234     is called for each new element.
235 
236     - This and extend are the only routine that allocate memory for
237     pod_vector. They uses thread_alloc for this allocation.
238     */
resize(size_t n)239     void resize(size_t n)
240     {   byte_length_ = n * sizeof(Type);
241 
242         // check if we must allocate new memory
243         if( byte_capacity_ < byte_length_ )
244         {   void* v_ptr;
245             //
246             if( byte_capacity_ > 0 )
247             {   // return old memory to available pool
248                 v_ptr = reinterpret_cast<void*>( data_ );
249                 thread_alloc::return_memory(v_ptr);
250             }
251             //
252             // get new memory and set capacity
253             v_ptr     = thread_alloc::get_memory(byte_length_, byte_capacity_);
254             data_     = reinterpret_cast<Type*>(v_ptr);
255             //
256         }
257         CPPAD_ASSERT_UNKNOWN( byte_length_ <= byte_capacity_ );
258     }
259     // ----------------------------------------------------------------------
260     /*!
261     Remove all the elements from this vector and free its memory.
262     */
clear(void)263     void clear(void)
264     {   if( byte_capacity_ > 0 )
265         {
266             void* v_ptr = reinterpret_cast<void*>( data_ );
267             thread_alloc::return_memory(v_ptr);
268         }
269         data_          = nullptr;
270         byte_capacity_ = 0;
271         byte_length_   = 0;
272     }
273     // -----------------------------------------------------------------------
274     /// vector assignment operator
operator =(const pod_vector & x)275     void operator=(
276         /// right hand size of the assingment operation
277         const pod_vector& x
278     )
279     {   CPPAD_ASSERT_UNKNOWN( x.byte_length_ % sizeof(Type) == 0 );
280         resize( x.byte_length_ / sizeof(Type) );
281         if( byte_length_ > 0 )
282         {
283             void* v_ptr   = reinterpret_cast<void*>( data_ );
284             void* v_ptr_x = reinterpret_cast<void*>( x.data_ );
285             std::memcpy(v_ptr, v_ptr_x, byte_length_);
286         }
287 
288     }
289 };
290 // ---------------------------------------------------------------------------
291 /*!
292 A vector class with that does not use element constructors or destructors
293 when is_pod<Type> is true.
294 */
295 template <class Type>
296 class pod_vector_maybe {
297 private:
298     /// maximum number of Type elements current allocation can hold
299     size_t capacity_;
300 
301     /// number of elements currently in this vector
302     size_t length_;
303 
304     /// pointer to the first type elements
305     /// (not defined and should not be used when capacity_ = 0)
306     Type   *data_;
307 
308     /// do not use the copy constructor
pod_vector_maybe(const pod_vector_maybe &)309     explicit pod_vector_maybe(const pod_vector_maybe& )
310     {   CPPAD_ASSERT_UNKNOWN(false); }
311 public:
312     /// default constructor sets capacity_ = length_ = data_ = 0
pod_vector_maybe(void)313     pod_vector_maybe(void)
314     : capacity_(0), length_(0), data_(nullptr)
315     {   CPPAD_ASSERT_UNKNOWN( is_pod<size_t>() );
316     }
317 
318     /// sizing constructor
pod_vector_maybe(size_t n)319     pod_vector_maybe(
320         /// number of elements in this vector
321         size_t n )
322     : capacity_(0), length_(0), data_(nullptr)
323     {   extend(n); }
324 
325 
326     /// Destructor: returns allocated memory to thread_alloc;
327     /// see extend and resize.  If this is not plain old data,
328     /// the destructor for each element is called.
~pod_vector_maybe(void)329     ~pod_vector_maybe(void)
330     {   if( capacity_ > 0 )
331         {   if( ! is_pod<Type>() )
332             {   // call destructor for each element
333                 for(size_t i = 0; i < capacity_; i++)
334                     (data_ + i)->~Type();
335             }
336             void* v_ptr = reinterpret_cast<void*>( data_ );
337             thread_alloc::return_memory(v_ptr);
338         }
339     }
340 
341     /// current number of elements in this vector.
size(void) const342     size_t size(void) const
343     {   return length_; }
344 
345     /// current capacity (amount of allocated storage) for this vector.
capacity(void) const346     size_t capacity(void) const
347     {   return capacity_; }
348 
349     /// current data pointer is no longer valid after any of the following:
350     /// extend, resize, erase, clear, assignment, and destructor.
data(void)351     Type* data(void)
352     {   return data_; }
353 
354     /// const version of data pointer (see non-const documentation)
data(void) const355     const Type* data(void) const
356     {   return data_; }
357     // ----------------------------------------------------------------------
358     /// non-constant element access; i.e., we can change this element value
operator [](size_t i)359     Type& operator[](
360         /// element index, must be less than length
361         size_t i
362     )
363     {   CPPAD_ASSERT_UNKNOWN( i < length_ );
364         return data_[i];
365     }
366     /// non-constant element access; i.e., we can change this element value
367     template <class Index>
operator [](Index i)368     Type& operator[](
369         /// element index, must be less than length and convertable to size_t
370         Index i
371     )
372     {   return (*this)[size_t(i)]; }
373 
374     // ----------------------------------------------------------------------
375     /// constant element access; i.e., we cannot change this element value
operator [](size_t i) const376     const Type& operator[](
377         /// element index, must be less than length
378         size_t i
379     ) const
380     {   CPPAD_ASSERT_UNKNOWN( i < length_ );
381         return data_[i];
382     }
383     /// constant element access; i.e., we cannot change this element value
384     template <class Index>
operator [](Index i) const385     const Type& operator[](
386         /// element index, must be less than length and convertable to size_t
387         Index i
388     ) const
389     {   return (*this)[size_t(i)]; }
390 
391     // ----------------------------------------------------------------------
392     /*!
393     Add an element to theh back of this vector
394 
395     \param e
396     is the element we are adding to the back of the vector.
397     */
push_back(const Type & e)398     void push_back(const Type& e)
399     {   size_t i = extend(1);
400         data_[i] = e;
401     }
402 
403     /*!
404     Swap all properties of this vector with another.
405     This is useful when moving a vector that grows after it has reached
406     its final size (without copying every element).
407 
408     \param other
409     is the other vector that we are swapping this vector with.
410     */
swap(pod_vector_maybe & other)411     void swap(pod_vector_maybe& other)
412     {   std::swap(capacity_, other.capacity_);
413         std::swap(length_,   other.length_);
414         std::swap(data_,     other.data_);
415     }
416     // ----------------------------------------------------------------------
417     /*!
418     Increase the number of elements the end of this vector
419     (existing elements are always preserved).
420 
421     \param n
422     is the number of elements to add to end of this vector.
423 
424     \return
425     is the number of elements in the vector before it was extended.
426     This is the index of the first new element added to the vector.
427 
428     - If Type is plain old data, new elements are not initialized;
429     i.e., their constructor is not called. Otherwise, the constructor
430     is called for each new element.
431 
432     - This and resize are the only routine that allocate memory for
433     pod_vector_maybe. They uses thread_alloc for this allocation.
434     */
extend(size_t n)435     size_t extend(size_t n)
436     {   size_t old_length   = length_;
437         length_            += n;
438 
439         // check if we can use current memory
440         if( length_ <= capacity_ )
441             return old_length;
442 
443         // save more old information
444         size_t old_capacity = capacity_;
445         Type* old_data      = data_;
446 
447         // get new memory and set capacity
448         size_t length_bytes = length_ * sizeof(Type);
449         size_t capacity_bytes;
450         void* v_ptr = thread_alloc::get_memory(length_bytes, capacity_bytes);
451         capacity_   = capacity_bytes / sizeof(Type);
452         data_       = reinterpret_cast<Type*>(v_ptr);
453 
454         if( ! is_pod<Type>() )
455         {   // call constructor for each new element
456             for(size_t i = 0; i < capacity_; i++)
457                 new(data_ + i) Type();
458         }
459 
460         // copy old data to new
461         for(size_t i = 0; i < old_length; i++)
462             data_[i] = old_data[i];
463 
464         // return old memory to available pool
465         if( old_capacity > 0 )
466         {   if( ! is_pod<Type>() )
467             {   for(size_t i = 0; i < old_capacity; i++)
468                     (old_data + i)->~Type();
469             }
470             v_ptr = reinterpret_cast<void*>( old_data );
471             thread_alloc::return_memory(v_ptr);
472         }
473 
474         // return value for extend(n) is the old length
475         CPPAD_ASSERT_UNKNOWN( length_ <= capacity_ );
476         return old_length;
477     }
478     // ----------------------------------------------------------------------
479     /*!
480     resize the vector (existing elements preserved when n <= capacity_).
481 
482     \param n
483     is the new size for this vector.
484 
485     \par
486     if n <= capacity(), no memory is freed or allocated, the capacity
487     is not changed, and existing elements are preserved.
488     If n > capacity(), new memory is allocates and all the
489     data in the vector is lost.
490 
491     - If  Type is plain old data, new elements are not initialized;
492     i.e., their constructor is not called. Otherwise, the constructor
493     is called for each new element.
494 
495     - This and extend are the only routine that allocate memory for
496     pod_vector_maybe. They uses thread_alloc for this allocation.
497     */
resize(size_t n)498     void resize(size_t n)
499     {   length_ = n;
500 
501         // check if we must allocate new memory
502         if( capacity_ < length_ )
503         {   void* v_ptr;
504             //
505             // return old memory to available pool
506             if( capacity_ > 0 )
507             {   if( ! is_pod<Type>() )
508                 {   // call destructor for each old element
509                     for(size_t i = 0; i < capacity_; i++)
510                         (data_ + i)->~Type();
511                 }
512                 v_ptr = reinterpret_cast<void*>( data_ );
513                 thread_alloc::return_memory(v_ptr);
514             }
515             //
516             // get new memory and set capacity
517             size_t length_bytes = length_ * sizeof(Type);
518             size_t capacity_bytes;
519             v_ptr     = thread_alloc::get_memory(length_bytes, capacity_bytes);
520             capacity_ = capacity_bytes / sizeof(Type);
521             data_     = reinterpret_cast<Type*>(v_ptr);
522             //
523             CPPAD_ASSERT_UNKNOWN( length_ <= capacity_ );
524             //
525             if( ! is_pod<Type>() )
526             {   // call constructor for each new element
527                 for(size_t i = 0; i < capacity_; i++)
528                     new(data_ + i) Type();
529             }
530         }
531     }
532     // ----------------------------------------------------------------------
533     /*!
534     Remove all the elements from this vector and free its memory.
535     */
clear(void)536     void clear(void)
537     {   if( capacity_ > 0 )
538         {   if( ! is_pod<Type>() )
539             {   // call destructor for each element
540                 for(size_t i = 0; i < capacity_; i++)
541                     (data_ + i)->~Type();
542             }
543             void* v_ptr = reinterpret_cast<void*>( data_ );
544             thread_alloc::return_memory(v_ptr);
545         }
546         data_     = nullptr;
547         capacity_ = 0;
548         length_   = 0;
549     }
550     // -----------------------------------------------------------------------
551     /// vector assignment operator
operator =(const pod_vector_maybe & x)552     void operator=(
553         /// right hand size of the assingment operation
554         const pod_vector_maybe& x
555     )
556     {   resize( x.length_ );
557         //
558         CPPAD_ASSERT_UNKNOWN( length_   == x.length_ );
559         for(size_t i = 0; i < length_; i++)
560         {   data_[i] = x.data_[i]; }
561     }
562 };
563 
564 } } // END_CPPAD_LOCAL_NAMESPACE
565 # endif
566