circular_list.dart 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import 'dart:collection';
  2. class CircularList<T> with ListMixin<T> {
  3. CircularList(int maxLength) : _array = List<T?>.filled(maxLength, null);
  4. late List<T?> _array;
  5. var _length = 0;
  6. var _startIndex = 0;
  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) => index < _array.length ? _array[_getCyclicIndex(index)] : null,
  25. );
  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(
  41. // void Function(T? item, int index) callback, [
  42. // bool includeBuffer = false,
  43. // ]) {
  44. // final len = includeBuffer ? _array.length : _length;
  45. // for (int i = 0; i < len; i++) {
  46. // callback(_array[_getCyclicIndex(i)], i);
  47. // }
  48. // }
  49. T operator [](int index) {
  50. if (index >= length) {
  51. throw RangeError.range(index, 0, length - 1);
  52. }
  53. return _array[_getCyclicIndex(index)]!;
  54. }
  55. operator []=(int index, T value) {
  56. if (index >= length) {
  57. throw RangeError.range(index, 0, length - 1);
  58. }
  59. _array[_getCyclicIndex(index)] = value;
  60. }
  61. void clear() {
  62. _startIndex = 0;
  63. _length = 0;
  64. }
  65. void push(T value) {
  66. _array[_getCyclicIndex(_length)] = value;
  67. if (_length == _array.length) {
  68. _startIndex++;
  69. if (_startIndex == _array.length) {
  70. _startIndex = 0;
  71. }
  72. } else {
  73. _length++;
  74. }
  75. }
  76. /// Removes and returns the last value on the list
  77. T pop() {
  78. return _array[_getCyclicIndex(_length-- - 1)]!;
  79. }
  80. /// Deletes item at [index].
  81. void remove(int index, [int count = 1]) {
  82. if (count > 0) {
  83. for (var i = index; i < _length - count; i++) {
  84. _array[_getCyclicIndex(i)] = _array[_getCyclicIndex(i + count)];
  85. }
  86. length -= count;
  87. }
  88. }
  89. /// Inserts [item] at [index].
  90. void insert(int index, T item) {
  91. for (var i = _length - 1; i >= index; i--) {
  92. _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)];
  93. }
  94. _array[_getCyclicIndex(index)] = item;
  95. if (_length + 1 > _array.length) {
  96. _startIndex += 1;
  97. onTrimmed?.call(1);
  98. } else {
  99. _length++;
  100. }
  101. }
  102. /// Inserts [items] at [index] in order.
  103. void insertAll(int index, List<T> items) {
  104. for (var i = _length - 1; i >= index; i--) {
  105. _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)];
  106. }
  107. for (var i = 0; i < items.length; i++) {
  108. _array[_getCyclicIndex(index + i)] = items[i];
  109. }
  110. if (_length + items.length > _array.length) {
  111. final countToTrim = _length + items.length - _array.length;
  112. _startIndex += countToTrim;
  113. length = _array.length;
  114. } else {
  115. _length += items.length;
  116. }
  117. }
  118. void trimStart(int count) {
  119. if (count > _length) count = _length;
  120. _startIndex += count;
  121. _startIndex %= _array.length;
  122. _length -= count;
  123. }
  124. void shiftElements(int start, int count, int offset) {
  125. if (count < 0) return;
  126. if (start < 0 || start >= _length)
  127. throw Exception('Start argument is out of range');
  128. if (start + offset < 0)
  129. throw Exception('Can not shift elements in list beyond index 0');
  130. if (offset > 0) {
  131. for (var i = count - 1; i >= 0; i--) {
  132. this[start + i + offset] = this[start + i];
  133. }
  134. var expandListBy = (start + count + offset) - _length;
  135. if (expandListBy > 0) {
  136. _length += expandListBy;
  137. while (_length > _array.length) {
  138. length--;
  139. _startIndex++;
  140. }
  141. }
  142. } else {
  143. for (var i = 0; i < count; i++) {
  144. this[start + i + offset] = this[start + i];
  145. }
  146. }
  147. }
  148. bool get isFull => length == maxLength;
  149. }