Line data Source code
1 : //* This file is part of the MOOSE framework 2 : //* https://mooseframework.inl.gov 3 : //* 4 : //* All rights reserved, see COPYRIGHT for full restrictions 5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT 6 : //* 7 : //* Licensed under LGPL 2.1, please see LICENSE for details 8 : //* https://www.gnu.org/licenses/lgpl-2.1.html 9 : 10 : #pragma once 11 : 12 : #include "MooseError.h" 13 : 14 : #include <vector> 15 : 16 : namespace MooseUtils 17 : { 18 : 19 : /** 20 : * Base class for a buffer. 21 : * 22 : * Enables the controlled access of an underlying raw vector 23 : * for storage, which can be used to limit memory allocation 24 : * and copies for various buffer types while providing a 25 : * useful public API. 26 : */ 27 : template <typename T> 28 : class Buffer 29 : { 30 : public: 31 : typedef typename std::vector<T>::iterator iterator; 32 : typedef typename std::vector<T>::const_iterator const_iterator; 33 : 34 : /** 35 : * Create an empty buffer 36 : */ 37 : Buffer(); 38 : 39 : /** 40 : * Create a buffer with a specific capacity 41 : */ 42 : Buffer(const std::size_t capacity); 43 : 44 2 : virtual ~Buffer() {} 45 : 46 : /** 47 : * Resize the capacity 48 : */ 49 2 : void setCapacity(const std::size_t capacity) { this->_data.resize(capacity); } 50 : /** 51 : * Get the capacity 52 : */ 53 14 : std::size_t capacity() const { return this->_data.size(); } 54 : 55 : /** 56 : * Set the size 57 : */ 58 : void setSize(const std::size_t size) { this->_end_pos = this->newEnd(this->_begin_pos + size); } 59 : /** 60 : * Get the size 61 : */ 62 52 : std::size_t size() const { return this->_end_pos - this->_begin_pos; } 63 : 64 : /** 65 : * Whether or not the buffer is empty 66 : */ 67 6 : bool empty() const { return !size(); } 68 : 69 : /** 70 : * Add a new entry on the end 71 : */ 72 : void push_back(const T & value); 73 : 74 : /** 75 : * Moves the object into the buffer (calls std::move()) 76 : */ 77 : void move(T & value); 78 : 79 : /** 80 : * Add new entries to the end 81 : * 82 : * Everything in [in_begin, in_end) is appended 83 : */ 84 : void append(const_iterator in_begin, const_iterator in_end); 85 : /** 86 : * Add new entries to the end 87 : */ 88 : void append(const std::vector<T> & vals); 89 : 90 : /** 91 : * Remove all entries (does not change the capacity) 92 : * Note: this does NOT at all free any entries 93 : */ 94 : void clear(); 95 : 96 : /** 97 : * Remove the first num elements 98 : * 99 : * Note that erased items are not guaranteed to be freed immediately 100 : */ 101 : virtual void erase(const std::size_t num) = 0; 102 : /* 103 : * Similar to erase(), but if the chunk size is larger than the current size, 104 : * no error (all elements are erased gracefully). 105 : * 106 : * Note that erased items are not guaranteed to be freed immediately 107 : */ 108 : virtual void eraseChunk(const std::size_t chunk_size) = 0; 109 : 110 : /** 111 : * Iterator for the first entry in the buffer 112 : */ 113 17 : iterator begin() { return this->_data.begin() + this->_begin_pos; } 114 : /** 115 : * Const iterator for the first entry in the buffer 116 : */ 117 0 : const_iterator begin() const { return this->_data.begin() + this->_begin_pos; } 118 : 119 : /** 120 : * Iterator for the last entry in the buffer 121 : */ 122 29 : iterator end() { return this->_data.begin() + this->_end_pos; } 123 : /** 124 : * Const iterator for the last entry in the buffer 125 : */ 126 0 : const_iterator end() const { return this->_data.begin() + this->_end_pos; } 127 : 128 : /** 129 : * Iterator for the first entry with a given chunk size in the buffer 130 : * If chunk_size is greater than the size of the buffer, the full range will be given. 131 : */ 132 : virtual iterator beginChunk(const std::size_t chunk_size) = 0; 133 : /** 134 : * Const iterator for the first entry with a given chunk size in the buffer 135 : * If chunk_size is greater than the size of the buffer, the full range will be given. 136 : */ 137 : virtual const_iterator beginChunk(const std::size_t chunk_size) const = 0; 138 : 139 : /** 140 : * Iterator for the last entry of a chunk size in the buffer 141 : * If chunk_size is greater than the size of the buffer, the full range will be given. 142 : */ 143 : virtual iterator endChunk(const std::size_t chunk_size) = 0; 144 : /** 145 : * Const iterator for the last entry of a chunk size in the buffer 146 : * If chunk_size is greater than the size of the buffer, the full range will be given. 147 : */ 148 : virtual const_iterator endChunk(const std::size_t chunk_size) const = 0; 149 : 150 : /** 151 : * Access an entry at index 152 : */ 153 : T & operator[](const std::size_t index); 154 : /** 155 : * Const access an entry at an index 156 : */ 157 : const T & operator[](const std::size_t index) const; 158 : 159 : /** 160 : * Use in_data as our data vector 161 : */ 162 : void swap(std::vector<T> & in_data); 163 : 164 : /** 165 : * Access the raw underlying storage. 166 : * 167 : * This is considered an advanced interface. Typically, you 168 : * should use the begin(), beginChunk(), end(), and endChunk() 169 : * methods for accessing the data. This should really only 170 : * be used in unit tests for verifying the underlying storage. 171 : */ 172 2 : const std::vector<T> & data() { return _data; } 173 : 174 : /** 175 : * The current beginning position of the buffer in data(). 176 : * 177 : * This is considered an advanced interface because data() 178 : * is the internal storage for the buffer. It should really 179 : * only be used in unit tests for verifying the underlying 180 : * storage. 181 : */ 182 26 : std::size_t dataBeginPos() const { return _begin_pos; } 183 : 184 : /** 185 : * The current end position of the buffer in data(). 186 : * 187 : * This is considered an advanced interface because data() 188 : * is the internal storage for the buffer. It should really 189 : * only be used in unit tests for verifying the underlying 190 : * storage. 191 : */ 192 24 : std::size_t dataEndPos() const { return _end_pos; } 193 : 194 : protected: 195 : /** 196 : * Find out where the new end will be. 197 : * This will resize/copy data as necessary 198 : * 199 : * @param new_end the proposed new_end position 200 : * @return The actual position of the new ending 201 : */ 202 : virtual std::size_t newEnd(const std::size_t new_end) = 0; 203 : 204 : /// The raw data 205 : std::vector<T> _data; 206 : 207 : /// The beginning position 208 : std::size_t _begin_pos; 209 : /// The ending position 210 : std::size_t _end_pos; 211 : }; 212 : 213 : template <typename T> 214 : Buffer<T>::Buffer() : _begin_pos(0), _end_pos(0) 215 : { 216 : } 217 : 218 : template <typename T> 219 2 : Buffer<T>::Buffer(const std::size_t capacity) : _data(capacity), _begin_pos(0), _end_pos(0) 220 : { 221 2 : } 222 : 223 : template <typename T> 224 : void 225 76 : Buffer<T>::push_back(const T & value) 226 : { 227 76 : this->_end_pos = newEnd(this->_end_pos + 1); 228 76 : this->_data[this->_end_pos - 1] = value; 229 76 : } 230 : 231 : template <typename T> 232 : void 233 : Buffer<T>::move(T & value) 234 : { 235 : this->_end_pos = newEnd(this->_end_pos + 1); 236 : this->_data[this->_end_pos - 1] = std::move(value); 237 : } 238 : 239 : template <typename T> 240 : void 241 2 : Buffer<T>::append(const_iterator in_begin, const_iterator in_end) 242 : { 243 2 : const auto additional_size = std::distance(in_begin, in_end); 244 2 : if (additional_size == 0) 245 0 : return; 246 : 247 2 : this->_end_pos = this->newEnd(this->_end_pos + additional_size); 248 2 : std::copy(in_begin, in_end, this->end() - additional_size); 249 : } 250 : 251 : template <typename T> 252 : void 253 2 : Buffer<T>::append(const std::vector<T> & vals) 254 : { 255 2 : this->append(vals.begin(), vals.end()); 256 2 : } 257 : 258 : template <typename T> 259 : void 260 6 : Buffer<T>::clear() 261 : { 262 6 : this->_begin_pos = 0; 263 6 : this->_end_pos = 0; 264 6 : } 265 : 266 : template <typename T> 267 : T & 268 22 : Buffer<T>::operator[](const std::size_t index) 269 : { 270 : mooseAssert(this->_begin_pos + index < this->_end_pos, "Attempt to access off end of Buffer!"); 271 22 : return this->_data[this->_begin_pos + index]; 272 : } 273 : 274 : template <typename T> 275 : const T & 276 : Buffer<T>::operator[](const std::size_t index) const 277 : { 278 : mooseAssert(this->_begin_pos + index < this->_end_pos, "Attempt to access off end of Buffer!"); 279 : return this->_data[this->_begin_pos + index]; 280 : } 281 : 282 : template <typename T> 283 : void 284 2 : Buffer<T>::swap(std::vector<T> & in_data) 285 : { 286 2 : std::swap(in_data, _data); 287 2 : this->_begin_pos = 0; 288 2 : this->_end_pos = this->_data.size(); 289 2 : } 290 : 291 : }