C++ API Reference RNBO: common/RNBO_List.h Source File

RNBO: common/RNBO_List.h Source File

1 #ifndef _RNBO_LIST_H_
2 #define _RNBO_LIST_H_
3 
4 #include "RNBO_Types.h"
5 #include "RNBO_Platform.h"
6 
7 #ifdef RNBO_NOSTL
8 #include "RNBO_UniquePtr.h"
9 #else
10 #include <memory>
11 #endif // RNBO_NOSTL
12 
13 #ifndef RNBO_FIXEDLISTSIZE
14 #define RNBO_FIXEDLISTSIZE 0
15 #endif
16 
17 namespace RNBO {
18 
19  template<class T, size_t N> class array;
20 
21  static const size_t listChunkSize = 8;
22 
35  template<typename T, size_t FIXED = RNBO_FIXEDLISTSIZE> class listbase {
36  public:
37 
42  : length(0, *this)
43  , _values(nullptr)
44  , _allocatedLength(FIXED)
45  {
46  allocate(0, length, false);
47  }
48 
59  template<typename... Ts> listbase(Ts ... args)
60  : length(sizeof...(args), *this)
61  , _values(nullptr)
62  , _allocatedLength(FIXED)
63  {
64  allocate(0, length, false);
65  T listValues[sizeof...(args)] = {static_cast<T>(args)...};
66  size_t i = 0;
67  if (FIXED) {
68  for (; i < length && i < FIXED; i++) {
69  _stackValues[i] = listValues[i];
70  }
71  }
72  for (; i < length; i++) {
73  _values[i] = listValues[i];
74  }
75  }
76 
77  // declaration of array constructor, definition is in RNBO_Array.h
78  template<typename TA, size_t NA>
79  listbase(const array<TA, NA>& arr);
80 
81  template<typename T1, size_t N1> listbase(const listbase<T1, N1>& l)
82  : length(l.length, *this)
83  , _values(nullptr)
84  , _allocatedLength(FIXED)
85  {
86  allocate(0, length, false);
87  size_t i = 0;
88  if (FIXED) {
89  for (; i < length; i++) {
90  _stackValues[i] = l[i];
91  }
92  }
93  for (; i < length; i++) {
94  _values[i] = l[i];
95  }
96  }
97 
108  listbase(const listbase &l)
109  : length(l.length, *this)
110  , _values(nullptr)
111  , _allocatedLength(FIXED)
112  {
113  allocate(0, length, false);
114  size_t i = 0;
115  if (FIXED) {
116  for (; i < length; i++) {
117  _stackValues[i] = l[i];
118  }
119  }
120  for (; i < length; i++) {
121  _values[i] = l[i];
122  }
123  }
124 
130  : length(l.length, *this)
131  , _values(l._values)
133  {
134  l._allocatedLength = 0;
135  l._values = nullptr;
136  }
137 
138 
143  {
144  if (_values) {
145  Platform::free(_values);
146  _values = nullptr;
147  }
148  }
149 
161  T& operator[](size_t i) {
162  if (i >= length) {
163  Platform::errorOrDefault(RuntimeError::OutOfRange, "list index out of range", false /*unused*/);
164  _dummy = static_cast<T>(0);
165  return _dummy;
166  }
167  if (FIXED) {
168  if (i < FIXED) return _stackValues[i];
169  else return _values[i - FIXED];
170  }
171  else
172  return _values[i];
173  }
174 
184  T operator[](size_t i) const {
185  if (i >= length) {
186  return Platform::errorOrDefault(RuntimeError::OutOfRange, "list index out of range", (T)0);
187  }
188  if (FIXED) {
189  if (i < FIXED) return _stackValues[i];
190  else return _values[i - FIXED];
191  }
192  else
193  return _values[i];
194  }
195 
196  listbase* operator->() {
197  return this;
198  }
199 
200  const listbase* operator->() const {
201  return this;
202  }
203 
208  {
209  if (&l != this) {
210  size_t oldLength = length;
211  length = l.length;
212  allocate(oldLength, length, false);
213  size_t i = 0;
214  if (FIXED) {
215  for (; i < length; i++) {
216  _stackValues[i] = l[i];
217  }
218  }
219  for (; i < length; i++) {
220  _values[i] = l[i];
221  }
222  }
223  return *this;
224  }
225 
226  template<size_t N1> listbase<T>& operator=(const listbase<T, N1>& l)
227  {
228  size_t oldLength = length;
229  length = l.length;
230  allocate(oldLength, length, false);
231  size_t i = 0;
232  if (FIXED) {
233  for (; i < length; i++) {
234  _stackValues[i] = l[i];
235  }
236  }
237  for (; i < length; i++) {
238  _values[i] = l[i];
239  }
240 
241  return *this;
242  }
243 
255  void push(T item) {
256  allocate(length, length + 1, true);
257  if (FIXED && length < FIXED) _stackValues[length] = item;
258  else _values[length - FIXED] = item;
259  length++;
260  }
261 
272  T pop() {
273  T tmp = 0;
274  if (length > 0) {
275  size_t lastIndex = length - 1;
276  if (FIXED && lastIndex < FIXED) tmp = _stackValues[lastIndex];
277  else tmp = _values[lastIndex - FIXED];
278  length--;
279  }
280  return tmp;
281  }
282 
291  T shift() {
292  if (length == 0) {
293  return Platform::errorOrDefault(RuntimeError::OutOfRange, "cannot shift out of empty list", 0);
294  }
295  T tmp;
296  if (FIXED) {
297  tmp = _stackValues[0];
298  Platform::memmove(_stackValues, _stackValues + 1, (FIXED - 1) * sizeof(T));
299  if (length > FIXED && _values) {
300  _stackValues[FIXED - 1] = _values[0];
301  Platform::memmove(_values, _values + 1, (length - FIXED - 1) * sizeof(T));
302  }
303  }
304  else {
305  tmp = _values[0];
306  Platform::memmove(_values, _values + 1, (length - 1) * sizeof(T));
307  }
308  length--;
309  return tmp;
310  }
311 
324  listbase concat(T item) const {
325  listbase tmp(*this);
326  tmp.push(item);
327  return tmp;
328  }
329 
342  listbase concat(const listbase& item) const {
343  listbase tmp(*this);
344  tmp.allocate(tmp.length, tmp.length + item.length, true);
345  for (size_t i = 0; i < item.length; i++) {
346  if (FIXED) {
347  tmp.length++;
348  tmp[tmp.length - 1] = item[i];
349  }
350  else {
351  tmp._values[tmp.length] = item[i];
352  tmp.length++;
353  }
354  }
355 
356  return tmp;
357  }
358 
368  listbase& fill(T value, size_t start = 0, size_t end = 0)
369  {
370  if (end == 0) end = length;
371  allocate(length, end, true);
372  if (end > length) length = end;
373  for (size_t i = start; i < end; i++) {
374  if (FIXED) {
375  (*this)[i] = value;
376  }
377  else {
378  _values[i] = value;
379  }
380  }
381 
382  return *this;
383  }
384 
391  template<typename... Ts> void unshift(Ts ... args) {
392  splice(0, 0, args...);
393  }
394 
405  template<typename... Ts> listbase<T> splice(Int start, size_t deleteCount, Ts ... args)
406  {
407  if (start < 0) start += length;
408  if (start < 0) start = 0;
409  size_t iStart = (size_t)start;
410  if (iStart >= length) {
411  deleteCount = 0;
412  iStart = length;
413  }
414 
415  if (iStart + deleteCount > length) deleteCount = length - iStart;
416 
417  const size_t addLength = sizeof...(args);
418  const long diff = (long)(addLength - deleteCount);
419 
420  listbase<T> deletedItems;
421  deletedItems.allocate(0, deleteCount, false);
422  for (size_t i = 0; i < deleteCount; i++) {
423  if (FIXED) {
424  T val = (*this)[iStart + i];
425  deletedItems.push(val);
426  }
427  else {
428  deletedItems.push(_values[iStart + i]);
429  }
430  }
431 
432  long newLength = (long)(length) + diff;
433  if (newLength <= 0) {
434  length = 0;
435  return deletedItems;
436  }
437 
438  allocate(length, static_cast<size_t>(newLength), true);
439 
440  if (diff < 0) {
441  RNBO_ASSERT(start - diff >= 0)
442  for (long i = (long)start - diff; i < (long)length; i++) {
443  RNBO_ASSERT(i + diff >= 0)
444  if (FIXED) {
445  (*this)[(size_t)(i + diff)] = (*this)[(size_t)i];
446  }
447  else {
448  _values[i + diff] = _values[i];
449  }
450  }
451  } else if (diff > 0) {
452  for (long i = (long)(length - 1); i >= (long)start; i--) {
453  RNBO_ASSERT(i + diff >= 0)
454  if (FIXED) {
455  (*this)[(size_t)(i + diff)] = (*this)[(size_t)i];
456  }
457  else {
458  _values[i + diff] = _values[i];
459  }
460  }
461  }
462 
463  // since allocating an array of 0 length is invalid, we always allocate at least length 1
464  T addValues[(sizeof...(args)) + 1] = {static_cast<T>(args)...};
465  RNBO_ASSERT(start >= 0)
466  for (size_t i = 0; i < addLength; i++) {
467  if (FIXED) {
468  (*this)[i + (size_t)start] = addValues[i];
469  }
470  else {
471  _values[i + (size_t)start] = addValues[i];
472  }
473  }
474 
475  length = static_cast<size_t>(newLength);
476 
477  return deletedItems;
478  }
479 
488  listbase slice(int start = 0, int end = 0) const {
489  listbase tmp;
490  int ilen = static_cast<int>(this->length);
491 
492  //negative is offset from end
493  if (start < 0) {
494  start = ilen + start;
495  if (start < 0) {
496  start = 0;
497  }
498  }
499 
500  //offset from end
501  if (end <= 0) {
502  end = ilen + end;
503  }
504 
505  //bounds check
506  if (start >= ilen || ilen == 0) {
507  return tmp;
508  }
509  if (end > ilen) {
510  end = ilen;
511  }
512 
513  //allocate and copy
514  tmp.allocate(0, static_cast<size_t>(end - start), false);
515  for (int i = start; i < end; i++) {
516  if (FIXED) {
517  tmp.length++;
518  tmp[tmp.length - 1] = (*this)[i];
519  }
520  else {
521  tmp._values[tmp.length] = _values[i];
522  tmp.length++;
523  }
524  }
525 
526  return tmp;
527  }
528 
536  bool includes(T value, int fromIndex = 0) const {
537  if (fromIndex < 0) {
538  fromIndex = int(length) + fromIndex;
539  if (fromIndex < 0) fromIndex = 0;
540  }
541 
542  for (size_t i = size_t(fromIndex); i < length; i++) {
543  if (FIXED) {
544  if ((*this)[i] == value) return true;
545  }
546  else {
547  if (_values[i] == value) return true;
548  }
549  }
550 
551  return false;
552  }
553 
563  int indexOf(T value, int fromIndex = 0) const {
564  if (fromIndex < 0) {
565  fromIndex = int(length) + fromIndex;
566  if (fromIndex < 0) fromIndex = 0;
567  }
568 
569  for (size_t i = size_t(fromIndex); i < length; i++) {
570  if (FIXED) {
571  if ((*this)[i] == value) return (int)i;
572  }
573  else {
574  if (_values[i] == value) return (int)i;
575  }
576  }
577 
578  return -1;
579  }
580 
587  size_t len2 = length >> 1;
588 
589  for (size_t i = 0; i < len2; ++i) {
590  if (FIXED) {
591  T tmp = (*this)[i];
592  size_t target = length - i - 1;
593  (*this)[i] = (*this)[target];
594  (*this)[target] = tmp;
595 
596  }
597  else {
598  T tmp = _values[i];
599  size_t target = length - i - 1;
600  _values[i] = _values[target];
601  _values[target] = tmp;
602  }
603  }
604 
605  return *this;
606  }
607 
613  void reserve(size_t size) {
614  allocate(length, size, true);
615  }
616 
617  protected:
618 
620  public:
621  ListLengthWrapper(size_t l, listbase& owner)
622  : _length(l)
623  , _owner(owner)
624  {}
625 
626  ListLengthWrapper(const ListLengthWrapper& l) = delete;
628 
629  operator size_t() const {
630  return _length;
631  }
632 
633  ListLengthWrapper& operator=(size_t l) {
634  size_t oldLength = _length;
635  _length = l;
636  if (_length < 0) _length = 0;
637  _owner->checkLength(oldLength);
638  return *this;
639  }
640 
641  ListLengthWrapper& operator=(const ListLengthWrapper& l) {
642  size_t oldLength = _length;
643  _length = l._length;
644  _owner->checkLength(oldLength);
645  return *this;
646  }
647 
648  void operator++() {
649  size_t oldLength = _length;
650  _length = _length + 1;
651  _owner->checkLength(oldLength);
652  }
653 
654  void operator--() {
655  size_t oldLength = _length;
656  _length = _length - 1;
657  if (_length < 0) _length = 0;
658  _owner->checkLength(oldLength);
659  }
660 
661  void operator++(int) {
662  size_t oldLength = _length;
663  _length = _length + 1;
664  _owner->checkLength(oldLength);
665  }
666 
667  void operator--(int) {
668  size_t oldLength = _length;
669  _length = _length - 1;
670  if (_length < 0) _length = 0;
671  _owner->checkLength(oldLength);
672  }
673 
674  private:
675  size_t _length = 0;
676  listbase& _owner;
677  };
678 
679  public:
683  ListLengthWrapper length = 0;
684 
688  T* inner() const { return _values; }
689 
690  protected:
691 
692  void checkLength(size_t oldLength) {
693  allocate(oldLength, length, true);
694  }
695 
696  void allocate(size_t oldLength, size_t newLength, bool keepValues)
697  {
698  if (newLength > _allocatedLength) {
699  T *old_values = _values;
700 
701  auto sizeToAllocate = newLength - FIXED;
702  auto lengthToAllocate = listChunkSize + (size_t(sizeToAllocate/listChunkSize)) * listChunkSize;
703  _values = (T*) Platform::calloc(lengthToAllocate, sizeof(T));
704  _allocatedLength = FIXED + lengthToAllocate;
705 
706  if (keepValues && old_values) {
707  Platform::memcpy(_values, old_values, (oldLength - FIXED) * sizeof(T));
708  }
709 
710  if (old_values) {
711  Platform::free(old_values);
712  }
713  }
714  }
715 
720  T _stackValues[FIXED + 1];
721 
726 
731  T _dummy = {};
732  };
733 
734  using list = listbase<number>;
735  using indexlist = listbase<Index>;
736  using intlist = listbase<int>;
737 
738  ATTRIBUTE_UNUSED
739  static list listWithSize(size_t n) { list l; l.length = n; return l; }
740  ATTRIBUTE_UNUSED
741  static list intlistWithSize(size_t n) { intlist l; l.length = n; return l; }
742  ATTRIBUTE_UNUSED
743  static list indexlistWithSize(size_t n) { indexlist l; l.length = n; return l; }
744 
752  class ListNum {
753  public:
754  ListNum()
755  : _val()
756  {}
757 
758  ListNum(number val)
759  : _val(val)
760  {}
761 
762  ListNum(list val)
763  : _val(val)
764  {}
765 
766  operator number() const { return _val.length > 0 ? _val[0] : 0; }
767  operator list() const { return _val; }
768 
769  number operator[](size_t i) const {
770  return _val[i];
771  }
772 
773  list& operator->() {
774  return _val;
775  }
776 
777  const list& operator->() const {
778  return _val;
779  }
780 
781  private:
782  list _val;
783  };
784 
785 #ifdef RNBO_NOSTL
786  using UniqueListPtr = UniquePtr<list>;
787 #else
788  using UniqueListPtr = std::unique_ptr<listbase<number>>;
789 #endif // RNBO_NOSTL
790 
791 } // namespace RNBO
792 
793 #endif // #ifndef _RNBO_LIST_H_