circular_list.dart 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. class CircularList<T> {
  2. CircularList(int maxLength) : _array = List<T?>.filled(maxLength, null);
  3. late List<T?> _array;
  4. var _length = 0;
  5. var _startIndex = 0;
  6. // Gets the cyclic index for the specified regular index. The cyclic index can then be used on the
  7. // backing array to get the element associated with the regular index.
  8. int _getCyclicIndex(int index) {
  9. return (_startIndex + index) % _array.length;
  10. }
  11. int get maxLength {
  12. return _array.length;
  13. }
  14. set maxLength(int value) {
  15. if (value <= 0)
  16. throw ArgumentError.value(
  17. value, 'value', 'maxLength can\'t be negative!');
  18. if (value == _array.length) return;
  19. // Reconstruct array, starting at index 0. Only transfer values from the
  20. // indexes 0 to length.
  21. final newArray = List<T?>.generate(
  22. value,
  23. (index) => index < _array.length ? _array[_getCyclicIndex(index)] : null,
  24. );
  25. _startIndex = 0;
  26. _array = newArray;
  27. }
  28. int get length {
  29. return _length;
  30. }
  31. set length(int value) {
  32. if (value > _length) {
  33. for (int i = length; i < value; i++) {
  34. _array[i] = null;
  35. }
  36. }
  37. _length = value;
  38. }
  39. void forEach(void Function(T item) callback) {
  40. final length = _length;
  41. for (int i = 0; i < length; i++) {
  42. callback(_array[_getCyclicIndex(i)]!);
  43. }
  44. }
  45. T operator [](int index) {
  46. if (index >= length) {
  47. throw RangeError.range(index, 0, length - 1);
  48. }
  49. return _array[_getCyclicIndex(index)]!;
  50. }
  51. operator []=(int index, T value) {
  52. if (index >= length) {
  53. throw RangeError.range(index, 0, length - 1);
  54. }
  55. _array[_getCyclicIndex(index)] = value;
  56. }
  57. void clear() {
  58. _startIndex = 0;
  59. _length = 0;
  60. }
  61. void pushAll(Iterable<T> items) {
  62. items.forEach((element) {
  63. push(element);
  64. });
  65. }
  66. void push(T value) {
  67. _array[_getCyclicIndex(_length)] = value;
  68. if (_length == _array.length) {
  69. _startIndex++;
  70. if (_startIndex == _array.length) {
  71. _startIndex = 0;
  72. }
  73. } else {
  74. _length++;
  75. }
  76. }
  77. /// Removes and returns the last value on the list
  78. T pop() {
  79. return _array[_getCyclicIndex(_length-- - 1)]!;
  80. }
  81. /// Deletes item at [index].
  82. void remove(int index, [int count = 1]) {
  83. if (count > 0) {
  84. if (index + count >= _length) {
  85. count = _length - index;
  86. }
  87. for (var i = index; i < _length - count; i++) {
  88. _array[_getCyclicIndex(i)] = _array[_getCyclicIndex(i + count)];
  89. }
  90. length -= count;
  91. }
  92. }
  93. /// Inserts [item] at [index].
  94. void insert(int index, T item) {
  95. if (index == 0 && _length >= _array.length) {
  96. // when something is inserted at index 0 and the list is full then
  97. // the new value immediately gets removed => nothing changes
  98. return;
  99. }
  100. for (var i = _length - 1; i >= index; i--) {
  101. _array[_getCyclicIndex(i + 1)] = _array[_getCyclicIndex(i)];
  102. }
  103. _array[_getCyclicIndex(index)] = item;
  104. if (_length + 1 > _array.length) {
  105. _startIndex += 1;
  106. } else {
  107. _length++;
  108. }
  109. }
  110. /// Inserts [items] at [index] in order.
  111. void insertAll(int index, List<T> items) {
  112. for (var i = items.length - 1; i >= 0; i--) {
  113. insert(index, items[i]);
  114. // when the list is full then we have to move the index down
  115. // as newly inserted values remove values with a lower index
  116. if (_length >= _array.length) {
  117. index--;
  118. if (index < 0) {
  119. return;
  120. }
  121. }
  122. }
  123. }
  124. void trimStart(int count) {
  125. if (count > _length) count = _length;
  126. _startIndex += count;
  127. _startIndex %= _array.length;
  128. _length -= count;
  129. }
  130. void shiftElements(int start, int count, int offset) {
  131. if (count < 0) return;
  132. if (start < 0 || start >= _length)
  133. throw Exception('Start argument is out of range');
  134. if (start + offset < 0)
  135. throw Exception('Can not shift elements in list beyond index 0');
  136. if (offset > 0) {
  137. for (var i = count - 1; i >= 0; i--) {
  138. this[start + i + offset] = this[start + i];
  139. }
  140. var expandListBy = (start + count + offset) - _length;
  141. if (expandListBy > 0) {
  142. _length += expandListBy;
  143. while (_length > _array.length) {
  144. length--;
  145. _startIndex++;
  146. }
  147. }
  148. } else {
  149. for (var i = 0; i < count; i++) {
  150. this[start + i + offset] = this[start + i];
  151. }
  152. }
  153. }
  154. void replaceWith(List<T> replacement) {
  155. var copyStart = 0;
  156. if (replacement.length > maxLength) {
  157. copyStart = replacement.length - maxLength;
  158. }
  159. final copyLength = replacement.length - copyStart;
  160. for (var i = 0; i < copyLength; i++) {
  161. _array[i] = replacement[copyStart + i];
  162. }
  163. _startIndex = 0;
  164. _length = copyLength;
  165. }
  166. bool get isFull => length == maxLength;
  167. List<T> toList() {
  168. return List<T>.generate(length, (index) => this[index]);
  169. }
  170. }