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