setRange method

  1. @override
void setRange(
  1. int start,
  2. int end,
  3. Iterable<double> iterable,
  4. [int skipCount = 0]
)
inherited

Writes some elements of iterable into a range of this list.

Copies the objects of iterable, skipping skipCount objects first, into the range from start, inclusive, to end, exclusive, of this list.

final list1 = <int>[1, 2, 3, 4];
final list2 = <int>[5, 6, 7, 8, 9];
// Copies the 4th and 5th items in list2 as the 2nd and 3rd items
// of list1.
const skipCount = 3;
list1.setRange(1, 3, list2, skipCount);
print(list1); // [1, 8, 9, 4]

The provided range, given by start and end, must be valid. A range from start to end is valid if 0 ≤ startendlength. An empty range (with end == start) is valid.

The iterable must have enough objects to fill the range from start to end after skipping skipCount objects.

If iterable is this list, the operation correctly copies the elements originally in the range from skipCount to skipCount + (end - start) to the range start to end, even if the two ranges overlap.

If iterable depends on this list in some other way, no guarantees are made.

Implementation

@override
void setRange(int start, int end, Iterable<E> iterable, [int skipCount = 0]) {
  RangeError.checkValidRange(start, end, length);
  if (start == end) return;

  var targetStart = (_head + start) & (_table.length - 1);
  var targetEnd = (_head + end) & (_table.length - 1);
  var targetIsContiguous = targetStart < targetEnd;
  if (identical(iterable, this)) {
    // If we're copying this queue to itself, we can copy [_table] in directly
    // which requires some annoying case analysis but in return bottoms out on
    // an extremely efficient `memmove` call. However, we may need to do three
    // copies to avoid overwriting data we'll need to use later.
    var sourceStart = (_head + skipCount) & (_table.length - 1);
    var sourceEnd = (sourceStart + (end - start)) & (_table.length - 1);
    if (sourceStart == targetStart) return;

    var sourceIsContiguous = sourceStart < sourceEnd;
    if (targetIsContiguous && sourceIsContiguous) {
      // If both the source and destination ranges are contiguous, we can
      // do a single [setRange]. Hooray!
      _table.setRange(targetStart, targetEnd, _table, sourceStart);
    } else if (!targetIsContiguous && !sourceIsContiguous) {
      // If neither range is contiguous, we need to do three copies.
      if (sourceStart > targetStart) {
        // [=====| targetEnd                 targetStart |======]
        // [========| sourceEnd                 sourceStart |===]

        // Copy front to back.
        var startGap = sourceStart - targetStart;
        var firstEnd = _table.length - startGap;
        _table.setRange(targetStart, firstEnd, _table, sourceStart);
        _table.setRange(firstEnd, _table.length, _table);
        _table.setRange(0, targetEnd, _table, startGap);
      } else if (sourceEnd < targetEnd) {
        // [=====| targetEnd                 targetStart |======]
        // [==| sourceEnd                 sourceStart |=========]

        // Copy back to front.
        var firstStart = targetEnd - sourceEnd;
        _table.setRange(firstStart, targetEnd, _table);
        _table.setRange(0, firstStart, _table, _table.length - firstStart);
        _table.setRange(targetStart, _table.length, _table, sourceStart);
      }
    } else if (sourceStart < targetEnd) {
      // Copying twice is safe here as long as we copy front to back.
      if (sourceIsContiguous) {
        //       [=====| targetEnd            targetStart |======]
        //       [  |===========| sourceEnd                      ]
        // sourceStart
        _table.setRange(targetStart, _table.length, _table, sourceStart);
        _table.setRange(0, targetEnd, _table,
            sourceStart + (_table.length - targetStart));
      } else {
        //                                               targetEnd
        // [                         targetStart |===========|  ]
        // [=====| sourceEnd                 sourceStart |======]
        var firstEnd = _table.length - sourceStart;
        _table.setRange(targetStart, firstEnd, _table, sourceStart);
        _table.setRange(firstEnd, targetEnd, _table);
      }
    } else {
      // Copying twice is safe here as long as we copy back to front. This
      // also covers the case where there's no overlap between the source and
      // target ranges, in which case the direction doesn't matter.
      if (sourceIsContiguous) {
        // [=====| targetEnd                 targetStart |======]
        // [                         sourceStart |===========|  ]
        //                                             sourceEnd
        _table.setRange(0, targetEnd, _table,
            sourceStart + (_table.length - targetStart));
        _table.setRange(targetStart, _table.length, _table, sourceStart);
      } else {
        // targetStart
        //       [  |===========| targetEnd                      ]
        //       [=====| sourceEnd            sourceStart |======]
        var firstStart = targetEnd - sourceEnd;
        _table.setRange(firstStart, targetEnd, _table);
        _table.setRange(targetStart, firstStart, _table, sourceStart);
      }
    }
  } else if (targetIsContiguous) {
    // If the range is contiguous within the table, we can set it with a
    // single underlying [setRange] call.
    _table.setRange(targetStart, targetEnd, iterable, skipCount);
  } else if (iterable is List<E>) {
    // If the range isn't contiguous and [iterable] is actually a [List] (but
    // not this queue), set it with two underlying [setRange] calls.
    _table.setRange(targetStart, _table.length, iterable, skipCount);
    _table.setRange(
        0, targetEnd, iterable, skipCount + (_table.length - targetStart));
  } else {
    // If [iterable] isn't a [List], we don't want to make two different
    // [setRange] calls because it could materialize a lazy iterable twice.
    // Instead we just fall back to the default iteration-based
    // implementation.
    super.setRange(start, end, iterable, skipCount);
  }
}