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(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(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(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(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  length = l.length;
211  allocate(length, false);
212  size_t i = 0;
213  if (FIXED) {
214  for (; i < length; i++) {
215  _stackValues[i] = l[i];
216  }
217  }
218  for (; i < length; i++) {
219  _values[i] = l[i];
220  }
221  }
222  return *this;
223  }
224 
225  template<size_t N1> listbase<T>& operator=(const listbase<T, N1>& l)
226  {
227  length = l.length;
228  allocate(length, false);
229  size_t i = 0;
230  if (FIXED) {
231  for (; i < length; i++) {
232  _stackValues[i] = l[i];
233  }
234  }
235  for (; i < length; i++) {
236  _values[i] = l[i];
237  }
238 
239  return *this;
240  }
241 
253  void push(T item) {
254  allocate(length + 1, true);
255  if (FIXED && length < FIXED) _stackValues[length] = item;
256  else _values[length - FIXED] = item;
257  length++;
258  }
259 
270  T pop() {
271  T tmp = 0;
272  if (length > 0) {
273  size_t lastIndex = length - 1;
274  if (FIXED && lastIndex < FIXED) tmp = _stackValues[lastIndex];
275  else tmp = _values[lastIndex - FIXED];
276  length--;
277  }
278  return tmp;
279  }
280 
289  T shift() {
290  if (length == 0) {
291  return Platform::errorOrDefault(RuntimeError::OutOfRange, "cannot shift out of empty list", 0);
292  }
293  T tmp;
294  if (FIXED) {
295  tmp = _stackValues[0];
296  Platform::memmove(_stackValues, _stackValues + 1, (FIXED - 1) * sizeof(T));
297  if (length > FIXED && _values) {
298  _stackValues[FIXED - 1] = _values[0];
299  Platform::memmove(_values, _values + 1, (length - FIXED - 1) * sizeof(T));
300  }
301  }
302  else {
303  tmp = _values[0];
304  Platform::memmove(_values, _values + 1, (length - 1) * sizeof(T));
305  }
306  length--;
307  return tmp;
308  }
309 
322  listbase concat(T item) const {
323  listbase tmp(*this);
324  tmp.push(item);
325  return tmp;
326  }
327 
340  listbase concat(const listbase& item) const {
341  listbase tmp(*this);
342  tmp.allocate(tmp.length + item.length, true);
343  for (size_t i = 0; i < item.length; i++) {
344  if (FIXED) {
345  tmp.length++;
346  tmp[tmp.length - 1] = item[i];
347  }
348  else {
349  tmp._values[tmp.length] = item[i];
350  tmp.length++;
351  }
352  }
353 
354  return tmp;
355  }
356 
366  listbase& fill(T value, size_t start = 0, size_t end = 0)
367  {
368  if (end == 0) end = length;
369  allocate(end, true);
370  if (end > length) length = end;
371  for (size_t i = start; i < end; i++) {
372  if (FIXED) {
373  (*this)[i] = value;
374  }
375  else {
376  _values[i] = value;
377  }
378  }
379 
380  return *this;
381  }
382 
389  template<typename... Ts> void unshift(Ts ... args) {
390  splice(0, 0, args...);
391  }
392 
403  template<typename... Ts> listbase<T> splice(Int start, size_t deleteCount, Ts ... args)
404  {
405  if (start < 0) start += length;
406  if (start < 0) start = 0;
407  size_t iStart = (size_t)start;
408  if (iStart >= length) {
409  deleteCount = 0;
410  iStart = length;
411  }
412 
413  if (iStart + deleteCount > length) deleteCount = length - iStart;
414 
415  const size_t addLength = sizeof...(args);
416  const long diff = (long)(addLength - deleteCount);
417 
418  listbase<T> deletedItems;
419  deletedItems.allocate(deleteCount, false);
420  for (size_t i = 0; i < deleteCount; i++) {
421  if (FIXED) {
422  T val = (*this)[iStart + i];
423  deletedItems.push(val);
424  }
425  else {
426  deletedItems.push(_values[iStart + i]);
427  }
428  }
429 
430  long newLength = (long)(length) + diff;
431  if (newLength <= 0) {
432  length = 0;
433  return deletedItems;
434  }
435 
436  allocate(static_cast<size_t>(newLength), true);
437 
438  if (diff < 0) {
439  RNBO_ASSERT(start - diff >= 0)
440  for (long i = (long)start - diff; i < (long)length; i++) {
441  RNBO_ASSERT(i + diff >= 0)
442  if (FIXED) {
443  (*this)[(size_t)(i + diff)] = (*this)[(size_t)i];
444  }
445  else {
446  _values[i + diff] = _values[i];
447  }
448  }
449  } else if (diff > 0) {
450  for (long i = (long)(length - 1); i >= (long)start; i--) {
451  RNBO_ASSERT(i + diff >= 0)
452  if (FIXED) {
453  (*this)[(size_t)(i + diff)] = (*this)[(size_t)i];
454  }
455  else {
456  _values[i + diff] = _values[i];
457  }
458  }
459  }
460 
461  // since allocating an array of 0 length is invalid, we always allocate at least length 1
462  T addValues[(sizeof...(args)) + 1] = {static_cast<T>(args)...};
463  RNBO_ASSERT(start >= 0)
464  for (size_t i = 0; i < addLength; i++) {
465  if (FIXED) {
466  (*this)[i + (size_t)start] = addValues[i];
467  }
468  else {
469  _values[i + (size_t)start] = addValues[i];
470  }
471  }
472 
473  length = static_cast<size_t>(newLength);
474 
475  return deletedItems;
476  }
477 
486  listbase slice(int start = 0, int end = 0) const {
487  listbase tmp;
488  int ilen = static_cast<int>(this->length);
489 
490  //negative is offset from end
491  if (start < 0) {
492  start = ilen + start;
493  if (start < 0) {
494  start = 0;
495  }
496  }
497 
498  //offset from end
499  if (end <= 0) {
500  end = ilen + end;
501  }
502 
503  //bounds check
504  if (start >= ilen || ilen == 0) {
505  return tmp;
506  }
507  if (end > ilen) {
508  end = ilen;
509  }
510 
511  //allocate and copy
512  tmp.allocate(static_cast<size_t>(end - start), false);
513  for (int i = start; i < end; i++) {
514  if (FIXED) {
515  tmp.length++;
516  tmp[tmp.length - 1] = (*this)[i];
517  }
518  else {
519  tmp._values[tmp.length] = _values[i];
520  tmp.length++;
521  }
522  }
523 
524  return tmp;
525  }
526 
534  bool includes(T value, int fromIndex = 0) const {
535  if (fromIndex < 0) {
536  fromIndex = int(length) + fromIndex;
537  if (fromIndex < 0) fromIndex = 0;
538  }
539 
540  for (size_t i = size_t(fromIndex); i < length; i++) {
541  if (FIXED) {
542  if ((*this)[i] == value) return true;
543  }
544  else {
545  if (_values[i] == value) return true;
546  }
547  }
548 
549  return false;
550  }
551 
561  int indexOf(T value, int fromIndex = 0) const {
562  if (fromIndex < 0) {
563  fromIndex = int(length) + fromIndex;
564  if (fromIndex < 0) fromIndex = 0;
565  }
566 
567  for (size_t i = size_t(fromIndex); i < length; i++) {
568  if (FIXED) {
569  if ((*this)[i] == value) return (int)i;
570  }
571  else {
572  if (_values[i] == value) return (int)i;
573  }
574  }
575 
576  return -1;
577  }
578 
585  size_t len2 = length >> 1;
586 
587  for (size_t i = 0; i < len2; ++i) {
588  if (FIXED) {
589  T tmp = (*this)[i];
590  size_t target = length - i - 1;
591  (*this)[i] = (*this)[target];
592  (*this)[target] = tmp;
593 
594  }
595  else {
596  T tmp = _values[i];
597  size_t target = length - i - 1;
598  _values[i] = _values[target];
599  _values[target] = tmp;
600  }
601  }
602 
603  return *this;
604  }
605 
611  void reserve(size_t size) {
612  allocate(size, true);
613  }
614 
615  protected:
616 
618  public:
619  ListLengthWrapper(size_t l, listbase& owner)
620  : _length(l)
621  , _owner(owner)
622  {}
623 
624  ListLengthWrapper(const ListLengthWrapper& l) = delete;
626 
627  operator size_t() const {
628  return _length;
629  }
630 
631  ListLengthWrapper& operator=(size_t l) {
632  _length = l;
633  if (_length < 0) _length = 0;
634  _owner->checkLength();
635  return *this;
636  }
637 
638  ListLengthWrapper& operator=(const ListLengthWrapper& l) {
639  _length = l._length;
640  _owner->checkLength();
641  return *this;
642  }
643 
644  void operator++() {
645  _length = _length + 1;
646  _owner->checkLength();
647  }
648 
649  void operator--() {
650  _length = _length - 1;
651  if (_length < 0) _length = 0;
652  _owner->checkLength();
653  }
654 
655  void operator++(int) {
656  _length = _length + 1;
657  _owner->checkLength();
658  }
659 
660  void operator--(int) {
661  _length = _length - 1;
662  if (_length < 0) _length = 0;
663  _owner->checkLength();
664  }
665 
666  private:
667  size_t _length = 0;
668  listbase& _owner;
669  };
670 
671  public:
675  ListLengthWrapper length = 0;
676 
680  T* inner() const { return _values; }
681 
682  protected:
683 
684  void checkLength() {
685  allocate(length, true);
686  }
687 
688  void allocate(size_t size, bool keepValues)
689  {
690  if (size > _allocatedLength) {
691  T *old_values = _values;
692 
693  auto sizeToAllocate = size - FIXED;
694  auto lengthToAllocate = listChunkSize + (size_t(sizeToAllocate/listChunkSize)) * listChunkSize;
695  _values = (T*) Platform::calloc(lengthToAllocate, sizeof(T));
696  _allocatedLength = FIXED + lengthToAllocate;
697 
698  if (keepValues && old_values) {
699  Platform::memcpy(_values, old_values, (length - FIXED) * sizeof(T));
700  }
701 
702  if (old_values) {
703  Platform::free(old_values);
704  }
705  }
706  }
707 
712  T _stackValues[FIXED + 1];
713 
718 
723  T _dummy = {};
724  };
725 
726  using list = listbase<number>;
727  using indexlist = listbase<Index>;
728  using intlist = listbase<int>;
729 
730  ATTRIBUTE_UNUSED
731  static list listWithSize(size_t n) { list l; l.length = n; return l; }
732  ATTRIBUTE_UNUSED
733  static list intlistWithSize(size_t n) { intlist l; l.length = n; return l; }
734  ATTRIBUTE_UNUSED
735  static list indexlistWithSize(size_t n) { indexlist l; l.length = n; return l; }
736 
744  class ListNum {
745  public:
746  ListNum()
747  : _val()
748  {}
749 
750  ListNum(number val)
751  : _val(val)
752  {}
753 
754  ListNum(list val)
755  : _val(val)
756  {}
757 
758  operator number() const { return _val.length > 0 ? _val[0] : 0; }
759  operator list() const { return _val; }
760 
761  number operator[](size_t i) const {
762  return _val[i];
763  }
764 
765  list& operator->() {
766  return _val;
767  }
768 
769  const list& operator->() const {
770  return _val;
771  }
772 
773  private:
774  list _val;
775  };
776 
777 #ifdef RNBO_NOSTL
778  using UniqueListPtr = UniquePtr<list>;
779 #else
780  using UniqueListPtr = std::unique_ptr<listbase<number>>;
781 #endif // RNBO_NOSTL
782 
783 } // namespace RNBO
784 
785 #endif // #ifndef _RNBO_LIST_H_