circular_list.dart 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. class CircularList<T> {
  2. List<T?> _array;
  3. int _length = 0;
  4. int _startIndex = 0;
  5. Function(int num)? onTrimmed;
  6. CircularList(int maxLength) : _array = List<T?>.filled(maxLength, null);
  7. // Gets the cyclic index for the specified regular index. The cyclic index can then be used on the
  8. // backing array to get the element associated with the regular index.
  9. int _getCyclicIndex(int index) {
  10. return (_startIndex + index) % _array.length;
  11. }
  12. int get maxLength {
  13. return _array.length;
  14. }
  15. set maxLength(int value) {
  16. if (value <= 0)
  17. throw ArgumentError.value(
  18. value, 'value', 'maxLength can\'t be negative!');
  19. if (value == _array.length) return;
  20. // Reconstruct array, starting at index 0. Only transfer values from the
  21. // indexes 0 to length.
  22. final newArray = List<T?>.generate(
  23. value,
  24. (index) =>
  25. index < _array.length ? _array[_getCyclicIndex(index)] : null);
  26. _startIndex = 0;
  27. _array = newArray;
  28. }
  29. int get length {
  30. return _length;
  31. }
  32. set length(int value) {
  33. if (value > _length) {
  34. for (int i = length; i < value; i++) {
  35. _array[i] = null;
  36. }
  37. }
  38. _length = value;
  39. }
  40. void forEach(void Function(T? item, int index) callback) {
  41. final len = _length;
  42. for (int i = 0; i < len; i++) {
  43. callback(_array[_getCyclicIndex(i)], i);
  44. }
  45. }
  46. T? operator [](int index) {
  47. return _array[_getCyclicIndex(index)];
  48. }
  49. operator []=(int index, T? value) {
  50. _array[_getCyclicIndex(index)] = value;
  51. }
  52. void clear() {
  53. _startIndex = 0;
  54. _length = 0;
  55. }
  56. void pushAll(Iterable<T> items) {
  57. items.forEach((element) {
  58. push(element);
  59. });
  60. }
  61. void push(T value) {
  62. _array[_getCyclicIndex(_length)] = value;
  63. if (_length == _array.length) {
  64. _startIndex++;
  65. if (_startIndex == _array.length) {
  66. _startIndex = 0;
  67. }
  68. onTrimmed?.call(1);
  69. } else {
  70. _length++;
  71. }
  72. }
  73. /// Removes and returns the last value on the list
  74. T pop() {
  75. return _array[_getCyclicIndex(_length-- - 1)]!;
  76. }
  77. /// Deletes and/or inserts items at a particular index (in that order).
  78. void splice(int start, int deleteCount, List<T> items) {
  79. // delete items
  80. if (deleteCount > 0) {
  81. for (int i = start; i < _length - deleteCount; i++)
  82. _array[_getCyclicIndex(i)] = _array[_getCyclicIndex(i + deleteCount)];
  83. length -= deleteCount;
  84. }
  85. if (items.length != 0) {
  86. // add items
  87. for (int i = _length - 1; i >= start; i--)
  88. _array[_getCyclicIndex(i + items.length)] = _array[_getCyclicIndex(i)];
  89. for (int i = 0; i < items.length; i++)
  90. _array[_getCyclicIndex(start + i)] = items[i];
  91. }
  92. // Adjust length as needed
  93. if (_length + items.length > _array.length) {
  94. int countToTrim = _length + items.length - _array.length;
  95. _startIndex += countToTrim;
  96. length = _array.length;
  97. onTrimmed?.call(countToTrim);
  98. } else {
  99. _length += items.length;
  100. }
  101. }
  102. void trimStart(int count) {
  103. if (count > _length) count = _length;
  104. // TODO: perhaps bug in original code, this does not clamp the value of startIndex
  105. _startIndex += count;
  106. _length -= count;
  107. onTrimmed?.call(count);
  108. }
  109. void shiftElements(int start, int count, int offset) {
  110. if (count < 0) return;
  111. if (start < 0 || start >= _length)
  112. throw Exception('Start argument is out of range');
  113. if (start + offset < 0)
  114. throw Exception('Can not shift elements in list beyond index 0');
  115. if (offset > 0) {
  116. for (var i = count - 1; i >= 0; i--) {
  117. this[start + i + offset] = this[start + i];
  118. }
  119. var expandListBy = (start + count + offset) - _length;
  120. if (expandListBy > 0) {
  121. _length += expandListBy;
  122. while (_length > _array.length) {
  123. length--;
  124. _startIndex++;
  125. onTrimmed?.call(1);
  126. }
  127. }
  128. } else {
  129. for (var i = 0; i < count; i++) {
  130. this[start + i + offset] = this[start + i];
  131. }
  132. }
  133. }
  134. bool get isFull => length == maxLength;
  135. List<T> toList() {
  136. return List<T>.generate(length, (index) => this[index]!);
  137. }
  138. }