--- /dev/null
+/* unrolledlinkedlist.vala
+ *
+ * Copyright (C) 2013-2014 Maciej Piechotka
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * Maciej Piechotka <uzytkownik2@gmail.com>
+ */
+
+using Gee.Utils.Assume;
+
+/**
+ * Unrolled doubly-linked list implementation of the {@link List} interface.
+ *
+ * The unrolled doubly-linked list combines the advantages and disadvantages
+ * of the {@link ArrayList} and {@link LinkedList} and is usually suitable when
+ * modifications and read operations are balanced.
+ *
+ * Please note that in our benchmarks the speed of most operations (insertion,
+ * deletion, sequential read) was on par or better then {@link ArrayList} and
+ * {@link LinkedList} except the prepending operation.
+ *
+ * @see ArrayList
+ * @see LinkedList
+ */
+public class Gee.UnrolledLinkedList<G> : AbstractBidirList<G>, Queue<G>, Deque<G> {
+ /**
+ * The elements' equality testing function.
+ */
+ [CCode (notify = false)]
+ public EqualDataFunc<G> equal_func {
+ private set {}
+ get {
+ return _equal_func.func;
+ }
+ }
+
+ /**
+ * Constructs a new, empty linked list.
+ *
+ * If not provided, the function parameter is requested to the
+ * {@link Functions} function factory methods.
+ *
+ * @param equal_func an optional element equality testing function
+ */
+ public UnrolledLinkedList (owned EqualDataFunc<G>? equal_func = null) {
+ if (equal_func == null) {
+ equal_func = Functions.get_equal_func_for (typeof (G));
+ }
+ _equal_func = new Functions.EqualDataFuncClosure<G> ((owned)equal_func);
+ }
+
+ private UnrolledLinkedList.with_closures (owned Functions.EqualDataFuncClosure<G> equal_func) {
+ _equal_func = equal_func;
+ }
+
+ ~UnrolledLinkedList () {
+ this.clear ();
+ }
+
+ public override bool foreach (ForallFunc<G> f) {
+#if DUMP
+ stdout.printf ("FOREACH BEGIN %p\n", this);
+ dump ();
+ try {
+#endif
+ for (unowned Node<G>? node = _head; node != null; node = node._next) {
+ for (int pos = 0; pos < node._size; pos++) {
+ if (! f(node._data[pos])) {
+ return false;
+ }
+ }
+ }
+ return true;
+#if DUMP
+ } finally {
+ dump();
+ stdout.printf ("FOREACH END\n");
+ }
+#endif
+ }
+
+ public override int size { get {return _size;} }
+ public override bool read_only { get {return false;} }
+
+ public override Gee.Iterator<G> iterator () {
+ return new Iterator<G> (this);
+ }
+
+ public override bool contains (G item) {
+ return find_node (item) != null;
+ }
+
+ public override bool add (G item) {
+#if DUMP
+ stdout.printf ("ADD BEGIN %p (%s)\n", item, (string)item);
+ dump ();
+#endif
+ if (_tail == null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_head == null);
+#else
+ assume (_head == null);
+#endif
+ _tail = _head = new Node<G>();
+ }
+ add_to_node (_tail, item, _tail._size);
+#if DUMP
+ dump ();
+ stdout.printf ("ADD END\n");
+#endif
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return true;
+ }
+ public override bool remove (G item) {
+ int pos;
+ unowned Node<G>? node = find_node (item, out pos);
+ if (node != null) {
+ remove_from_node (node, pos);
+ }
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return node != null;
+ }
+
+ public override void clear () {
+ for (Node<G>? node = (owned)_head; node != null; node = (owned)node._next) {
+ for (uint pos = 0; pos < node._size; pos++) {
+ node._data[pos] = null;
+ }
+ }
+ _head = null;
+ _tail = null;
+ _stamp++;
+ _size = 0;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ }
+
+ public override ListIterator<G> list_iterator () {
+ return new Iterator<G> (this);
+ }
+
+ public override G? get (int index) {
+ assert (index >= 0);
+ assert (index < this._size);
+
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+
+ int pos;
+ unowned Node<G>? node = find_node_by_idx (index, out pos);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node != null);
+#endif
+ return node._data[pos];
+ }
+
+ public override void set (int index, G item) {
+ assert (index >= 0);
+ assert (index < this._size);
+
+ int pos;
+ unowned Node<G>? node = find_node_by_idx (index, out pos);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node != null);
+#endif
+ node._data[pos] = item;
+ }
+
+ public override int index_of (G item) {
+ int idx;
+ if (find_node (item, null, out idx) != null) {
+ return idx;
+ } else {
+ return -1;
+ }
+ }
+
+ public override void insert (int index, G item) {
+#if DUMP
+ stdout.printf ("INSERT BEGIN %p (%s)\n", item, (string)item);
+ dump ();
+#endif
+ assert (index >= 0);
+ assert (index <= this._size);
+
+ if (index != this._size) {
+ int pos;
+ unowned Node<G>? node = find_node_by_idx (index, out pos);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node != null);
+#endif
+ add_to_node (node, item, pos);
+ } else {
+ if (index == 0) {
+
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_head == null && _tail == null);
+#else
+ assume (_head == null && _tail == null);
+#endif
+ _tail = _head = new Node<G>();
+ }
+ add_to_node (_tail, item, _tail._size);
+ }
+#if DUMP
+ dump ();
+ stdout.printf ("INSERT END\n");
+#endif
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ }
+
+ public override G remove_at (int index) {
+ assert (index >= 0);
+ assert (index < this._size);
+
+ int pos;
+ unowned Node<G>? node = find_node_by_idx (index, out pos);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node != null);
+#endif
+ return remove_from_node (node, pos);
+ }
+
+ public override List<G>? slice (int start, int stop) {
+ assert (0 <= start && start <= stop && stop <= _size);
+
+ UnrolledLinkedList<G> slice = new UnrolledLinkedList<G>.with_closures (_equal_func);
+ slice._size = stop - start;
+ unowned Node<G>? copy = slice._head = new Node<G> ();
+
+ int orig_pos;
+ unowned Node<G>? orig = find_node_by_idx (start, out orig_pos);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (orig != null);
+#endif
+
+ int i = 0;
+ while (true) {
+ int j = 0;
+ while (j < NODE_SIZE && i < stop - start) {
+ copy._data[j] = orig._data[orig_pos];
+ i++;
+ j++;
+ orig_pos++;
+ if (unlikely (orig_pos == orig._size)) {
+ orig = orig._next;
+ orig_pos = 0;
+ }
+
+ }
+ copy._size = j;
+ if (unlikely (!(i < stop - start))) {
+ break;
+ }
+ copy._next = new Node<G> ();
+ copy._next._prev = copy;
+ copy = copy._next;
+ }
+ slice._tail = copy;
+#if CONSISTENCY_CHECKS
+ slice.check ();
+#endif
+ return slice;
+ }
+
+ public override BidirListIterator<G> bidir_list_iterator () {
+ return new Iterator<G> (this);
+ }
+
+ public int capacity { get { return Queue.UNBOUNDED_CAPACITY; } }
+
+ public int remaining_capacity { get { return Queue.UNBOUNDED_CAPACITY; } }
+
+ public bool is_full { get { return false; } }
+
+ public bool offer (G element) {
+ if (_tail == null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_head == null);
+#else
+ assume (_head == null);
+#endif
+ _tail = _head = new Node<G>();
+ }
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_head != null && _tail != null);
+#else
+ assume (_head != null && _tail != null);
+#endif
+ add_to_node (_tail, element, _tail._size);
+ return true;
+ }
+
+ public G? peek () {
+ if (_head != null) {
+ return _head._data[0];
+ } else {
+ return null;
+ }
+ }
+
+ public G? poll () {
+ if (_head != null) {
+ return remove_from_node (_head, 0);
+ } else {
+ return null;
+ }
+ }
+
+ public int drain (Collection<G> recipient, int amount = -1) {
+ int drained = 0;
+ if (amount < 0) {
+ for (Node<G>? node = (owned)_head; node != null; node = (owned)node._next) {
+ for (int i = 0; i < node._size; i++) {
+ G data = (owned)node._data[i];
+ recipient.add (data);
+ }
+ }
+ drained = _size;
+ _tail = null;
+ _size = 0;
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ } else {
+ Node<G>? node;
+ for (node = (owned)_head; node != null; node = (owned)node._next) {
+ if (likely (node._size <= amount)) {
+ for (int i = 0; i < node._size; i++) {
+ G data = (owned)node._data[i];
+ recipient.add (data);
+ }
+ amount -= node._size;
+ drained += node._size;
+ _size -= node._size;
+ } else {
+ for (int i = 0; i < amount; i++) {
+ G data = (owned)node._data[i];
+ recipient.add (data);
+ }
+ Memory.move(node._data, &node._data[amount], sizeof(G) * (node._size - amount));
+ drained += amount;
+ _size -= amount;
+ node._size -= amount;
+ unowned Node<G>? n = node;
+ _head = (owned)node;
+ if (likely (n._next != null) && unlikely (n._size + n._next._size < MERGE_THRESHOLD)) {
+ merge_with_next (node);
+ }
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ }
+ }
+ _tail = null;
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ }
+ }
+
+ public bool offer_head (G element) {
+ if (_head == null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_tail == null);
+#else
+ assume (_tail == null);
+#endif
+ _tail = _head = new Node<G>();
+ }
+ add_to_node (_head, element, 0);
+ return true;
+ }
+
+ public G? peek_head () {
+ return peek ();
+ }
+
+ public G? poll_head () {
+ return poll ();
+ }
+
+ public int drain_head (Collection<G> recipient, int amount = -1) {
+ return drain (recipient, amount);
+ }
+
+ public bool offer_tail (G element) {
+ return offer (element);
+ }
+
+ public G? peek_tail () {
+ if (_tail != null) {
+ return _tail._data[_tail._size - 1];
+ } else {
+ return null;
+ }
+ }
+
+ public G? poll_tail () {
+ if (_head != null) {
+ return remove_from_node (_tail, _tail._size - 1);
+ } else {
+ return null;
+ }
+ }
+
+ public int drain_tail (Collection<G> recipient, int amount = -1) {
+ int drained = 0;
+ if (amount < 0) {
+ for (unowned Node<G>? node = _tail; node != null; node = node._prev) {
+ for (int i = node._size; i-- > 0;) {
+ G data = (owned)node._data[i];
+ recipient.add (data);
+ }
+ node._next = null;
+ }
+ drained = _size;
+ _head = null;
+ _tail = null;
+ _size = 0;
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ } else {
+ for (unowned Node<G>? node = _tail; node != null;) {
+ if (node._size <= amount) {
+ for (int i = node._size; i-- > 0;) {
+ G data = (owned)node._data[i];
+ recipient.add (data);
+ }
+ amount -= node._size;
+ drained += node._size;
+ _size -= node._size;
+ node = node._prev;
+ if (node != null) {
+ node._next = null;
+ }
+ } else {
+ for (int i = 0; i < amount; i++) {
+ G data = (owned)node._data[node._size - i - 1];
+ recipient.add (data);
+ }
+ drained += amount;
+ _size -= amount;
+ node._size -= amount;
+ _tail = node;
+ if (likely (node._prev != null) && unlikely (node._size + node._prev._size < MERGE_THRESHOLD)) {
+ merge_with_next (node._prev);
+ }
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ }
+ }
+ _head = null;
+ _tail = null;
+ _stamp++;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return drained;
+ }
+ }
+
+ private unowned Node<G>? find_node (G item, out int pos = null, out int idx = null) {
+ idx = 0;
+ for (unowned Node<G>? node = _head; node != null; node = node._next) {
+ for (pos = 0; pos < node._size; pos++, idx++) {
+ if (this.equal_func (item, node._data[pos])) {
+ return node;
+ }
+ }
+ }
+ pos = -1;
+ return null;
+ }
+
+ private unowned Node<G>? find_node_by_idx (int idx, out int? pos = null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= idx && idx < _size);
+#else
+ assume (0 <= idx && idx < _size);
+#endif
+ if (idx <= size/2) {
+ for (unowned Node<G>? node = _head; node != null; node = node._next) {
+ if (idx < node._size) {
+ pos = idx;
+ return node;
+ }
+ idx -= node._size;
+ }
+ }
+ else {
+ int start_of_node = _size;
+ int count = 0;
+ for (unowned Node<G>? node = _tail; node != null; node = node._prev) {
+ start_of_node -= node._size;
+ count += node._size;
+ if (idx >= start_of_node) {
+ pos = idx - start_of_node;
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= pos && pos < node._size);
+#else
+ assume (0 <= pos && pos < node._size);
+#endif
+ return node;
+ }
+ }
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (start_of_node == 0);
+#endif
+ }
+#if !DISABLE_INTERNAL_ASSERTS
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ assert_not_reached ();
+#else
+ assume (false);
+ pos = -1;
+ return null;
+#endif
+ }
+
+ private void add_to_node (Node<G> node, G item, int pos, out unowned Node<G>? new_node = null, out int new_pos = null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= pos && pos <= node._size && node._size <= NODE_SIZE);
+#else
+ assume (0 <= pos && pos <= node._size && node._size <= NODE_SIZE);
+#endif
+ if (node._size == NODE_SIZE) {
+ Node<G> next = new Node<G> ();
+ next._next = (owned)node._next;
+ next._prev = node;
+ if (GLib.unlikely (next._next == null)) {
+ _tail = next;
+ } else {
+ next._next._prev = next;
+ }
+ Memory.copy(next._data, &node._data[SPLIT_POS], sizeof(G) * (NODE_SIZE - SPLIT_POS));
+ node._size = SPLIT_POS;
+ next._size = NODE_SIZE - SPLIT_POS;
+ node._next = (owned)next;
+ if (pos > SPLIT_POS) {
+ node = node._next;
+ pos = pos - SPLIT_POS;
+ }
+ }
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= pos && pos <= node._size && node._size < NODE_SIZE);
+#else
+ assume (0 <= pos && pos <= node._size && node._size < NODE_SIZE);
+#endif
+ Memory.move(&node._data[pos + 1], &node._data[pos], sizeof(G) * (node._size - pos));
+ Memory.set(&node._data[pos], 0, sizeof(G));
+ node._data[pos] = item;
+ node._size++;
+ _size++;
+ _stamp++;
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node._size <= NODE_SIZE);
+#else
+ assume (node._size <= NODE_SIZE);
+#endif
+ new_node = node;
+ new_pos = pos;
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ }
+
+ private G remove_from_node (Node<G> node, int pos, out unowned Node<G>? new_node = null, out int new_pos = null) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert ((0 <= pos && pos <= node._size) && pos <= NODE_SIZE);
+#else
+ assume ((0 <= pos && pos <= node._size) && pos <= NODE_SIZE);
+#endif
+ G item = (owned)node._data[pos];
+ Memory.move(&node._data[pos], &node._data[pos + 1], sizeof(G) * (node._size - (pos + 1)));
+ node._size--;
+ _size--;
+ _stamp++;
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node._size >= 0);
+ assert (_size >= 0);
+#else
+ assume (node._size >= 0);
+ assume (_size >= 0);
+#endif
+ if (unlikely (node._size == 0)) {
+ new_node = node._prev;
+ if (likely (node._prev != null)) {
+ new_pos = node._prev._size - 1;
+ } else {
+ new_pos = -1;
+ }
+ delete_node (node);
+ } else if (likely (node._prev != null) && unlikely (node._size + node._prev._size < MERGE_THRESHOLD)) {
+ new_node = node._prev;
+ new_pos = node._prev._size + pos - 1;
+ merge_with_next (node._prev);
+ } else if (likely (node._next != null) && unlikely (node._size + node._next._size < MERGE_THRESHOLD)) {
+ merge_with_next (node);
+ new_node = node;
+ new_pos = pos - 1;
+ } else {
+ if (pos != 0) {
+ new_node = node;
+ new_pos = pos - 1;
+ } else {
+ new_node = node._prev;
+ if (likely (node._prev != null)) {
+ new_pos = node._prev._size - 1;
+ } else {
+ new_pos = -1;
+ }
+ }
+ }
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ return item;
+ }
+
+ private void merge_with_next (Node<G>? node) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node._next != null);
+ assert (node._size + node._next._size <= NODE_SIZE);
+#else
+ assume (node._next != null);
+ assume (node._size + node._next._size <= NODE_SIZE);
+#endif
+ unowned Node<G> next = node._next;
+ Memory.copy(&node._data[node._size], next._data, sizeof(G) * next._size);
+ node._size += next._size;
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (node._size <= NODE_SIZE);
+#else
+ assume (node._size <= NODE_SIZE);
+#endif
+ delete_node (next);
+#if CONSISTENCY_CHECKS
+ check ();
+#endif
+ }
+
+ private void delete_node (Node<G>? node) {
+ if (likely (node._next != null)) {
+ node._next._prev = node._prev;
+ } else {
+ _tail = node._prev;
+ }
+ if (GLib.likely (node._prev != null)) {
+ node._prev._next = (owned)node._next;
+ } else {
+ _head = (owned)node._next;
+ }
+ }
+
+#if DUMP
+ private void dump () {
+ stdout.printf ("UnrolledLinkedList %p\n Size %d\n", this, _size);
+ for (unowned Node<G>? node = _head; node != null; node = node._next) {
+ stdout.printf (" Node %p\n Prev %p\n Next %p\n", node, node._prev, node._next);
+ for (int i = 0; i < node._size; i++) {
+ stdout.printf (" %d: %p (\"%s\")\n", i, node._data[i], (string)node._data[i]);
+ }
+ }
+ }
+#endif
+#if CONSISTENCY_CHECKS
+ private void check () {
+ int size = 0;
+ for (unowned Node<G>? node = _head; node != null; node = node._next) {
+ size += node._size;
+ }
+ assert (size == _size);
+ size = 0;
+ for (unowned Node<G>? node = _tail; node != null; node = node._prev) {
+ size += node._size;
+ }
+ assert (size == _size);
+ }
+#endif
+
+ private int _size = 0;
+ private int _stamp = 0;
+ private Node<G>? _head = null;
+ private unowned Node<G>? _tail = null;
+ private Functions.EqualDataFuncClosure<G> _equal_func;
+ private static const int NODE_SIZE = 29; // Chosen for node to be multiply cache line (4 on 64 bit and 2 on 32 bit)
+ private static const int SPLIT_POS = (NODE_SIZE - 1)/2 + 1;
+ private static const int MERGE_THRESHOLD = (NODE_SIZE * 4)/5;
+
+ private class Iterator<G> : Object, Gee.Traversable<G>, Gee.Iterator<G>, ListIterator<G>, BidirIterator<G>, BidirListIterator<G> {
+ public Iterator (UnrolledLinkedList<G> list) {
+ this._list = list;
+ this._stamp = list._stamp;
+ }
+
+ public new bool foreach (ForallFunc<G> f) {
+#if DUMP
+ stdout.printf ("FOREACH BEGIN [%p -> %p %d]\n", this, _current, _pos);
+ _list.dump ();
+ try {
+#endif
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ unowned Node<G>? current = _current;
+ unowned Node<G>? prev = null;
+ int pos = _pos;
+ int prev_pos = -1;
+ int index = _index;
+ int prev_index = -1;
+ bool deleted = _deleted;
+ if (current == null) {
+ current = _list._head;
+ pos = 0;
+ deleted = false;
+ index = 0;
+ if (unlikely (current == null)) {
+ return true;
+ }
+ } else if (unlikely (deleted)) {
+ if (unlikely(_current._size == _pos + 1)) {
+ if (unlikely (_current._next != null)) {
+ return true;
+ }
+ prev = current;
+ current = _current._next;
+ prev_pos = pos;
+ pos = 0;
+ deleted = false;
+ prev_index = index++;
+ } else {
+ prev = current;
+ prev_pos = pos++;
+ deleted = false;
+ prev_index = index++;
+ }
+ }
+ for (; current != null; prev = current, current = current._next, pos = 0) {
+ int size = current._size;
+ for (; pos < size; prev = current, prev_pos = pos++, prev_index = index++) {
+ deleted = false;
+ if (!f (current._data[pos])) {
+ _current = current;
+ _pos = pos;
+ _deleted = deleted;
+ _index = index;
+ return false;
+ }
+ }
+ }
+ _current = prev;
+ _pos = prev_pos;
+ _deleted = deleted;
+ _index = prev_index;
+ return true;
+#if DUMP
+ } finally {
+ _list.dump ();
+ stdout.printf ("FOREACH END [%p -> %p %d]\n", this, _current, _pos);
+ }
+#endif
+ }
+
+ public bool next () {
+#if DUMP
+ stdout.printf ("NEXT BEGIN [%p -> %p %d]\n", this, _current, _pos);
+ _list.dump ();
+ try {
+#endif
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ if (unlikely (_current == null)) {
+ _current = _list._head;
+ if (_current != null) {
+ _pos = 0;
+ _deleted = false;
+ _index = 0;
+ }
+ return _current != null;
+ } else if (unlikely(_current._size == _pos + 1)) {
+ if (likely (_current._next != null)) {
+ _current = _current._next;
+ _pos = 0;
+ _deleted = false;
+ _index++;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _pos++;
+ _deleted = false;
+ _index++;
+ return true;
+ }
+#if DUMP
+ } finally {
+ _list.dump ();
+ stdout.printf ("NEXT END [%p -> %p %d]\n", this, _current, _pos);
+ }
+#endif
+ }
+
+ public bool has_next () {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ if (unlikely (_current == null)) {
+ return _list._head != null;
+ } else if (unlikely(_current._size == _pos + 1)) {
+ return _current._next != null;
+ } else {
+ return true;
+ }
+ }
+
+ public new G get () {
+ assert (_list._stamp == _stamp);
+ assert (_current != null && !_deleted);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= _pos && _pos < _current._size);
+#else
+ assume (0 <= _pos && _pos < _current._size);
+#endif
+ return _current._data[_pos];
+ }
+
+ public void remove () {
+#if DUMP
+ stdout.printf ("REMOVE BEGIN [%p -> %p %d]\n", this, _current, _pos);
+ _list.dump ();
+#endif
+ assert (_list._stamp == _stamp);
+ assert (_current != null && !_deleted);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= _pos && _pos <= _current._size);
+#else
+ assume (0 <= _pos && _pos <= _current._size);
+#endif
+ _list.remove_from_node (_current, _pos, out _current, out _pos);
+ _deleted = true;
+ _index--;
+ _stamp++;
+#if DUMP
+ _list.dump ();
+ stdout.printf ("REMOVE END [%p -> %p %d]\n", this, _current, _pos);
+#endif
+ }
+
+ public bool valid {
+ get {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ return _current != null && !_deleted;
+ }
+ }
+
+ public bool read_only { get { return false; } }
+
+ public bool previous () {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ if (unlikely (_deleted)) {
+ _deleted = false;
+ return _current != null;
+ } else if (unlikely (_current == null)) {
+ return false;
+ } else if (unlikely (_pos == 0)) {
+ if (likely (_current._prev != null)) {
+ _current = _current._prev;
+ _pos = _current._size - 1;
+ _index--;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ _pos--;
+ _index--;
+ return true;
+ }
+ }
+
+ public bool has_previous () {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ if (unlikely (_deleted)) {
+ return _current != null;
+ } else if (unlikely (_current == null)) {
+ return false;
+ } else if (unlikely (_pos == 0)) {
+ return _current._prev != null;
+ } else {
+ return true;
+ }
+ }
+
+ public bool first () {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ _current = _list._head;
+ _deleted = false;
+ if (_current != null) {
+ _pos = 0;
+ } else {
+ _pos = -1;
+ }
+ _index = 0;
+ return _current != null;
+ }
+
+ public bool last () {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ _current = _list._tail;
+ _deleted = false;
+ if (_current != null) {
+ _pos = _current._size - 1;
+ } else {
+ _pos = -1;
+ }
+ _index = _list._size - 1;
+ return _current != null;
+ }
+
+ public new void set (G item) {
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ _current._data[_pos] = item;
+ _list._stamp++;
+ _stamp++;
+ }
+
+ public void add (G item) {
+#if DUMP
+ stdout.printf ("ADD BEGIN %p (%s) [%p -> %p %d]\n", item, (string)item, this, _current, _pos);
+ _list.dump ();
+#endif
+ assert (_list._stamp == _stamp);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (!(_current == null) || _pos == -1);
+ assert (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#else
+ assume (!(_current == null) || _pos == -1);
+ assume (!(_current != null) || (0 <= _pos && _pos <= _current._size));
+#endif
+ if (likely (_current != null)) {
+ _list.add_to_node (_current, item, _pos + 1, out _current, out _pos);
+ } else {
+ if (likely (_list._head != null)) {
+ _current = _list._head;
+ } else {
+ _current = _list._tail = _list._head = new Node<G>();
+ }
+ _list.add_to_node (_current, item, 0);
+ _pos = 0;
+ }
+ _stamp++;
+ _index++;
+ _deleted = false;
+#if DUMP
+ _list.dump ();
+ stdout.printf ("ADD END [%p -> %p %d]\n", this, _current, _pos);
+#endif
+ }
+
+ public int index () {
+ assert (_list._stamp == _stamp);
+ assert (_current != null);
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (0 <= _pos && _pos <= _current._size);
+#else
+ assume (0 <= _pos && _pos <= _current._size);
+#endif
+ return _index;
+ }
+
+ public void insert (G item) {
+#if DUMP
+ stdout.printf ("INSERT BEGIN %p (%s) [%p -> %p %d]\n", item, (string)item, this, _current, _pos);
+ _list.dump ();
+#endif
+ assert (_list._stamp == _stamp);
+
+ if (unlikely (_deleted)) {
+ if (unlikely (_current == null)) {
+ if (likely (_list._head != null)) {
+ _current = _list._head;
+ _pos = -1;
+ } else {
+ _current = _list._tail = _list._head = new Node<G>();
+ _pos = -1;
+ }
+ }
+ _list.add_to_node (_current, item, _pos + 1, out _current, out _pos);
+ if (unlikely (_pos == 0)) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_current._prev != null);
+#endif
+ _current = _current._prev;
+ _pos = _current._prev._size - 1;
+ } else {
+ _pos--;
+ }
+ } else {
+ _list.add_to_node (_current, item, _pos, out _current, out _pos);
+ if (unlikely (_pos + 1 == _current._size)) {
+#if !DISABLE_INTERNAL_ASSERTS
+ assert (_current._next != null);
+#endif
+ _current = _current._next;
+ _pos = 0;
+ } else {
+ _pos++;
+ }
+ }
+ _stamp++;
+ _index++;
+#if DUMP
+ _list.dump ();
+ stdout.printf ("INSERT END [%p -> %p %d]\n", this, _current, _pos);
+#endif
+ }
+
+ private UnrolledLinkedList<G>? _list;
+ private int _stamp;
+ private unowned Node<G>? _current = null;
+ private int _pos = -1;
+ private bool _deleted = false;
+ private int _index = -1;
+ }
+
+ [Compact]
+ private class Node<G> {
+ public unowned Node<G>? _prev = null;
+ public Node<G>? _next = null;
+ public int _size = 0;
+ public G _data[29 /* NODE_SIZE */];
+ }
+}
+