circular_list.dart 4.2 KB

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