| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | /// | ||
| 4 | /// @file PagedArray.h | ||
| 5 | /// | ||
| 6 | /// @author Ken Museth | ||
| 7 | /// | ||
| 8 | /// @brief Concurrent, page-based, dynamically-sized linear data | ||
| 9 | /// structure with O(1) random access and STL-compliant | ||
| 10 | /// iterators. It is primarily intended for applications | ||
| 11 | /// that involve multi-threading push_back of (a possibly | ||
| 12 | /// unkown number of) elements into a dynamically growing | ||
| 13 | /// linear array, and fast random access to said elements. | ||
| 14 | |||
| 15 | #ifndef OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED | ||
| 16 | #define OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED | ||
| 17 | |||
| 18 | #include <openvdb/version.h> | ||
| 19 | #include <openvdb/Types.h>// SharedPtr | ||
| 20 | #include <deque> | ||
| 21 | #include <cassert> | ||
| 22 | #include <iostream> | ||
| 23 | #include <algorithm>// std::swap | ||
| 24 | #include <atomic> | ||
| 25 | #include <tbb/spin_mutex.h> | ||
| 26 | #include <tbb/parallel_for.h> | ||
| 27 | #include <tbb/parallel_sort.h> | ||
| 28 | |||
| 29 | namespace openvdb { | ||
| 30 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 31 | namespace OPENVDB_VERSION_NAME { | ||
| 32 | namespace util { | ||
| 33 | |||
| 34 | //////////////////////////////////////// | ||
| 35 | |||
| 36 | |||
| 37 | /// @brief Concurrent, page-based, dynamically-sized linear data structure | ||
| 38 | /// with O(1) random access and STL-compliant iterators. It is | ||
| 39 | /// primarily intended for applications that concurrently insert | ||
| 40 | /// (a possibly unkown number of) elements into a dynamically | ||
| 41 | /// growing linear array, and fast random access to said elements. | ||
| 42 | /// | ||
| 43 | /// @note Multiple threads can grow the page-table and push_back | ||
| 44 | /// new elements concurrently. A ValueBuffer provides accelerated | ||
| 45 | /// and threadsafe push_back at the cost of potentially re-ordering | ||
| 46 | /// elements (when multiple instances are used). | ||
| 47 | /// | ||
| 48 | /// @details This data structure employes contiguous pages of elements | ||
| 49 | /// (a std::deque) which avoids moving data when the | ||
| 50 | /// capacity is out-grown and new pages are allocated. The | ||
| 51 | /// size of the pages can be controlled with the Log2PageSize | ||
| 52 | /// template parameter (defaults to 1024 elements of type ValueT). | ||
| 53 | /// | ||
| 54 | /// There are three fundamentally different ways to insert elements to | ||
| 55 | /// this container - each with different advanteges and disadvanteges. | ||
| 56 | /// | ||
| 57 | /// The simplest way to insert elements is to use PagedArray::push_back_unsafe | ||
| 58 | /// which is @a not thread-safe: | ||
| 59 | /// @code | ||
| 60 | /// PagedArray<size_t> array; | ||
| 61 | /// for (size_t i=0; i<100000; ++i) array.push_back_unsafe(i); | ||
| 62 | /// @endcode | ||
| 63 | /// | ||
| 64 | /// The fastest way (by far) to insert elements is by means of a PagedArray::ValueBuffer: | ||
| 65 | /// @code | ||
| 66 | /// PagedArray<size_t> array; | ||
| 67 | /// auto buffer = array.getBuffer(); | ||
| 68 | /// for (size_t i=0; i<100000; ++i) buffer.push_back(i); | ||
| 69 | /// buffer.flush(); | ||
| 70 | /// @endcode | ||
| 71 | /// or | ||
| 72 | /// @code | ||
| 73 | /// PagedArray<size_t> array; | ||
| 74 | /// { | ||
| 75 | /// //local scope of a single thread | ||
| 76 | /// auto buffer = array.getBuffer(); | ||
| 77 | /// for (size_t i=0; i<100000; ++i) buffer.push_back(i); | ||
| 78 | /// } | ||
| 79 | /// @endcode | ||
| 80 | /// or with TBB task-based multi-threading: | ||
| 81 | /// @code | ||
| 82 | /// PagedArray<size_t> array; | ||
| 83 | /// tbb::parallel_for( | ||
| 84 | /// tbb::blocked_range<size_t>(0, 10000, array.pageSize()), | ||
| 85 | /// [&array](const tbb::blocked_range<size_t>& range) { | ||
| 86 | /// auto buffer = array.getBuffer(); | ||
| 87 | /// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); | ||
| 88 | /// } | ||
| 89 | /// ); | ||
| 90 | /// @endcode | ||
| 91 | /// or with TBB thread-local storage for even better performance (due | ||
| 92 | /// to fewer concurrent instantiations of partially full ValueBuffers) | ||
| 93 | /// @code | ||
| 94 | /// PagedArray<size_t> array; | ||
| 95 | /// auto exemplar = array.getBuffer();//dummy used for initialization | ||
| 96 | /// tbb::enumerable_thread_specific<PagedArray<size_t>::ValueBuffer> | ||
| 97 | /// pool(exemplar);//thread local storage pool of ValueBuffers | ||
| 98 | /// tbb::parallel_for( | ||
| 99 | /// tbb::blocked_range<size_t>(0, 10000, array.pageSize()), | ||
| 100 | /// [&pool](const tbb::blocked_range<size_t>& range) { | ||
| 101 | /// auto &buffer = pool.local(); | ||
| 102 | /// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); | ||
| 103 | /// } | ||
| 104 | /// ); | ||
| 105 | /// for (auto i=pool.begin(); i!=pool.end(); ++i) i->flush(); | ||
| 106 | /// @endcode | ||
| 107 | /// This technique generally outperforms PagedArray::push_back_unsafe, | ||
| 108 | /// std::vector::push_back, std::deque::push_back and even | ||
| 109 | /// tbb::concurrent_vector::push_back. Additionally it | ||
| 110 | /// is thread-safe as long as each thread has it's own instance of a | ||
| 111 | /// PagedArray::ValueBuffer. The only disadvantage is the ordering of | ||
| 112 | /// the elements is undefined if multiple instance of a | ||
| 113 | /// PagedArray::ValueBuffer are employed. This is typically the case | ||
| 114 | /// in the context of multi-threading, where the | ||
| 115 | /// ordering of inserts are undefined anyway. Note that a local scope | ||
| 116 | /// can be used to guarentee that the ValueBuffer has inserted all its | ||
| 117 | /// elements by the time the scope ends. Alternatively the ValueBuffer | ||
| 118 | /// can be explicitly flushed by calling ValueBuffer::flush. | ||
| 119 | /// | ||
| 120 | /// The third way to insert elements is to resize the container and use | ||
| 121 | /// random access, e.g. | ||
| 122 | /// @code | ||
| 123 | /// PagedArray<int> array; | ||
| 124 | /// array.resize(100000); | ||
| 125 | /// for (int i=0; i<100000; ++i) array[i] = i; | ||
| 126 | /// @endcode | ||
| 127 | /// or in terms of the random access iterator | ||
| 128 | /// @code | ||
| 129 | /// PagedArray<int> array; | ||
| 130 | /// array.resize(100000); | ||
| 131 | /// for (auto i=array.begin(); i!=array.end(); ++i) *i = i.pos(); | ||
| 132 | /// @endcode | ||
| 133 | /// While this approach is both fast and thread-safe it suffers from the | ||
| 134 | /// major disadvantage that the problem size, i.e. number of elements, needs to | ||
| 135 | /// be known in advance. If that's the case you might as well consider | ||
| 136 | /// using std::vector or a raw c-style array! In other words the | ||
| 137 | /// PagedArray is most useful in the context of applications that | ||
| 138 | /// involve multi-threading of dynamically growing linear arrays that | ||
| 139 | /// require fast random access. | ||
| 140 | |||
| 141 | template<typename ValueT, size_t Log2PageSize = 10UL> | ||
| 142 | class PagedArray | ||
| 143 | { | ||
| 144 | private: | ||
| 145 | static_assert(Log2PageSize > 1UL, "Expected Log2PageSize > 1"); | ||
| 146 | class Page; | ||
| 147 | |||
| 148 | // must allow mutiple threads to call operator[] as long as only one thread calls push_back | ||
| 149 | using PageTableT = std::deque<Page*>; | ||
| 150 | |||
| 151 | public: | ||
| 152 | using ValueType = ValueT; | ||
| 153 | using Ptr = SharedPtr<PagedArray>; | ||
| 154 | |||
| 155 | /// @brief Default constructor | ||
| 156 | 26 | PagedArray() : mCapacity{0} { mSize = 0; } | |
| 157 | |||
| 158 | /// @brief Destructor removed all allocated pages | ||
| 159 | 13 | ~PagedArray() { this->clear(); } | |
| 160 | |||
| 161 | // Disallow copy construction and assignment | ||
| 162 | PagedArray(const PagedArray&) = delete; | ||
| 163 | PagedArray& operator=(const PagedArray&) = delete; | ||
| 164 | |||
| 165 | /// @brief Return a shared pointer to a new instance of this class | ||
| 166 | static Ptr create() { return Ptr(new PagedArray); } | ||
| 167 | |||
| 168 | /// @brief Caches values into a local memory Page to improve | ||
| 169 | /// performance of push_back into a PagedArray. | ||
| 170 | /// | ||
| 171 | /// @note The ordering of inserted elements is undefined when | ||
| 172 | /// multiple ValueBuffers are used! | ||
| 173 | /// | ||
| 174 | /// @warning By design this ValueBuffer is not threadsafe so | ||
| 175 | /// make sure to create an instance per thread! | ||
| 176 | class ValueBuffer; | ||
| 177 | |||
| 178 | /// @return a new instance of a ValueBuffer which supports thread-safe push_back! | ||
| 179 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | ValueBuffer getBuffer() { return ValueBuffer(*this); } |
| 180 | |||
| 181 | /// Const std-compliant iterator | ||
| 182 | class ConstIterator; | ||
| 183 | |||
| 184 | /// Non-const std-compliant iterator | ||
| 185 | class Iterator; | ||
| 186 | |||
| 187 | /// @brief This method is deprecated and will be removed shortly! | ||
| 188 | OPENVDB_DEPRECATED size_t push_back(const ValueType& value) | ||
| 189 | { | ||
| 190 | return this->push_back_unsafe(value); | ||
| 191 | } | ||
| 192 | |||
| 193 | /// @param value value to be added to this PagedArray | ||
| 194 | /// | ||
| 195 | /// @note For best performance consider using the ValueBuffer! | ||
| 196 | /// | ||
| 197 | /// @warning Not thread-safe and mostly intended for debugging! | ||
| 198 |
2/2✓ Branch 0 taken 352 times.
✓ Branch 1 taken 356675 times.
|
714054 | size_t push_back_unsafe(const ValueType& value) |
| 199 | { | ||
| 200 | const size_t index = mSize.fetch_add(1); | ||
| 201 |
2/2✓ Branch 0 taken 352 times.
✓ Branch 1 taken 356675 times.
|
714054 | if (index >= mCapacity) { |
| 202 | 704 | mPageTable.push_back( new Page() ); | |
| 203 | 704 | mCapacity += Page::Size; | |
| 204 | } | ||
| 205 | 714054 | (*mPageTable[index >> Log2PageSize])[index] = value; | |
| 206 | 714054 | return index; | |
| 207 | } | ||
| 208 | |||
| 209 | /// @brief Reduce the page table to fix the current size. | ||
| 210 | /// | ||
| 211 | /// @warning Not thread-safe! | ||
| 212 | void shrink_to_fit(); | ||
| 213 | |||
| 214 | /// @brief Return a reference to the value at the specified offset | ||
| 215 | /// | ||
| 216 | /// @param i linear offset of the value to be accessed. | ||
| 217 | /// | ||
| 218 | /// @note This random access has constant time complexity. | ||
| 219 | /// | ||
| 220 | /// @warning It is assumed that the i'th element is already allocated! | ||
| 221 | 90367776 | ValueType& operator[](size_t i) | |
| 222 | { | ||
| 223 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 45183888 times.
|
90367776 | assert(i<mCapacity); |
| 224 | 90367776 | return (*mPageTable[i>>Log2PageSize])[i]; | |
| 225 | } | ||
| 226 | |||
| 227 | /// @brief Return a const-reference to the value at the specified offset | ||
| 228 | /// | ||
| 229 | /// @param i linear offset of the value to be accessed. | ||
| 230 | /// | ||
| 231 | /// @note This random access has constant time complexity. | ||
| 232 | /// | ||
| 233 | /// @warning It is assumed that the i'th element is already allocated! | ||
| 234 | const ValueType& operator[](size_t i) const | ||
| 235 | { | ||
| 236 | assert(i<mCapacity); | ||
| 237 | return (*mPageTable[i>>Log2PageSize])[i]; | ||
| 238 | } | ||
| 239 | |||
| 240 | /// @brief Set all elements in the page table to the specified value | ||
| 241 | /// | ||
| 242 | /// @param v value to be filled in all the existing pages of this PagedArray. | ||
| 243 | /// | ||
| 244 | /// @note Multi-threaded | ||
| 245 | 1 | void fill(const ValueType& v) | |
| 246 | { | ||
| 247 | 380 | auto op = [&](const tbb::blocked_range<size_t>& r){ | |
| 248 |
2/2✓ Branch 0 taken 251 times.
✓ Branch 1 taken 128 times.
|
379 | for(size_t i=r.begin(); i!=r.end(); ++i) mPageTable[i]->fill(v); |
| 249 | }; | ||
| 250 | 1 | tbb::parallel_for(tbb::blocked_range<size_t>(0, this->pageCount()), op); | |
| 251 | 1 | } | |
| 252 | |||
| 253 | /// @brief Copy the first @a count values in this PageArray into | ||
| 254 | /// a raw c-style array, assuming it to be at least @a count | ||
| 255 | /// elements long. | ||
| 256 | /// | ||
| 257 | /// @param p pointer to an array that will used as the destination of the copy. | ||
| 258 | /// @param count number of elements to be copied. | ||
| 259 | /// | ||
| 260 | bool copy(ValueType *p, size_t count) const | ||
| 261 | { | ||
| 262 | size_t last_page = count >> Log2PageSize; | ||
| 263 | if (last_page >= this->pageCount()) return false; | ||
| 264 | auto op = [&](const tbb::blocked_range<size_t>& r){ | ||
| 265 | for (size_t i=r.begin(); i!=r.end(); ++i) { | ||
| 266 | mPageTable[i]->copy(p+i*Page::Size, Page::Size); | ||
| 267 | } | ||
| 268 | }; | ||
| 269 | if (size_t m = count & Page::Mask) {//count is not divisible by page size | ||
| 270 | tbb::parallel_for(tbb::blocked_range<size_t>(0, last_page, 32), op); | ||
| 271 | mPageTable[last_page]->copy(p+last_page*Page::Size, m); | ||
| 272 | } else { | ||
| 273 | tbb::parallel_for(tbb::blocked_range<size_t>(0, last_page+1, 32), op); | ||
| 274 | } | ||
| 275 | return true; | ||
| 276 | } | ||
| 277 | void copy(ValueType *p) const { this->copy(p, mSize); } | ||
| 278 | |||
| 279 | /// @brief Resize this array to the specified size. | ||
| 280 | /// | ||
| 281 | /// @param size number of elements that this PageArray will contain. | ||
| 282 | /// | ||
| 283 | /// @details Will grow or shrink the page table to contain | ||
| 284 | /// the specified number of elements. It will affect the size(), | ||
| 285 | /// iteration will go over all those elements, push_back will | ||
| 286 | /// insert after them and operator[] can be used directly access | ||
| 287 | /// them. | ||
| 288 | /// | ||
| 289 | /// @note No reserve method is implemented due to efficiency concerns | ||
| 290 | /// (especially for the ValueBuffer) from having to deal with empty pages. | ||
| 291 | /// | ||
| 292 | /// @warning Not thread-safe! | ||
| 293 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
6 | void resize(size_t size) |
| 294 | { | ||
| 295 | mSize = size; | ||
| 296 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
6 | if (size > mCapacity) { |
| 297 | 6 | this->grow(size-1); | |
| 298 | } else { | ||
| 299 | ✗ | this->shrink_to_fit(); | |
| 300 | } | ||
| 301 | 6 | } | |
| 302 | |||
| 303 | /// @brief Resize this array to the specified size and initialize | ||
| 304 | /// all values to @a v. | ||
| 305 | /// | ||
| 306 | /// @param size number of elements that this PageArray will contain. | ||
| 307 | /// @param v value of all the @a size values. | ||
| 308 | /// | ||
| 309 | /// @details Will grow or shrink the page table to contain | ||
| 310 | /// the specified number of elements. It will affect the size(), | ||
| 311 | /// iteration will go over all those elements, push_back will | ||
| 312 | /// insert after them and operator[] can be used directly access them. | ||
| 313 | /// | ||
| 314 | /// @note No reserve method is implemented due to efficiency concerns | ||
| 315 | /// (especially for the ValueBuffer) from having to deal with empty pages. | ||
| 316 | /// | ||
| 317 | /// @warning Not thread-safe! | ||
| 318 | void resize(size_t size, const ValueType& v) | ||
| 319 | { | ||
| 320 | this->resize(size); | ||
| 321 | this->fill(v); | ||
| 322 | } | ||
| 323 | |||
| 324 | /// @brief Return the number of elements in this array. | ||
| 325 | size_t size() const { return mSize; } | ||
| 326 | |||
| 327 | /// @brief Return the maximum number of elements that this array | ||
| 328 | /// can contain without allocating more memory pages. | ||
| 329 |
12/24✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 1 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 1 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 35 not taken.
|
13 | size_t capacity() const { return mCapacity; } |
| 330 | |||
| 331 | /// @brief Return the number of additional elements that can be | ||
| 332 | /// added to this array without allocating more memory pages. | ||
| 333 | size_t freeCount() const { return mCapacity - mSize; } | ||
| 334 | |||
| 335 | /// @brief Return the number of allocated memory pages. | ||
| 336 | size_t pageCount() const { return mPageTable.size(); } | ||
| 337 | |||
| 338 | /// @brief Return the number of elements per memory page. | ||
| 339 | static size_t pageSize() { return Page::Size; } | ||
| 340 | |||
| 341 | /// @brief Return log2 of the number of elements per memory page. | ||
| 342 | static size_t log2PageSize() { return Log2PageSize; } | ||
| 343 | |||
| 344 | /// @brief Return the memory footprint of this array in bytes. | ||
| 345 | size_t memUsage() const | ||
| 346 | { | ||
| 347 | return sizeof(*this) + mPageTable.size() * Page::memUsage(); | ||
| 348 | } | ||
| 349 | |||
| 350 | /// @brief Return true if the container contains no elements. | ||
| 351 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | bool isEmpty() const { return mSize == 0; } |
| 352 | |||
| 353 | /// @brief Return true if the page table is partially full, i.e. the | ||
| 354 | /// last non-empty page contains less than pageSize() elements. | ||
| 355 | /// | ||
| 356 | /// @details When the page table is partially full calling merge() | ||
| 357 | /// or using a ValueBuffer will rearrange the ordering of | ||
| 358 | /// existing elements. | ||
| 359 |
4/8✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
|
4 | bool isPartiallyFull() const { return (mSize & Page::Mask) > 0; } |
| 360 | |||
| 361 | /// @brief Removes all elements from the array and delete all pages. | ||
| 362 | /// | ||
| 363 | /// @warning Not thread-safe! | ||
| 364 | 28 | void clear() | |
| 365 | { | ||
| 366 |
3/4✓ Branch 0 taken 65745 times.
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 65745 times.
✗ Branch 3 not taken.
|
263008 | for (size_t i=0, n=mPageTable.size(); i<n; ++i) delete mPageTable[i]; |
| 367 | 28 | PageTableT().swap(mPageTable); | |
| 368 | mSize = 0; | ||
| 369 | 28 | mCapacity = 0; | |
| 370 | 28 | } | |
| 371 | |||
| 372 | /// @brief Return a non-const iterator pointing to the first element | ||
| 373 | 1 | Iterator begin() { return Iterator(*this, 0); } | |
| 374 | |||
| 375 | /// @brief Return a non-const iterator pointing to the | ||
| 376 | /// past-the-last element. | ||
| 377 | /// | ||
| 378 | /// @warning Iterator does not point to a valid element and should not | ||
| 379 | /// be dereferenced! | ||
| 380 | Iterator end() { return Iterator(*this, mSize); } | ||
| 381 | |||
| 382 | //@{ | ||
| 383 | /// @brief Return a const iterator pointing to the first element | ||
| 384 | ConstIterator cbegin() const { return ConstIterator(*this, 0); } | ||
| 385 | ConstIterator begin() const { return ConstIterator(*this, 0); } | ||
| 386 | //@} | ||
| 387 | |||
| 388 | //@{ | ||
| 389 | /// @brief Return a const iterator pointing to the | ||
| 390 | /// past-the-last element. | ||
| 391 | /// | ||
| 392 | /// @warning Iterator does not point to a valid element and should not | ||
| 393 | /// be dereferenced! | ||
| 394 | ConstIterator cend() const { return ConstIterator(*this, mSize); } | ||
| 395 | ConstIterator end() const { return ConstIterator(*this, mSize); } | ||
| 396 | //@} | ||
| 397 | |||
| 398 | /// @brief Parallel sort of all the elements in ascending order. | ||
| 399 | 8 | void sort() { tbb::parallel_sort(this->begin(), this->end(), std::less<ValueT>() ); } | |
| 400 | |||
| 401 | /// @brief Parallel sort of all the elements in descending order. | ||
| 402 | 1 | void invSort() { tbb::parallel_sort(this->begin(), this->end(), std::greater<ValueT>()); } | |
| 403 | |||
| 404 | //@{ | ||
| 405 | /// @brief Parallel sort of all the elements based on a custom | ||
| 406 | /// functor with the api: | ||
| 407 | /// @code bool operator()(const ValueT& a, const ValueT& b) @endcode | ||
| 408 | /// which returns true if a comes before b. | ||
| 409 | template <typename Functor> | ||
| 410 | void sort(Functor func) { tbb::parallel_sort(this->begin(), this->end(), func ); } | ||
| 411 | //@} | ||
| 412 | |||
| 413 | /// @brief Transfer all the elements (and pages) from the other array to this array. | ||
| 414 | /// | ||
| 415 | /// @param other non-const reference to the PagedArray that will be merged into this PagedArray. | ||
| 416 | /// | ||
| 417 | /// @note The other PagedArray is empty on return. | ||
| 418 | /// | ||
| 419 | /// @warning The ordering of elements is undefined if this page table is partially full! | ||
| 420 | void merge(PagedArray& other); | ||
| 421 | |||
| 422 | /// @brief Print information for debugging | ||
| 423 | void print(std::ostream& os = std::cout) const | ||
| 424 | { | ||
| 425 | os << "PagedArray:\n" | ||
| 426 | << "\tSize: " << this->size() << " elements\n" | ||
| 427 | << "\tPage table: " << this->pageCount() << " pages\n" | ||
| 428 | << "\tPage size: " << this->pageSize() << " elements\n" | ||
| 429 | << "\tCapacity: " << this->capacity() << " elements\n" | ||
| 430 | << "\tFootprint: " << this->memUsage() << " bytes\n"; | ||
| 431 | } | ||
| 432 | |||
| 433 | private: | ||
| 434 | |||
| 435 | friend class ValueBuffer; | ||
| 436 | |||
| 437 | 6 | void grow(size_t index) | |
| 438 | { | ||
| 439 | 6 | tbb::spin_mutex::scoped_lock lock(mGrowthMutex); | |
| 440 |
2/2✓ Branch 0 taken 32196 times.
✓ Branch 1 taken 3 times.
|
64398 | while(index >= mCapacity) { |
| 441 |
1/4✓ Branch 1 taken 32196 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
64392 | mPageTable.push_back( new Page() ); |
| 442 | 64392 | mCapacity += Page::Size; | |
| 443 | } | ||
| 444 | 6 | } | |
| 445 | |||
| 446 | void add_full(Page*& page, size_t size); | ||
| 447 | |||
| 448 | void add_partially_full(Page*& page, size_t size); | ||
| 449 | |||
| 450 | 66436 | void add(Page*& page, size_t size) { | |
| 451 | 66436 | tbb::spin_mutex::scoped_lock lock(mGrowthMutex); | |
| 452 |
2/2✓ Branch 0 taken 32950 times.
✓ Branch 1 taken 268 times.
|
66436 | if (size == Page::Size) {//page is full |
| 453 |
1/2✓ Branch 1 taken 32950 times.
✗ Branch 2 not taken.
|
65900 | this->add_full(page, size); |
| 454 |
2/2✓ Branch 0 taken 259 times.
✓ Branch 1 taken 9 times.
|
536 | } else if (size>0) {//page is only partially full |
| 455 |
1/2✓ Branch 1 taken 259 times.
✗ Branch 2 not taken.
|
518 | this->add_partially_full(page, size); |
| 456 | } | ||
| 457 | 66436 | } | |
| 458 | PageTableT mPageTable;//holds points to allocated pages | ||
| 459 | std::atomic<size_t> mSize;// current number of elements in array | ||
| 460 | size_t mCapacity;//capacity of array given the current page count | ||
| 461 | tbb::spin_mutex mGrowthMutex;//Mutex-lock required to grow pages | ||
| 462 | }; // Public class PagedArray | ||
| 463 | |||
| 464 | //////////////////////////////////////////////////////////////////////////////// | ||
| 465 | |||
| 466 | template <typename ValueT, size_t Log2PageSize> | ||
| 467 | ✗ | void PagedArray<ValueT, Log2PageSize>::shrink_to_fit() | |
| 468 | { | ||
| 469 | ✗ | if (mPageTable.size() > (mSize >> Log2PageSize) + 1) { | |
| 470 | ✗ | tbb::spin_mutex::scoped_lock lock(mGrowthMutex); | |
| 471 | ✗ | const size_t pageCount = (mSize >> Log2PageSize) + 1; | |
| 472 | ✗ | if (mPageTable.size() > pageCount) { | |
| 473 | ✗ | delete mPageTable.back(); | |
| 474 | ✗ | mPageTable.pop_back(); | |
| 475 | ✗ | mCapacity -= Page::Size; | |
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | |||
| 480 | template <typename ValueT, size_t Log2PageSize> | ||
| 481 | 1 | void PagedArray<ValueT, Log2PageSize>::merge(PagedArray& other) | |
| 482 | { | ||
| 483 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
1 | if (&other != this && !other.isEmpty()) { |
| 484 | 1 | tbb::spin_mutex::scoped_lock lock(mGrowthMutex); | |
| 485 | // extract last partially full page if it exists | ||
| 486 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | Page* page = nullptr; |
| 487 | 1 | const size_t size = mSize & Page::Mask; //number of elements in the last page | |
| 488 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if ( size > 0 ) { |
| 489 | 1 | page = mPageTable.back(); | |
| 490 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | mPageTable.pop_back(); |
| 491 | mSize -= size; | ||
| 492 | } | ||
| 493 | // transfer all pages from the other page table | ||
| 494 |
2/6✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
1 | mPageTable.insert(mPageTable.end(), other.mPageTable.begin(), other.mPageTable.end()); |
| 495 | mSize += other.mSize; | ||
| 496 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | mCapacity = Page::Size*mPageTable.size(); |
| 497 | other.mSize = 0; | ||
| 498 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | other.mCapacity = 0; |
| 499 | 1 | PageTableT().swap(other.mPageTable); | |
| 500 | // add back last partially full page | ||
| 501 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
|
1 | if (page) this->add_partially_full(page, size); |
| 502 | } | ||
| 503 | 1 | } | |
| 504 | |||
| 505 | template <typename ValueT, size_t Log2PageSize> | ||
| 506 | 65900 | void PagedArray<ValueT, Log2PageSize>::add_full(Page*& page, size_t size) | |
| 507 | { | ||
| 508 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32950 times.
|
65900 | assert(size == Page::Size);//page must be full |
| 509 |
2/2✓ Branch 0 taken 249 times.
✓ Branch 1 taken 32701 times.
|
65900 | if (mSize & Page::Mask) {//page-table is partially full |
| 510 | Page*& tmp = mPageTable.back(); | ||
| 511 | std::swap(tmp, page);//swap last table entry with page | ||
| 512 | } | ||
| 513 | 65900 | mPageTable.push_back(page); | |
| 514 | 65900 | mCapacity += Page::Size; | |
| 515 | mSize += size; | ||
| 516 | 65900 | page = nullptr; | |
| 517 | 65900 | } | |
| 518 | |||
| 519 | template <typename ValueT, size_t Log2PageSize> | ||
| 520 | 520 | void PagedArray<ValueT, Log2PageSize>::add_partially_full(Page*& page, size_t size) | |
| 521 | { | ||
| 522 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 260 times.
|
520 | assert(size > 0 && size < Page::Size);//page must be partially full |
| 523 |
2/2✓ Branch 0 taken 252 times.
✓ Branch 1 taken 8 times.
|
520 | if (size_t m = mSize & Page::Mask) {//page table is also partially full |
| 524 | 1008 | ValueT *s = page->data(), *t = mPageTable.back()->data() + m; | |
| 525 |
2/2✓ Branch 0 taken 128832 times.
✓ Branch 1 taken 252 times.
|
258168 | for (size_t i=std::min(mSize+size, mCapacity)-mSize; i; --i) *t++ = *s++; |
| 526 |
2/2✓ Branch 0 taken 240 times.
✓ Branch 1 taken 12 times.
|
504 | if (mSize+size > mCapacity) {//grow page table |
| 527 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 238 times.
|
960 | mPageTable.push_back( new Page() ); |
| 528 | 480 | t = mPageTable.back()->data(); | |
| 529 |
2/2✓ Branch 0 taken 117120 times.
✓ Branch 1 taken 240 times.
|
234720 | for (size_t i=mSize+size-mCapacity; i; --i) *t++ = *s++; |
| 530 | 480 | mCapacity += Page::Size; | |
| 531 | } | ||
| 532 | } else {//page table is full so simply append page | ||
| 533 | 16 | mPageTable.push_back( page ); | |
| 534 | 16 | mCapacity += Page::Size; | |
| 535 | 16 | page = nullptr; | |
| 536 | } | ||
| 537 | mSize += size; | ||
| 538 | } | ||
| 539 | |||
| 540 | //////////////////////////////////////////////////////////////////////////////// | ||
| 541 | |||
| 542 | // Public member-class of PagedArray | ||
| 543 | template <typename ValueT, size_t Log2PageSize> | ||
| 544 | class PagedArray<ValueT, Log2PageSize>:: | ||
| 545 | ValueBuffer | ||
| 546 | { | ||
| 547 | public: | ||
| 548 | using PagedArrayType = PagedArray<ValueT, Log2PageSize>; | ||
| 549 | /// @brief Constructor from a PageArray | ||
| 550 | 524 | ValueBuffer(PagedArray& parent) : mParent(&parent), mPage(new Page()), mSize(0) {} | |
| 551 | /// @warning This copy-constructor is shallow in the sense that no | ||
| 552 | /// elements are copied, i.e. size = 0. | ||
| 553 | 3 | ValueBuffer(const ValueBuffer& other) : mParent(other.mParent), mPage(new Page()), mSize(0) {} | |
| 554 | /// @brief Destructor that transfers an buffered values to the parent PagedArray. | ||
| 555 |
2/2✓ Branch 1 taken 260 times.
✓ Branch 2 taken 5 times.
|
530 | ~ValueBuffer() { mParent->add(mPage, mSize); delete mPage; } |
| 556 | |||
| 557 | ValueBuffer& operator=(const ValueBuffer&) = delete;// disallow copy assignment | ||
| 558 | |||
| 559 | /// @brief Add a value to the buffer and increment the size. | ||
| 560 | /// | ||
| 561 | /// @details If the internal memory page is full it will | ||
| 562 | /// automaically flush the page to the parent PagedArray. | ||
| 563 | 2960004 | void push_back(const ValueT& v) { | |
| 564 | 2960004 | (*mPage)[mSize++] = v; | |
| 565 | 65900 | if (mSize == Page::Size) this->flush(); | |
| 566 | 2960004 | } | |
| 567 | /// @brief Manually transfers the values in this buffer to the parent PagedArray. | ||
| 568 | /// | ||
| 569 | /// @note This method is also called by the destructor and | ||
| 570 | /// push_back so it should only be called if one manually wants to | ||
| 571 | /// sync up the buffer with the array, e.g. during debugging. | ||
| 572 | 65906 | void flush() { | |
| 573 | 65906 | mParent->add(mPage, mSize); | |
| 574 |
2/2✓ Branch 0 taken 32952 times.
✓ Branch 1 taken 1 times.
|
65906 | if (mPage == nullptr) mPage = new Page(); |
| 575 | 65906 | mSize = 0; | |
| 576 | 65906 | } | |
| 577 | /// @brief Return a reference to the parent PagedArray | ||
| 578 | PagedArrayType& parent() const { return *mParent; } | ||
| 579 | /// @brief Return the current number of elements cached in this buffer. | ||
| 580 | size_t size() const { return mSize; } | ||
| 581 | static size_t pageSize() { return 1UL << Log2PageSize; } | ||
| 582 | private: | ||
| 583 | PagedArray* mParent; | ||
| 584 | Page* mPage; | ||
| 585 | size_t mSize; | ||
| 586 | };// Public class PagedArray::ValueBuffer | ||
| 587 | |||
| 588 | //////////////////////////////////////////////////////////////////////////////// | ||
| 589 | |||
| 590 | // Const std-compliant iterator | ||
| 591 | // Public member-class of PagedArray | ||
| 592 | template <typename ValueT, size_t Log2PageSize> | ||
| 593 | class PagedArray<ValueT, Log2PageSize>:: | ||
| 594 | ConstIterator : public std::iterator<std::random_access_iterator_tag, ValueT> | ||
| 595 | { | ||
| 596 | public: | ||
| 597 | using BaseT = std::iterator<std::random_access_iterator_tag, ValueT>; | ||
| 598 | using difference_type = typename BaseT::difference_type; | ||
| 599 | // constructors and assignment | ||
| 600 | ConstIterator() : mPos(0), mParent(nullptr) {} | ||
| 601 | ConstIterator(const PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} | ||
| 602 | ConstIterator(const ConstIterator& other) : mPos(other.mPos), mParent(other.mParent) {} | ||
| 603 | ConstIterator& operator=(const ConstIterator& other) { | ||
| 604 | mPos=other.mPos; | ||
| 605 | mParent=other.mParent; | ||
| 606 | return *this; | ||
| 607 | } | ||
| 608 | // prefix | ||
| 609 | ConstIterator& operator++() { ++mPos; return *this; } | ||
| 610 | ConstIterator& operator--() { --mPos; return *this; } | ||
| 611 | // postfix | ||
| 612 | ConstIterator operator++(int) { ConstIterator tmp(*this); ++mPos; return tmp; } | ||
| 613 | ConstIterator operator--(int) { ConstIterator tmp(*this); --mPos; return tmp; } | ||
| 614 | // value access | ||
| 615 | const ValueT& operator*() const { return (*mParent)[mPos]; } | ||
| 616 | const ValueT* operator->() const { return &(this->operator*()); } | ||
| 617 | const ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } | ||
| 618 | // offset | ||
| 619 | ConstIterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } | ||
| 620 | ConstIterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } | ||
| 621 | ConstIterator operator+(const difference_type &pos) const { return Iterator(*mParent,mPos+pos); } | ||
| 622 | ConstIterator operator-(const difference_type &pos) const { return Iterator(*mParent,mPos-pos); } | ||
| 623 | difference_type operator-(const ConstIterator& other) const { return mPos - other.pos(); } | ||
| 624 | // comparisons | ||
| 625 | bool operator==(const ConstIterator& other) const { return mPos == other.mPos; } | ||
| 626 | bool operator!=(const ConstIterator& other) const { return mPos != other.mPos; } | ||
| 627 | bool operator>=(const ConstIterator& other) const { return mPos >= other.mPos; } | ||
| 628 | bool operator<=(const ConstIterator& other) const { return mPos <= other.mPos; } | ||
| 629 | bool operator< (const ConstIterator& other) const { return mPos < other.mPos; } | ||
| 630 | bool operator> (const ConstIterator& other) const { return mPos > other.mPos; } | ||
| 631 | // non-std methods | ||
| 632 | bool isValid() const { return mParent != nullptr && mPos < mParent->size(); } | ||
| 633 | size_t pos() const { return mPos; } | ||
| 634 | private: | ||
| 635 | size_t mPos; | ||
| 636 | const PagedArray* mParent; | ||
| 637 | };// Public class PagedArray::ConstIterator | ||
| 638 | |||
| 639 | //////////////////////////////////////////////////////////////////////////////// | ||
| 640 | |||
| 641 | // Non-const std-compliant iterator | ||
| 642 | // Public member-class of PagedArray | ||
| 643 | template <typename ValueT, size_t Log2PageSize> | ||
| 644 | class PagedArray<ValueT, Log2PageSize>:: | ||
| 645 | Iterator : public std::iterator<std::random_access_iterator_tag, ValueT> | ||
| 646 | { | ||
| 647 | public: | ||
| 648 | using BaseT = std::iterator<std::random_access_iterator_tag, ValueT>; | ||
| 649 | using difference_type = typename BaseT::difference_type; | ||
| 650 | // constructors and assignment | ||
| 651 | Iterator() : mPos(0), mParent(nullptr) {} | ||
| 652 | 6 | Iterator(PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} | |
| 653 |
2/6✓ Branch 94 taken 1 times.
✗ Branch 95 not taken.
✓ Branch 101 taken 1 times.
✗ Branch 102 not taken.
✗ Branch 108 not taken.
✗ Branch 109 not taken.
|
10895770 | Iterator(const Iterator& other) : mPos(other.mPos), mParent(other.mParent) {} |
| 654 | Iterator& operator=(const Iterator& other) { | ||
| 655 | 761053 | mPos=other.mPos; | |
| 656 | 72369 | mParent=other.mParent; | |
| 657 | return *this; | ||
| 658 | } | ||
| 659 | // prefix | ||
| 660 | 5643892 | Iterator& operator++() { ++mPos; return *this; } | |
| 661 | 5983201 | Iterator& operator--() { --mPos; return *this; } | |
| 662 | // postfix | ||
| 663 | Iterator operator++(int) { Iterator tmp(*this); ++mPos; return tmp; } | ||
| 664 | Iterator operator--(int) { Iterator tmp(*this); --mPos; return tmp; } | ||
| 665 | // value access | ||
| 666 | 21395190 | ValueT& operator*() const { return (*mParent)[mPos]; } | |
| 667 | ValueT* operator->() const { return &(this->operator*()); } | ||
| 668 | 5450691 | ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } | |
| 669 | // offset | ||
| 670 | Iterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } | ||
| 671 | Iterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } | ||
| 672 |
2/6✗ Branch 59 not taken.
✗ Branch 60 not taken.
✓ Branch 63 taken 3 times.
✗ Branch 64 not taken.
✓ Branch 67 taken 1 times.
✗ Branch 68 not taken.
|
2945626 | Iterator operator+(const difference_type &pos) const { return Iterator(*mParent, mPos+pos); } |
| 673 | 844422 | Iterator operator-(const difference_type &pos) const { return Iterator(*mParent, mPos-pos); } | |
| 674 |
3/6✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1 times.
|
290 | difference_type operator-(const Iterator& other) const { return mPos - other.pos(); } |
| 675 | // comparisons | ||
| 676 |
2/6✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
285 | bool operator==(const Iterator& other) const { return mPos == other.mPos; } |
| 677 |
21/32✓ Branch 0 taken 255990 times.
✓ Branch 1 taken 233 times.
✓ Branch 2 taken 516063 times.
✓ Branch 3 taken 481 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 253570 times.
✓ Branch 7 taken 143 times.
✓ Branch 8 taken 2145 times.
✓ Branch 9 taken 143 times.
✓ Branch 10 taken 509588 times.
✓ Branch 11 taken 142 times.
✓ Branch 12 taken 2130 times.
✓ Branch 13 taken 142 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 18 taken 143 times.
✗ Branch 19 not taken.
✓ Branch 20 taken 142 times.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 27 times.
✓ Branch 27 taken 3 times.
✓ Branch 28 taken 9 times.
✓ Branch 29 taken 1 times.
✓ Branch 30 taken 100000 times.
✓ Branch 31 taken 1 times.
|
1641097 | bool operator!=(const Iterator& other) const { return mPos != other.mPos; } |
| 678 | bool operator>=(const Iterator& other) const { return mPos >= other.mPos; } | ||
| 679 | bool operator<=(const Iterator& other) const { return mPos <= other.mPos; } | ||
| 680 |
6/18✓ Branch 0 taken 16241 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 56128 times.
✓ Branch 5 taken 505955 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 910 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
|
579238 | bool operator< (const Iterator& other) const { return mPos < other.mPos; } |
| 681 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
5 | bool operator> (const Iterator& other) const { return mPos > other.mPos; } |
| 682 | // non-std methods | ||
| 683 | bool isValid() const { return mParent != nullptr && mPos < mParent->size(); } | ||
| 684 |
28/58✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 398 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 47 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 9 taken 103 times.
✓ Branch 10 taken 2 times.
✓ Branch 11 taken 2 times.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✓ Branch 19 taken 48 times.
✓ Branch 20 taken 1 times.
✓ Branch 21 taken 43 times.
✓ Branch 22 taken 1 times.
✓ Branch 23 taken 104 times.
✓ Branch 24 taken 1 times.
✓ Branch 25 taken 93 times.
✓ Branch 26 taken 1 times.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 32 not taken.
✗ Branch 33 not taken.
✓ Branch 35 taken 910 times.
✗ Branch 36 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✓ Branch 42 taken 116982 times.
✓ Branch 43 taken 910 times.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✓ Branch 49 taken 143 times.
✗ Branch 50 not taken.
✓ Branch 51 taken 16241 times.
✓ Branch 52 taken 16384 times.
✓ Branch 53 taken 142 times.
✗ Branch 54 not taken.
✓ Branch 55 taken 57038 times.
✓ Branch 56 taken 55360 times.
✗ Branch 57 not taken.
✗ Branch 58 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✓ Branch 62 taken 1 times.
✗ Branch 63 not taken.
✓ Branch 65 taken 1 times.
✗ Branch 66 not taken.
✗ Branch 68 not taken.
✗ Branch 69 not taken.
|
454931 | size_t pos() const { return mPos; } |
| 685 | private: | ||
| 686 | size_t mPos; | ||
| 687 | PagedArray* mParent; | ||
| 688 | };// Public class PagedArray::Iterator | ||
| 689 | |||
| 690 | //////////////////////////////////////////////////////////////////////////////// | ||
| 691 | |||
| 692 | // Private member-class of PagedArray implementing a memory page | ||
| 693 | template <typename ValueT, size_t Log2PageSize> | ||
| 694 | class PagedArray<ValueT, Log2PageSize>:: | ||
| 695 | Page | ||
| 696 | { | ||
| 697 | public: | ||
| 698 | static const size_t Size = 1UL << Log2PageSize; | ||
| 699 | static const size_t Mask = Size - 1UL; | ||
| 700 | static size_t memUsage() { return sizeof(ValueT)*Size; } | ||
| 701 | // Raw memory allocation without any initialization | ||
| 702 |
14/32✓ Branch 1 taken 32000 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✓ Branch 12 taken 196 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 196 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 240 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 32000 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 32000 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 195 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 2 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 259 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 757 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 3 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 252 times.
✗ Branch 47 not taken.
✓ Branch 50 taken 100 times.
✗ Branch 51 not taken.
|
66005 | Page() : mData(reinterpret_cast<ValueT*>(new char[sizeof(ValueT)*Size])) {} |
| 703 |
6/16✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 8 taken 1253 times.
✗ Branch 9 not taken.
✓ Branch 12 taken 492 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 1 times.
✗ Branch 17 not taken.
✓ Branch 20 taken 258 times.
✗ Branch 21 not taken.
✓ Branch 24 taken 1 times.
✗ Branch 25 not taken.
✓ Branch 28 taken 64000 times.
✗ Branch 29 not taken.
|
66005 | ~Page() { delete [] mData; } |
| 704 | Page(const Page&) = delete;//copy construction is not implemented | ||
| 705 | Page& operator=(const Page&) = delete;//copy assignment is not implemented | ||
| 706 |
6/6✓ Branch 0 taken 32000 times.
✓ Branch 1 taken 224000 times.
✓ Branch 2 taken 194 times.
✓ Branch 3 taken 199806 times.
✓ Branch 4 taken 756 times.
✓ Branch 5 taken 1023246 times.
|
1837029 | ValueT& operator[](const size_t i) { return mData[i & Mask]; } |
| 707 | const ValueT& operator[](const size_t i) const { return mData[i & Mask]; } | ||
| 708 | void fill(const ValueT& v) { | ||
| 709 | 251 | ValueT* dst = mData; | |
| 710 |
2/2✓ Branch 0 taken 257024 times.
✓ Branch 1 taken 251 times.
|
257275 | for (size_t i=Size; i; --i) *dst++ = v; |
| 711 | } | ||
| 712 |
4/12✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 3 times.
✓ Branch 9 taken 249 times.
✓ Branch 10 taken 240 times.
✓ Branch 11 taken 12 times.
|
495 | ValueT* data() { return mData; } |
| 713 | // Copy the first n elements of this Page to dst (which is assumed to large | ||
| 714 | // enough to hold the n elements). | ||
| 715 | void copy(ValueType *dst, size_t n) const { | ||
| 716 | const ValueT* src = mData; | ||
| 717 | for (size_t i=n; i; --i) *dst++ = *src++; | ||
| 718 | } | ||
| 719 | protected: | ||
| 720 | ValueT* mData; | ||
| 721 | };// Private class PagedArray::Page | ||
| 722 | |||
| 723 | //////////////////////////////////////////////////////////////////////////////// | ||
| 724 | |||
| 725 | } // namespace util | ||
| 726 | } // namespace OPENVDB_VERSION_NAME | ||
| 727 | } // namespace openvdb | ||
| 728 | |||
| 729 | #endif // OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED | ||
| 730 |