HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NET_CircularBuffer.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: NET_CircularBuffer.h
7  *
8  * COMMENTS:
9  *
10  */
11 
12 #ifndef __NET_CIRCULARBUFFER_H__
13 #define __NET_CIRCULARBUFFER_H__
14 
15 #include "NET_API.h"
16 
17 #include <UT/UT_Array.h>
18 #include <UT/UT_Format.h>
19 #include <UT/UT_Debug.h>
20 
21 #include <SYS/SYS_Compiler.h>
22 #include <SYS/SYS_Types.h>
23 
24 #include <utility>
25 #include <cstring>
26 
27 template <typename T>
29 {
30 public:
31  using index_t = exint;
32  using range_t = std::pair<T*, int>;
33  using const_range_t = std::pair<const T*, int>;
34 
36  {
37  clear();
38  }
39 
40  template <typename U>
42  {
43  public:
45 
46  base_iterator(const range_t& one, const range_t& two) :
47  myArrayOne(one), myArrayTwo(two), myCurrent(0)
48  {}
49  base_iterator(exint current) :
50  myArrayOne(), myArrayTwo(), myCurrent(current)
51  {}
52 
53  U* operator->() const
54  {
55  return this->item_(myCurrent);
56  }
57  U& operator*() const
58  {
59  return *(this->item_(myCurrent));
60  }
61  U& operator[](exint idx) const
62  {
63  return *(this->item_(idx));
64  }
66  {
67  ++myCurrent;
68  return *this;
69  }
71  {
72  base_iterator tmp = *this;
73  ++myCurrent;
74  return tmp;
75  }
77  {
78  --myCurrent;
79  return *this;
80  }
82  {
83  base_iterator tmp = *this;
84  --myCurrent;
85  return tmp;
86  }
88  {
89  myCurrent += n;
90  return *this;
91  }
93  {
94  base_iterator tmp = *this;
95  tmp.myCurrent += n;
96  return tmp;
97  }
99  {
100  return (*this) += (-n);
101  }
103  {
104  return (*this) + (-n);
105  }
106  bool operator==(const base_iterator& it) const
107  {
108  return myCurrent == it.myCurrent;
109  }
110  bool operator!=(const base_iterator& it) const { return !(*this == it); }
111  bool operator<(const base_iterator& it) const
112  {
113  return myCurrent < it.myCurrent;
114  }
115  bool operator<=(const base_iterator& it) const
116  {
117  return !(it < *this);
118  }
119  bool operator>(const base_iterator& it) const
120  {
121  return it < *this;
122  }
123  bool operator>=(const base_iterator& it) const
124  {
125  return !(*this < it);
126  }
127  exint operator-(const base_iterator& it) const
128  {
129  return myCurrent - it.myCurrent;
130  }
131 
132  private:
133  U* item_(exint idx) const
134  {
135  exint count = myArrayOne.second + myArrayTwo.second;
136  UT_ASSERT(myCurrent <= count);
137 
138  if (myCurrent > count)
139  return nullptr;
140 
141  if (myCurrent > myArrayOne.second)
142  {
143  exint idx = myCurrent - myArrayOne.second;
144  return myArrayTwo.first + idx;
145  }
146 
147  return myArrayOne.first + myCurrent;
148  }
149 
150  range_t myArrayOne;
151  range_t myArrayTwo;
152  exint myCurrent;
153  };
154 
155  using iterator = base_iterator<T>;
156  using const_iterator = base_iterator<const T>;
157 
158  /// @brief The first element in the buffer. Undefined behaviour if the buffer
159  /// is empty.
160  ///
161  /// @return The first element in the buffer
162  T& front() { return myData[firstIndex_()]; }
163  /// @brief The last element in the buffer. Undefined behaviour if the buffer
164  /// is empty.
165  ///
166  /// @return The last element in the buffer. Undefined behaviour if the buffer
167  /// is empty.
168  T& back() { return myData[lastIndex_()]; }
169  /// @brief The first element in the buffer. Undefined behaviour if the buffer
170  /// is empty.
171  ///
172  /// @return The first element in the buffer.
173  const T& front() const { return myData[firstIndex_()]; }
174  /// @brief The last element in the buffer. Undefined behaviour if the buffer
175  /// is empty.
176  ///
177  /// @return The last element in the buffer.
178  const T& back() const { return myData[lastIndex_()]; }
179 
180  /// @brief The begin iterator for the buffer.
181  ///
182  /// @return The begin iterator
184  /// @brief The end iterator for the buffer.
185  ///
186  /// @return The end iterator.
187  iterator end() { return iterator(size()); }
188  /// @brief The begin const iterator for the buffer.
189  ///
190  /// @return The begin const iterator.
192  {
193  return const_base_iterator(arrayOne(), arrayTwo());
194  }
195  /// @brief The end const iterator for the buffer.
196  ///
197  /// @return The end const iterator.
199  {
200  return const_base_iterator(size());
201  }
202  /// @brief Retrieve element at provided index.
203  ///
204  /// @param index The index to retrieve the element at.
205  ///
206  /// @return The element at index.
208  {
209  UT_ASSERT_P(!isEmpty());
210  index_t i = (firstIndex_() + index) % capacity();
211  return myData[i];
212  }
213  /// @brief Retrieve element at provided index.
214  ///
215  /// @param index The index to retrieve the element at.
216  ///
217  /// @return The element at index.
218  const T& operator[](int index) const
219  {
220  UT_ASSERT_P(!isEmpty());
221  index_t i = (firstIndex_() + index) % capacity();
222  return myData[i];
223  }
224 
225  /// @brief Place a single element onto the back of the buffer.
226  ///
227  /// @param data
228  void push(const T& data)
229  {
230  push(&data, 1);
231  }
232  /// @brief Place an array of elements onto the back of the buffer.
233  ///
234  /// @param data The array of elements to add to the buffer.
235  /// @param count The number of elements in the array to add.
236  void push(const T* data, exint count)
237  {
238  growIfNeeded_(count);
239 
240  UT_ASSERT(!isFull());
241  UT_ASSERT(size() + count <= maxSize());
242 
243  exint start_idx = mask_(myWrite);
244  exint end_idx = start_idx + count;
245 
246  T* start = myData.getArray() + start_idx;
247  const T* data_start = data;
248 
249  exint full_count = count;
250 
251  // Check if we need to split up the copies into two. The first being
252  // at the tail end and the second being at the beginning of the
253  // underlying array.
254  if (end_idx > capacity())
255  {
256  // Grab the chunk of data to copy
257  exint chunk = capacity() - start_idx;
258  UT_ASSERT(chunk > 0);
259  SYS_ASSUME(chunk > 0); // suppress gcc warnings
260  std::memcpy(start, data_start, sizeof(T) * chunk);
261 
262  full_count -= chunk;
263  start = myData.data();
264  data_start += chunk;
265  }
266 
267  // If there is anything else that needs to be copied do that at the
268  // beginning of the underlying array.
269  if (full_count > 0)
270  {
271  std::memcpy(start, data_start, sizeof(T) * full_count);
272  }
273 
274  incrementWrite_(count);
275  }
276  /// @brief Remove a single element from the front of the buffer.
277  ///
278  /// @return The element removed.
279  T pop()
280  {
281  UT_ASSERT(!isEmpty());
282  T data = myData[myRead];
283  incrementRead_(1);
284  return data;
285  }
286  /// @brief Remove multiple elements from the front of the buffer.
287  ///
288  /// @param count The number of elements to remove.
289  void pop(int count)
290  {
291  incrementRead_(count);
292  }
293 
294  /// @brief Is the buffer currently full.
295  ///
296  /// @return True if the buffer is currently full. Any new push() calls will
297  /// allocate more space.
298  bool isFull() const { return increment_(myWrite) == myRead; }
299  /// @brief Is the buffer currently empty.
300  ///
301  /// @return True if the buffer currently does not store any elements.
302  bool isEmpty() const { return myRead == myWrite; }
303  /// @brief The number of elements currently stored in the buffer.
304  ///
305  /// @return The current size of the buffer.
306  exint size() const
307  {
308  if (myWrite >= myRead)
309  return myWrite - myRead;
310  return (capacity() - myRead) + myWrite;
311  }
312  /// @brief The number of elements currently stored in the buffer.
313  ///
314  /// @return The current size of the buffer.
315  int entries() const { return size(); }
316  /// @brief The current capacity of the buffer.
317  ///
318  /// @return The total capacity of the buffer.
319  exint capacity() const { return myData.capacity(); }
320  /// @brief The absolute maximum size of the buffer. Use size() if you need
321  /// to know the current size of the buffer.
322  ///
323  /// @return The maximum size of the buffer.
325 
326  /// @brief A circular buffer can wrap around in memory. This is the first
327  /// array of continious memory. If the buffer is not currently wrapped then
328  /// the entire buffer will be present in this array and the second array
329  /// will be empty.
330  ///
331  /// @return A pair of the start of the array and the length of the first
332  /// array.
334  {
335  exint start = firstIndex_();
336  exint end = mask_(myWrite);
337  if (end < start)
338  end = capacity();
339 
340  range_t result = std::make_pair(myData.data() + start, end - start);
341  return result;
342  }
343  /// @brief Holds information about the second array of contigous data. See
344  /// arrayOne() for further details.
345  ///
346  /// @return A pair of that start of the array and the length of the
347  /// second array.
349  {
350  exint first_index = firstIndex_();
351  exint last_index = mask_(myWrite);
352  if (isEmpty() || last_index >= first_index)
353  return std::make_pair(nullptr, 0);
354 
355  range_t result = std::make_pair(myData.data(), mask_(myWrite));
356  return result;
357  }
358  /// @brief Const variant of the arrayOne().
359  ///
360  /// @return See arrayOne()
362  {
363  return arrayOne();
364  }
365  /// @brief Const variant of the arrayTwo().
366  ///
367  /// @return See arrayTwo()
369  {
370  return arrayTwo();
371  }
372  /// @brief Completely clear the buffer. This will free any currently
373  /// allocated data.
374  void clear()
375  {
376  myRead = 0;
377  myWrite = 0;
378  myData.setCapacity(0);
379  }
380 
381  /// @brief Printout debug information about this buffer.
382  void debug() const
383  {
384  UTformat(stderr, "Capacity: {}\n", capacity());
385  UTformat(stderr, "Size: {}\n", size());
386  UTformat(stderr, "Read: {} idx={}\n", myRead, mask_(myRead));
387  UTformat(stderr, "Write: {} idx={}\n", myWrite, mask_(myWrite));
388  }
389 private:
390  /// @brief Calculate the index of a value.
391  ///
392  /// @param value The value to calculate the index.
393  ///
394  /// @return The index into the buffer.
395  index_t mask_(index_t value) const
396  {
397  exint last = myData.capacity() - 1;
398  if (value > last)
399  return value - last - 1;
400  return value;
401  }
402  /// @brief Check if we need to grow our buffer. This will check against the
403  /// current size and the extra data we need for some operation. If the extra
404  /// data we need isnt available we bump the size of our buffer.
405  ///
406  /// @param count The amount of data we need available for some operation.
407  void growIfNeeded_(index_t count)
408  {
409  if (size() + count >= capacity())
410  {
411  exint new_size = UTbumpAlloc(size() + count);
412  myData.setCapacity(new_size);
413  myData.entries(new_size);
414  }
415  }
416  /// @brief The first index into our buffer.
417  ///
418  /// @return The index of the first element in our buffer.
419  index_t firstIndex_() const
420  {
421  if (isEmpty())
422  return 0;
423 
424  return mask_(myRead);
425  }
426  /// @brief The last index into our buffer. This is the last index of an
427  /// element and not the last index of our buffer. Use mask_(myWrite) if you
428  /// need the first invalid index into the buffer (used for range iterators).
429  ///
430  /// @return The last index of a valid element in the buffer.
431  index_t lastIndex_() const
432  {
433  if (isEmpty())
434  return 0;
435 
436  return mask_(myWrite - 1);
437  }
438  index_t increment_(index_t index, index_t count = 1) const
439  {
440  return mask_(index + count);
441  }
442  void incrementRead_(index_t count)
443  {
444  myRead = increment_(myRead, count);
445  }
446  void incrementWrite_(index_t count)
447  {
448  myWrite = increment_(myWrite, count);
449  }
450 
451  UT_Array<T> myData;
452  index_t myRead;
453  index_t myWrite;
454 };
455 
456 #endif // __NET_CIRCULARBUFFER_H__
457 
base_iterator operator+(exint n) const
void debug() const
Printout debug information about this buffer.
bool operator>(const base_iterator &it) const
const_range_t arrayTwo() const
Const variant of the arrayTwo().
bool operator!=(const base_iterator &it) const
void clear()
Completely clear the buffer. This will free any currently allocated data.
GLboolean * data
Definition: glcorearb.h:131
T & back()
The last element in the buffer. Undefined behaviour if the buffer is empty.
GLuint start
Definition: glcorearb.h:475
GLsizei const GLfloat * value
Definition: glcorearb.h:824
const_iterator begin() const
The begin const iterator for the buffer.
bool operator<(const base_iterator &it) const
int64 exint
Definition: SYS_Types.h:125
iterator end()
The end iterator for the buffer.
T pop()
Remove a single element from the front of the buffer.
std::pair< const U *, int > const_range_t
**But if you need a result
Definition: thread.h:622
range_t arrayTwo()
Holds information about the second array of contigous data. See arrayOne() for further details...
void push(const T &data)
Place a single element onto the back of the buffer.
bool isEmpty() const
Is the buffer currently empty.
void push(const T *data, exint count)
Place an array of elements onto the back of the buffer.
const T & operator[](int index) const
Retrieve element at provided index.
base_iterator & operator+=(exint n)
base_iterator< U > iterator
base_iterator operator-(exint n) const
const_iterator end() const
The end const iterator for the buffer.
GLdouble n
Definition: glcorearb.h:2008
bool operator>=(const base_iterator &it) const
base_iterator(const range_t &one, const range_t &two)
#define UT_ASSERT_P(ZZ)
Definition: UT_Assert.h:164
iterator begin()
The begin iterator for the buffer.
GLuint GLuint end
Definition: glcorearb.h:475
exint capacity() const
The current capacity of the buffer.
bool isFull() const
Is the buffer currently full.
range_t arrayOne()
A circular buffer can wrap around in memory. This is the first array of continious memory...
#define SYS_ASSUME(EXPR)
Definition: SYS_Compiler.h:124
exint maxSize() const
The absolute maximum size of the buffer. Use size() if you need to know the current size of the buffe...
bool operator<=(const base_iterator &it) const
__hostdev__ uint64_t last(uint32_t i) const
Definition: NanoVDB.h:5976
bool operator==(const base_iterator &it) const
std::pair< U *, int > range_t
exint operator-(const base_iterator &it) const
const T & back() const
The last element in the buffer. Undefined behaviour if the buffer is empty.
base_iterator< const U > const_iterator
GLuint index
Definition: glcorearb.h:786
ImageBuf OIIO_API max(Image_or_Const A, Image_or_Const B, ROI roi={}, int nthreads=0)
Type-safe formatting, modeled on the Python str.format function.
size_t UTformat(FILE *file, const char *format, const Args &...args)
Definition: UT_Format.h:718
T & operator[](int index)
Retrieve element at provided index.
typename NET_CircularBuffer< U >::range_t range_t
exint size() const
The number of elements currently stored in the buffer.
base_iterator & operator-=(exint n)
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:165
T & front()
The first element in the buffer. Undefined behaviour if the buffer is empty.
const T & front() const
The first element in the buffer. Undefined behaviour if the buffer is empty.
void pop(int count)
Remove multiple elements from the front of the buffer.
int entries() const
The number of elements currently stored in the buffer.
GLint GLsizei count
Definition: glcorearb.h:405
const_range_t arrayOne() const
Const variant of the arrayOne().
Definition: format.h:1821