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_PlatformInterface.h"
6 
7 #ifdef RNBO_NOSTDLIB
8 #include "RNBO_UniquePtr.h"
9 #else
10 #include <memory>
11 #endif // RNBO_NOSTDLIB
12 
13 namespace RNBO {
14 
15  static const size_t listChunkSize = 8;
16 
27  template<typename T> class listbase {
28  public:
29 
34  : length(0)
35  , _values(nullptr)
36  , _allocatedLength(0)
37  {
38  allocate(length, false);
39  }
40 
51  template<typename... Ts> listbase(Ts ... args)
52  : length(sizeof...(args))
53  , _values(nullptr)
54  , _allocatedLength(0)
55  {
56  allocate(length, false);
57  T listValues[sizeof...(args)] = {static_cast<T>(args)...};
58  for (size_t i = 0; i < length; i++) {
59  _values[i] = listValues[i];
60  }
61  }
62 
73  listbase(const listbase &l)
74  : length(l.length)
75  , _values(nullptr)
76  , _allocatedLength(0)
77  {
78  allocate(length, false);
79  for (size_t i = 0; i < length; i++) {
80  _values[i] = l._values[i];
81  }
82  }
83 
88  : length(l.length)
89  , _values(l._values)
91  {
92  l._allocatedLength = 0;
93  l._values = nullptr;
94  }
95 
100  {
101  if (_values) {
102  Platform::get()->free(_values);
103  _values = nullptr;
104  }
105  }
106 
118  T& operator[](size_t i) {
119  if (i >= length) {
120  Platform::get()->errorOrDefault(RuntimeError::OutOfRange, "list index out of range", false /*unused*/);
121  _dummy = static_cast<T>(0);
122  return _dummy;
123  }
124  return _values[i];
125  }
126 
136  T operator[](size_t i) const {
137  if (i >= length) {
138  return Platform::get()->errorOrDefault(RuntimeError::OutOfRange, "list index out of range", 0);
139  }
140  return _values[i];
141  }
142 
143  listbase* operator->() {
144  return this;
145  }
146 
147  const listbase* operator->() const {
148  return this;
149  }
150 
155  {
156  if (&l != this) {
157  length = l.length;
158  allocate(length, false);
159  for (size_t i = 0; i < length; i++) {
160  _values[i] = l._values[i];
161  }
162  }
163  return *this;
164  }
165 
177  void push(T item) {
178  allocate(length + 1, true);
179  _values[length] = item;
180  length++;
181  }
182 
193  T pop() {
194  T tmp = 0;
195  if (length > 0) {
196  tmp = _values[length - 1];
197  length--;
198  }
199  return tmp;
200  }
201 
210  T shift() {
211  if (length == 0) {
212  return Platform::get()->errorOrDefault(RuntimeError::OutOfRange, "cannot shift out of empty list", 0);
213  }
214  T tmp = _values[0];
215  Platform::get()->memmove(_values, _values + 1, (length - 1) * sizeof(T));
216  length--;
217  return tmp;
218  }
219 
232  listbase concat(T item) const {
233  listbase tmp(*this);
234  tmp.push(item);
235  return tmp;
236  }
237 
250  listbase concat(const listbase& item) const {
251  listbase tmp(*this);
252  tmp.allocate(tmp.length + item.length, true);
253  for (size_t i = 0; i < item.length; i++) {
254  tmp._values[tmp.length] = item[i];
255  tmp.length++;
256  }
257 
258  return tmp;
259  }
260 
270  listbase& fill(T value, size_t start = 0, size_t end = 0)
271  {
272  if (end == 0) end = length;
273  allocate(end, true);
274  if (end > length) length = end;
275  for (size_t i = start; i < end; i++) {
276  _values[i] = value;
277  }
278 
279  return *this;
280  }
281 
288  template<typename... Ts> void unshift(Ts ... args) {
289  splice(0, 0, args...);
290  }
291 
302  template<typename... Ts> listbase<T> splice(Int start, size_t deleteCount, Ts ... args)
303  {
304  if (start < 0) start += length;
305  if (start < 0) start = 0;
306  size_t iStart = (size_t)start;
307  if (iStart >= length) {
308  deleteCount = 0;
309  iStart = length;
310  }
311 
312  if (iStart + deleteCount > length) deleteCount = length - iStart;
313 
314  const size_t addLength = sizeof...(args);
315  const long diff = (long)(addLength - deleteCount);
316 
317  listbase<T> deletedItems;
318  deletedItems.allocate(deleteCount, false);
319  for (size_t i = 0; i < deleteCount; i++) {
320  deletedItems.push(_values[iStart + i]);
321  }
322 
323  long newLength = (long)(length) + diff;
324  if (newLength <= 0) {
325  length = 0;
326  return deletedItems;
327  }
328 
329  allocate(static_cast<size_t>(newLength), true);
330 
331  if (diff < 0) {
332  for (long i = (long)start - diff; i < (long)length; i++) {
333  _values[i + diff] = _values[i];
334  }
335  } else if (diff > 0) {
336  for (long i = (long)(length - 1); i >= (long)start; i--) {
337  _values[i + diff] = _values[i];
338  }
339  }
340 
341  // since allocating an array of 0 length is invalid, we always allocate at least length 1
342  T addValues[(sizeof...(args)) + 1] = {static_cast<T>(args)...};
343  for (size_t i = 0; i < addLength; i++) {
344  _values[i + start] = addValues[i];
345  }
346 
347  length = static_cast<size_t>(newLength);
348 
349  return deletedItems;
350  }
351 
360  listbase slice(int start = 0, int end = 0) const {
361  listbase tmp;
362  int ilen = static_cast<int>(this->length);
363 
364  //negative is offset from end
365  if (start < 0) {
366  start = ilen + start;
367  if (start < 0) {
368  start = 0;
369  }
370  }
371 
372  //offset from end
373  if (end <= 0) {
374  end = ilen + end;
375  }
376 
377  //bounds check
378  if (start >= ilen || ilen == 0) {
379  return tmp;
380  }
381  if (end > ilen) {
382  end = ilen;
383  }
384 
385  //allocate and copy
386  tmp.allocate(static_cast<size_t>(end - start), false);
387  for (int i = start; i < end; i++) {
388  tmp._values[tmp.length] = _values[i];
389  tmp.length++;
390  }
391 
392  return tmp;
393  }
394 
402  bool includes(T value, int fromIndex = 0) const {
403  if (fromIndex < 0) {
404  fromIndex = int(length) + fromIndex;
405  if (fromIndex < 0) fromIndex = 0;
406  }
407 
408  for (size_t i = size_t(fromIndex); i < length; i++) {
409  if (_values[i] == value) return true;
410  }
411 
412  return false;
413  }
414 
424  int indexOf(T value, int fromIndex = 0) const {
425  if (fromIndex < 0) {
426  fromIndex = int(length) + fromIndex;
427  if (fromIndex < 0) fromIndex = 0;
428  }
429 
430  for (size_t i = size_t(fromIndex); i < length; i++) {
431  if (_values[i] == value) return (int)i;
432  }
433 
434  return -1;
435  }
436 
443  size_t len2 = length >> 1;
444 
445  for (size_t i = 0; i < len2; ++i) {
446  T tmp = _values[i];
447  size_t target = length - i - 1;
448  _values[i] = _values[target];
449  _values[target] = tmp;
450  }
451 
452  return *this;
453  }
454 
460  void reserve(size_t size) {
461  allocate(size, true);
462  }
463 
467  size_t length = 0;
468 
472  T* inner() const { return _values; }
473 
474  protected:
475  void allocate(size_t size, bool keepValues)
476  {
477  if (size > _allocatedLength) {
478  T *old_values = _values;
479 
480  _allocatedLength = listChunkSize + (size_t(size/listChunkSize)) * listChunkSize;
481  _values = (T*) Platform::get()->malloc(sizeof(T) * _allocatedLength);
482 
483  if (keepValues) {
484  Platform::get()->memcpy(_values, old_values, length * sizeof(T));
485  }
486 
487  if (old_values) {
488  Platform::get()->free(old_values);
489  }
490  }
491  }
492 
497 
502 
507  T _dummy = static_cast<T>(0);
508  };
509 
510  using list = listbase<number>;
511  using indexlist = listbase<Index>;
512  using intlist = listbase<int>;
513 
514 
522  class ListNum {
523  public:
524  ListNum()
525  : _val()
526  {}
527 
528  ListNum(number val)
529  : _val(val)
530  {}
531 
532  ListNum(list val)
533  : _val(val)
534  {}
535 
536  operator number() const { return _val.length > 0 ? _val[0] : 0; }
537  operator list() const { return _val; }
538 
539  list& operator->() {
540  return _val;
541  }
542 
543  const list& operator->() const {
544  return _val;
545  }
546 
547  private:
548  list _val;
549  };
550 
551 #ifdef RNBO_NOSTDLIB
552  using UniqueListPtr = UniquePtr<list>;
553 #else
554  using UniqueListPtr = std::unique_ptr<listbase<number>>;
555 #endif // RNBO_NOSTDLIB
556 
557 } // namespace RNBO
558 
559 #endif // #ifndef _RNBO_LIST_H_