package org.multiverse.collections; import org.multiverse.api.Stm; import org.multiverse.api.Txn; import org.multiverse.api.collections.TxnDeque; import org.multiverse.api.collections.TxnIterator; import org.multiverse.api.collections.TxnList; import org.multiverse.api.exceptions.TodoException; import org.multiverse.api.references.TxnInteger; import org.multiverse.api.references.TxnRef; import org.multiverse.api.references.TxnRefFactory; import java.util.NoSuchElementException; import static org.multiverse.api.TxnThreadLocal.getRequiredThreadLocalTxn; import static org.multiverse.api.TxnThreadLocal.getThreadLocalTxn; /** * A LinkedList implementation that also acts as a TxnQueue, TxnDeque. * * @param <E> */ public final class NaiveTxnLinkedList<E> extends AbstractTxnCollection<E> implements TxnDeque<E>, TxnList<E> { private final int capacity; private final TxnInteger size; private final TxnRef<Entry<E>> head; private final TxnRef<Entry<E>> tail; public NaiveTxnLinkedList(Stm stm) { this(stm, Integer.MAX_VALUE); } public NaiveTxnLinkedList(Stm stm, int capacity) { super(stm); if (capacity < 0) { throw new IllegalArgumentException(); } this.capacity = capacity; this.size = stm.getDefaultRefFactory().newTxnInteger(0); this.head = stm.getDefaultRefFactory().newTxnRef(null); this.tail = stm.getDefaultRefFactory().newTxnRef(null); } @Override public int getCapacity() { return capacity; } @Override public E set(int index, E element) { return set(getThreadLocalTxn(), index, element); } @Override public E set(Txn txn, int index, E element) { return entry(txn, index).value.getAndSet(txn, element); } @Override public int size(Txn txn) { return size.get(txn); } @Override public int indexOf(Object item) { return indexOf(getThreadLocalTxn(), item); } @Override public int indexOf(Txn txn, Object item) { if (item == null) { return -1; } int index = 0; Entry<E> node = head.get(txn); while (node != null) { if (node.value.get(txn).equals(item)) { return index; } node = node.next.get(txn); index++; } return -1; } @Override public int lastIndexOf(Object item) { return lastIndexOf(getThreadLocalTxn(), item); } @Override public int lastIndexOf(Txn txn, Object item) { if (item == null) { return -1; } int index = size.get(txn) - 1; Entry<E> node = tail.get(txn); while (node != null) { if (node.value.get(txn).equals(item)) { return index; } node = node.previous.get(txn); index--; } return -1; } private Entry<E> entry(Txn txn, int index) { if (index < 0) { throw new IndexOutOfBoundsException(); } int s = size.get(txn); if (index >= s) { throw new IndexOutOfBoundsException(); } if (index < (s >> 1)) { int i = 0; Entry<E> node = head.get(txn); while (true) { if (i == index) { return node; } node = node.next.get(txn); i++; } } else { int i = s - 1; Entry<E> node = tail.get(txn); while (true) { if (i == index) { return node; } node = node.previous.get(txn); i--; } } } @Override public boolean contains(Txn txn, Object o) { return indexOf(txn, o) != -1; } @Override public boolean remove(Txn txn, Object o) { throw new UnsupportedOperationException(); } @Override public void clear(Txn txn) { if (size.get(txn) == 0) { return; } size.set(txn, 0); head.set(txn, null); tail.set(txn, null); } // ==================== needs sorting ===================================== @Override public E element() { return element(getThreadLocalTxn()); } @Override public E element(Txn txn) { return getFirst(txn); } @Override public E pop() { return pop(getThreadLocalTxn()); } @Override public E pop(Txn txn) { return removeFirst(txn); } @Override public void push(E e) { push(getThreadLocalTxn(), e); } @Override public void push(Txn txn, E e) { addFirst(txn, e); } // =============== remove ============== @Override public E remove(int index) { return remove(getThreadLocalTxn(), index); } @Override public E remove(Txn txn, int index) { Entry entry = entry(txn, index); throw new UnsupportedOperationException(); } @Override public E removeFirst() { return removeFirst(getThreadLocalTxn()); } @Override public E removeFirst(Txn txn) { E element = pollFirst(txn); if (element == null) { throw new NoSuchElementException("NaiveTxnLinkedList is empty"); } return element; } @Override public E removeLast() { return removeLast(getThreadLocalTxn()); } @Override public E removeLast(Txn txn) { E element = pollLast(txn); if (element == null) { throw new NoSuchElementException("NaiveTxnLinkedList is empty"); } return element; } @Override public E remove() { return remove(getThreadLocalTxn()); } @Override public E remove(Txn txn) { return removeFirst(txn); } @Override public boolean removeFirstOccurrence(Object o) { return removeFirstOccurrence(getThreadLocalTxn(), o); } @Override public boolean removeFirstOccurrence(Txn txn, Object o) { throw new TodoException(); } @Override public boolean removeLastOccurrence(Object o) { return removeLastOccurrence(getThreadLocalTxn(), o); } @Override public boolean removeLastOccurrence(Txn txn, Object o) { throw new TodoException(); } // =============== gets ============== @Override public E getFirst() { return getFirst(getThreadLocalTxn()); } @Override public E getFirst(Txn txn) { E result = pollFirst(txn); if (result == null) { throw new NoSuchElementException("NaiveTxnLinkedList is empty"); } return result; } @Override public E getLast() { return getLast(getThreadLocalTxn()); } @Override public E getLast(Txn txn) { E result = pollLast(txn); if (result == null) { throw new NoSuchElementException("NaiveTxnLinkedList is empty"); } return result; } @Override public E get(int index) { return get(getThreadLocalTxn(), index); } @Override public E get(Txn txn, int index) { return entry(txn, index).value.get(txn); } // ============== adds ================ @Override public void addFirst(E e) { addFirst(getThreadLocalTxn(), e); } @Override public void addFirst(Txn txn, E e) { if (!offerFirst(txn, e)) { throw new IllegalStateException("NaiveTxnLinkedList full"); } } @Override public void addLast(E e) { addLast(getThreadLocalTxn(), e); } @Override public void addLast(Txn txn, E e) { if (!offerLast(txn, e)) { throw new IllegalStateException("NaiveTxnLinkedList full"); } } @Override public boolean add(Txn txn, E e) { if (!offer(txn, e)) { throw new IllegalStateException("NaiveTxnLinkedList full"); } return true; } // ================ puts ========================== @Override public void putFirst(E item) { putFirst(getThreadLocalTxn(), item); } @Override public void putFirst(Txn txn, E item) { if (!offerFirst(txn, item)) { txn.retry(); } } @Override public void put(E item) { put(getThreadLocalTxn(), item); } @Override public void put(Txn txn, E item) { putLast(txn, item); } @Override public void putLast(E item) { putLast(getRequiredThreadLocalTxn(), item); } @Override public void putLast(Txn txn, E item) { if (!offerLast(txn, item)) { txn.retry(); } } // ================== takes =============================== @Override public E take() { return take(getThreadLocalTxn()); } @Override public E take(Txn txn) { return takeLast(txn); } @Override public E takeFirst() { return takeFirst(getThreadLocalTxn()); } @Override public E takeFirst(Txn txn) { E item = pollFirst(txn); if (item == null) { txn.retry(); } return item; } @Override public E takeLast() { return takeLast(getThreadLocalTxn()); } @Override public E takeLast(Txn txn) { E item = pollLast(txn); if (item == null) { txn.retry(); } return item; } // ================== offers ======================== @Override public boolean offerFirst(E e) { return offerFirst(getThreadLocalTxn(), e); } @Override public boolean offerFirst(Txn txn, E item) { if (item == null) { throw new NullPointerException(); } int s = size.get(txn); if (s == capacity) { return false; } Entry<E> node = new Entry<E>(defaultRefFactory, item); if (s == 0) { head.set(txn, node); tail.set(txn, node); } else { node.next.set(txn, head.get(txn)); head.get(txn).previous.set(txn, node); head.set(txn, node); } size.increment(txn); return true; } @Override public boolean offerLast(E e) { return offerLast(getThreadLocalTxn(), e); } @Override public boolean offerLast(Txn txn, E item) { if (item == null) { throw new NullPointerException(); } int s = size.get(txn); if (s == capacity) { return false; } Entry<E> node = new Entry<E>(defaultRefFactory, item); if (s == 0) { head.set(txn, node); tail.set(txn, node); } else { node.previous.set(txn, tail.get(txn)); tail.get(txn).next.set(txn, node); tail.set(txn, node); } size.increment(txn); return true; } @Override public boolean offer(E item) { return offer(getThreadLocalTxn(), item); } @Override public boolean offer(Txn txn, E item) { return offerLast(txn, item); } // ================ polls ======================= @Override public E pollFirst() { return pollFirst(getThreadLocalTxn()); } @Override public E pollFirst(Txn txn) { int s = size.get(txn); if (s == 0) { return null; } E item; if (s == 1) { item = tail.get(txn).value.get(txn); head.set(txn, null); tail.set(txn, null); } else { Entry<E> oldHead = head.get(txn); item = oldHead.value.get(txn); Entry<E> newHead = oldHead.next.get(txn); head.set(txn, newHead); newHead.previous.set(txn, null); } size.decrement(txn); return item; } @Override public E pollLast() { return pollLast(getThreadLocalTxn()); } @Override public E pollLast(Txn txn) { int s = size.get(txn); if (s == 0) { return null; } E item; if (s == 1) { item = head.get(txn).value.get(txn); head.set(txn, null); tail.set(txn, null); } else { Entry<E> oldTail = tail.get(txn); item = oldTail.value.get(txn); Entry<E> newTail = oldTail.previous.get(txn); tail.set(txn, newTail); newTail.next.set(txn, null); } size.decrement(txn); return item; } @Override public E poll() { return poll(getThreadLocalTxn()); } @Override public E poll(Txn txn) { return pollLast(txn); } // =============== peeks ================= @Override public E peekFirst() { return peekFirst(getThreadLocalTxn()); } @Override public E peekFirst(Txn txn) { Entry<E> h = head.get(txn); return h == null ? null : h.value.get(txn); } @Override public E peekLast() { return peekLast(getThreadLocalTxn()); } @Override public E peekLast(Txn txn) { Entry<E> t = tail.get(txn); return t == null ? null : t.value.get(txn); } @Override public E peek() { return peek(getThreadLocalTxn()); } @Override public E peek(Txn txn) { return peekFirst(txn); } // ================ misc ========================== @Override public TxnIterator<E> iterator(Txn txn) { throw new TodoException(); } @Override public TxnIterator<E> descendingIterator() { return descendingIterator(getThreadLocalTxn()); } @Override public TxnIterator<E> descendingIterator(Txn txn) { throw new TodoException(); } // ================ misc ========================== @Override public String toString(Txn txn) { int s = size(txn); if (s == 0) { return "[]"; } StringBuffer sb = new StringBuffer(); sb.append('['); Entry<E> node = head.get(txn); do { sb.append(node.value.get(txn)); node = node.next.get(txn); if (node != null) { sb.append(", "); } } while (node != null); sb.append(']'); return sb.toString(); } static class Entry<E> { private final TxnRef<Entry<E>> next; private final TxnRef<Entry<E>> previous; private final TxnRef<E> value; Entry(TxnRefFactory refFactory, E value) { this.next = refFactory.newTxnRef(null); this.previous = refFactory.newTxnRef(null); this.value = refFactory.newTxnRef(value); } } }