Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions src/com/amazon/ion/impl/_Private_RecyclingQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.amazon.ion.impl;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* A queue whose elements are recycled. This queue will be extended and iterated frequently.
* @param <T> the type of elements stored.
*/
public class _Private_RecyclingQueue<T> {

/**
* Factory for new queue elements.
* @param <T> the type of element.
*/
public interface ElementFactory<T> {
/**
* @return a new instance.
*/
T newElement();
}

@FunctionalInterface
public interface Recycler<T> {
/**
* Re-initialize an element
*/
void recycle(T t);
}

/**
* Iterator for the queue.
*/
private class ElementIterator implements Iterator<T> {
int i = 0;
@Override
public boolean hasNext() {
return i <= currentIndex;
}

@Override
public T next() {
return elements.get(i++);
}
}

private final ElementIterator iterator;
private final List<T> elements;
private final ElementFactory<T> elementFactory;
private int currentIndex;
private T top;

/**
* @param initialCapacity the initial capacity of the underlying collection.
* @param elementFactory the factory used to create a new element on {@link #push()} when the queue has
* not previously grown to the new depth.
*/
public _Private_RecyclingQueue(int initialCapacity, ElementFactory<T> elementFactory) {
elements = new ArrayList<T>(initialCapacity);
this.elementFactory = elementFactory;
currentIndex = -1;
iterator = new ElementIterator();
}

public void truncate(int index) {
currentIndex = index;
}

public T get(int index) {
return elements.get(index);
}

/**
* Pushes an element onto the top of the queue, instantiating a new element only if the queue has not
* previously grown to the new depth.
* @return the element at the top of the queue after the push. This element must be initialized by the caller.
*/
public int push(Recycler<T> recycler) {
currentIndex++;
if (currentIndex >= elements.size()) {
top = elementFactory.newElement();
elements.add(top);
} else {
top = elements.get(currentIndex);
}
recycler.recycle(top);
return currentIndex;
}

/**
* Reclaim the current element.
*/
public void remove() {
currentIndex = Math.max(-1, currentIndex - 1);
}

public Iterator<T> iterate() {
iterator.i = 0;
return iterator;
}

/**
* @return true if the queue is empty; otherwise, false.
*/
public boolean isEmpty() {
return currentIndex < 0;
}

/**
* Reset the index and make the queue reusable.
*/
public void clear() {
currentIndex = -1;
}

/**
* @return the number of elements within the queue.
*/
public int size() {
return currentIndex + 1;
}
}
87 changes: 83 additions & 4 deletions src/com/amazon/ion/impl/_Private_RecyclingStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

/**
* A stack whose elements are recycled. This can be useful when the stack needs to grow and shrink
* frequently and has a predictable maximum depth.
* @param <T> the type of elements stored.
*/
public final class _Private_RecyclingStack<T> {
public final class _Private_RecyclingStack<T> implements Iterable<T> {
public $Iterator stackIterator;
@Override
public ListIterator<T> iterator() {
if (stackIterator != null) {
stackIterator.cursor = _Private_RecyclingStack.this.currentIndex;
} else {
stackIterator = new $Iterator();
}
return stackIterator;
}

/**
* Factory for new stack elements.
Expand All @@ -22,18 +34,26 @@ public interface ElementFactory<T> {
T newElement();
}

@FunctionalInterface
public interface Recycler<T> {
/**
* Re-initialize an element
*/
void recycle(T t);
}

private final List<T> elements;
private final ElementFactory<T> elementFactory;
private int currentIndex;
private T top;

/**
* @param initialCapacity the initial capacity of the underlying collection.
* @param elementFactory the factory used to create a new element on {@link #push()} when the stack has
* @param elementFactory the factory used to create a new element on {@link #push(Recycler)} when the stack has
* not previously grown to the new depth.
*/
public _Private_RecyclingStack(int initialCapacity, ElementFactory<T> elementFactory) {
elements = new ArrayList<T>(initialCapacity);
elements = new ArrayList<>(initialCapacity);
this.elementFactory = elementFactory;
currentIndex = -1;
top = null;
Expand All @@ -44,14 +64,15 @@ public _Private_RecyclingStack(int initialCapacity, ElementFactory<T> elementFac
* previously grown to the new depth.
* @return the element at the top of the stack after the push. This element must be initialized by the caller.
*/
public T push() {
public T push(Recycler<T> recycler) {
currentIndex++;
if (currentIndex >= elements.size()) {
top = elementFactory.newElement();
elements.add(top);
} else {
top = elements.get(currentIndex);
}
recycler.recycle(top);
return top;
}

Expand Down Expand Up @@ -92,4 +113,62 @@ public boolean isEmpty() {
public int size() {
return currentIndex + 1;
}

private class $Iterator implements ListIterator<T> {
private int cursor;

@Override
public boolean hasNext() {
return cursor >= 0;
}

@Override
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
// post-decrement because "next" is where the cursor is
return _Private_RecyclingStack.this.elements.get(cursor--);
}

@Override
public boolean hasPrevious() {
return cursor + 1 <= _Private_RecyclingStack.this.currentIndex;
}

@Override
public T previous() {
if (!hasPrevious()) {
throw new NoSuchElementException();
}
// pre-increment: "next" is where the cursor is, so "previous" is upward in stack
return _Private_RecyclingStack.this.elements.get(++cursor);
}

@Override
public int nextIndex() {
return cursor;
}

@Override
public int previousIndex() {
return cursor + 1;
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}

@Override
public void set(T t) {
throw new UnsupportedOperationException();
}

@Override
public void add(T t) {
throw new UnsupportedOperationException();
}
}

}
Loading