Skip to content

Commit

Permalink
First step to convert ImmutableList to LinkedList.
Browse files Browse the repository at this point in the history
ImmutableList extends LinkedList and reimplemented all the methods such
that the order and usages of ImmutableList is kept the same.

Co-authored-by: rdvdijk <[email protected]>
Co-authored-by: jvdb <[email protected]>
  • Loading branch information
3 people committed Feb 5, 2024
1 parent be1c5c7 commit c7aedb1
Show file tree
Hide file tree
Showing 66 changed files with 461 additions and 470 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ private static Trampoline<BigInteger> calculateTotalSize(final ImmutableList<Val
if (values.isEmpty()) {
return complete(() -> size);
}
if (values.head.equals(NOT_A_VALUE)) {
if (values.head().equals(NOT_A_VALUE)) {
return complete(() -> ZERO);
}
return intermediate(() -> calculateTotalSize(values.tail, size.add(values.head.slice().length)));
return intermediate(() -> calculateTotalSize(values.tail(), size.add(values.head().slice().length)));
}

@Override
Expand All @@ -80,15 +80,15 @@ private Trampoline<byte[]> getData(final ImmutableList<Value> values, final BigI
if (length.compareTo(ZERO) <= 0) {
return complete(() -> output);
}
final BigInteger nextOffset = currentOffset.add(values.head.slice().length);
final BigInteger nextOffset = currentOffset.add(values.head().slice().length);
if (nextOffset.compareTo(offset) <= 0) {
return intermediate(() -> getData(values.tail, nextOffset, currentDest, offset, length, output));
return intermediate(() -> getData(values.tail(), nextOffset, currentDest, offset, length, output));
}
final BigInteger localOffset = offset.subtract(currentOffset).compareTo(ZERO) < 0 ? ZERO : offset.subtract(currentOffset);
// The second argument in getData in Slice is a limit. It will return less if the end of slice is reached.
final byte[] data = values.head.slice().getData(localOffset, length);
final byte[] data = values.head().slice().getData(localOffset, length);
System.arraycopy(data, 0, output, currentDest.intValueExact(), data.length);
return intermediate(() -> getData(values.tail, nextOffset, currentDest.add(valueOf(data.length)), offset, length.subtract(valueOf(data.length)), output));
return intermediate(() -> getData(values.tail(), nextOffset, currentDest.add(valueOf(data.length)), offset, length.subtract(valueOf(data.length)), output));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ protected boolean isAvailable(final BigInteger offset, final BigInteger length)
private synchronized byte[] getValue() {
if (cache == null) {
final ImmutableList<Value> results = dataExpression.eval(parseState, encoding);
if (results.size <= index) {
throw new IllegalStateException(format("ValueExpression dataExpression yields %d result(s) (expected at least %d).", results.size, index+1));
if ((long) results.size() <= index) {
throw new IllegalStateException(format("ValueExpression dataExpression yields %d result(s) (expected at least %d).", (long) results.size(), index+1));
}
final Value cacheValue = getValueAtIndex(results, index, 0).computeResult();
if (cacheValue.equals(NOT_A_VALUE)) {
Expand All @@ -82,9 +82,9 @@ private synchronized byte[] getValue() {

private Trampoline<Value> getValueAtIndex(final ImmutableList<Value> results, final int index, final int current) {
if (index == current) {
return complete(() -> results.head);
return complete(() -> results.head());
}
return intermediate(() -> getValueAtIndex(results.tail, index, current + 1));
return intermediate(() -> getValueAtIndex(results.tail(), index, current + 1));
}

@Override
Expand Down
88 changes: 25 additions & 63 deletions core/src/main/java/io/parsingdata/metal/data/ImmutableList.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,97 +16,59 @@

package io.parsingdata.metal.data;

import static io.parsingdata.metal.Trampoline.complete;
import static io.parsingdata.metal.Trampoline.intermediate;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.data.Selection.reverse;

import java.util.Objects;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.stream.Collectors;

import io.parsingdata.metal.ImmutableObject;
import io.parsingdata.metal.Trampoline;
import io.parsingdata.metal.Util;

public class ImmutableList<T> extends ImmutableObject {

public final T head;
public final ImmutableList<T> tail;
public final long size;
public class ImmutableList<T> extends LinkedList<T> {

public ImmutableList() {
head = null;
tail = null;
size = 0;
super();
}

private ImmutableList(final T head, final ImmutableList<T> tail) {
this.head = checkNotNull(head, "head");
this.tail = checkNotNull(tail, "tail");
size = tail.size + 1;
public ImmutableList(final LinkedList<T> ts) {
super(ts);
}

public static <T> ImmutableList<T> create(final T head) {
return new ImmutableList<T>().add(checkNotNull(head, "head"));
return new ImmutableList<T>().addHead(checkNotNull(head, "head"));
}

public static <T> ImmutableList<T> create(final T[] array) {
return createFromArray(new ImmutableList<>(), checkNotNull(array, "array"), array.length - 1).computeResult();
return new ImmutableList<>(new LinkedList<>(Arrays.stream(array).collect(Collectors.toList())));
}

private static <T> Trampoline<ImmutableList<T>> createFromArray(final ImmutableList<T> list, final T[] array, final int index) {
if (index < 0) {
return complete(() -> list);
}
return intermediate(() -> createFromArray(list.add(array[index]), array, index - 1));
public ImmutableList<T> addHead(final T head) {
final LinkedList<T> ts = new LinkedList<>(this);
ts.addFirst(head);
return new ImmutableList<>(ts);
}

public ImmutableList<T> add(final T head) {
return new ImmutableList<>(checkNotNull(head, "head"), this);
public ImmutableList<T> addList(final ImmutableList<T> list) {
final LinkedList<T> ts = new LinkedList<>(list);
ts.addAll(this);
return new ImmutableList<>(ts);
}

public ImmutableList<T> add(final ImmutableList<T> list) {
checkNotNull(list, "list");
public T head() {
if (isEmpty()) {
return list;
return null;
}
return addRecursive(reverse(list)).computeResult();
}

private Trampoline<ImmutableList<T>> addRecursive(final ImmutableList<T> list) {
if (list.isEmpty()) {
return complete(() -> this);
}
return intermediate(() -> add(list.head).addRecursive(list.tail));
}

public boolean isEmpty() {
return size == 0;
return this.getFirst();
}

public boolean contains(final T value) { return containsRecursive(value).computeResult(); }

private Trampoline<Boolean> containsRecursive(final T value) {
if (isEmpty()) { return complete(() -> false); }
if (head.equals(value)) { return complete(() -> true); }
return intermediate(() -> tail.containsRecursive(value));
public ImmutableList<T> tail() {
final LinkedList<T> ts = new LinkedList<>(this.subList(1, size()));
return new ImmutableList<>(ts);
}

@Override
public String toString() {
return isEmpty() ? "" : ">" + head + tail;
return isEmpty() ? "" : ">" + head() + tail();
}

@Override
public boolean equals(final Object obj) {
return Util.notNullAndSameClass(this, obj)
&& Objects.equals(head, ((ImmutableList<?>)obj).head)
&& Objects.equals(tail, ((ImmutableList<?>)obj).tail);
// The size field is excluded from equals() and hashCode() because it is cached data.
}

@Override
public int immutableHashCode() {
return Objects.hash(getClass(), head, tail);
}
// TODO cache the hashcode like in ImmutableObject.

}
8 changes: 4 additions & 4 deletions core/src/main/java/io/parsingdata/metal/data/ParseGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ private Trampoline<Optional<ParseValue>> current(final ImmutableList<ParseItem>
if (items.isEmpty()) {
return complete(Optional::empty);
}
final ParseItem item = items.head;
final ParseItem item = items.head();
if (item.isValue()) {
return complete(() -> Optional.of(item.asValue()));
}
if (item.isGraph() && !item.asGraph().isEmpty()) {
return intermediate(() -> current(items.tail.add(item.asGraph().tail)
.add(item.asGraph().head)));
return intermediate(() -> current(items.tail().addHead(item.asGraph().tail)
.addHead(item.asGraph().head)));
}
return intermediate(() -> current(items.tail));
return intermediate(() -> current(items.tail()));
}

@Override public boolean isGraph() { return true; }
Expand Down
12 changes: 6 additions & 6 deletions core/src/main/java/io/parsingdata/metal/data/ParseState.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,18 +60,18 @@ public static ParseState createFromByteStream(final ByteStream input) {
}

public ParseState addBranch(final Token token) {
return new ParseState(order.addBranch(token), cache, source, offset, token.isIterable() ? iterations.add(new ImmutablePair<>(token, ZERO)) : iterations, references);
return new ParseState(order.addBranch(token), cache, source, offset, token.isIterable() ? iterations.addHead(new ImmutablePair<>(token, ZERO)) : iterations, references);
}

public ParseState closeBranch(final Token token) {
if (token.isIterable() && !iterations.head.left.equals(token)) {
throw new IllegalStateException(format("Cannot close branch for iterable token %s. Current iteration state is for token %s.", token.name, iterations.head.left.name));
if (token.isIterable() && !iterations.head().left.equals(token)) {
throw new IllegalStateException(format("Cannot close branch for iterable token %s. Current iteration state is for token %s.", token.name, iterations.head().left.name));
}
return new ParseState(order.closeBranch(), cache, source, offset, token.isIterable() ? iterations.tail : iterations, references);
return new ParseState(order.closeBranch(), cache, source, offset, token.isIterable() ? iterations.tail() : iterations, references);
}

public ParseState add(final ParseReference parseReference) {
return new ParseState(order, cache, source, offset, iterations, references.add(parseReference));
return new ParseState(order, cache, source, offset, iterations, references.addHead(parseReference));
}

public ParseState add(final ParseValue parseValue) {
Expand All @@ -83,7 +83,7 @@ public ParseState createCycle(final ParseReference parseReference) {
}

public ParseState iterate() {
return new ParseState(order, cache, source, offset, iterations.tail.add(new ImmutablePair<>(iterations.head.left, iterations.head.right.add(ONE))), references);
return new ParseState(order, cache, source, offset, iterations.tail().addHead(new ImmutablePair<>(iterations.head().left, iterations.head().right.add(ONE))), references);
}

public Optional<ParseState> seek(final BigInteger newOffset) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ public Optional<ImmutableList<Value>> find(final String scopeName, int limit) {
}

private Trampoline<ImmutableList<Value>> find(final ImmutableList<ParseValue> searchList, final String scopeName, final int limit, final ImmutableList<Value> result) {
if (searchList.isEmpty() || (limit != NO_LIMIT && result.size == limit)) {
if (searchList.isEmpty() || (limit != NO_LIMIT && (long) result.size() == limit)) {
return complete(() -> result);
}
final ParseValue head = searchList.head;
final ParseValue head = searchList.head();
if (head.matches(scopeName)) {
return intermediate(() -> find(searchList.tail, scopeName, limit, result.add(head)));
return intermediate(() -> find(searchList.tail(), scopeName, limit, result.addHead(head)));
}
return intermediate(() -> find(searchList.tail, scopeName, limit, result));
return intermediate(() -> find(searchList.tail(), scopeName, limit, result));
}

public ParseValueCache add(final ParseValue value) {
Expand All @@ -61,7 +61,7 @@ public ParseValueCache add(final ParseValue value) {
final String name = shortName(value.name);
final Map<String, ImmutableList<ParseValue>> stringImmutableListHashMap = new HashMap<>(cache);
stringImmutableListHashMap.computeIfAbsent(name, pattern -> new ImmutableList<>());
stringImmutableListHashMap.computeIfPresent(name, (pattern, valueImmutableList) -> valueImmutableList.add(value));
stringImmutableListHashMap.computeIfPresent(name, (pattern, valueImmutableList) -> valueImmutableList.addHead(value));
return new ParseValueCache(stringImmutableListHashMap);
}

Expand Down
38 changes: 19 additions & 19 deletions core/src/main/java/io/parsingdata/metal/data/Selection.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static Trampoline<Optional<ParseItem>> findItemAtOffset(final ImmutableLi
if (items.isEmpty()) {
return complete(Optional::empty);
}
final ParseItem head = items.head;
final ParseItem head = items.head();
if (head.isValue() && matchesLocation(head.asValue(), offset, source)) {
return complete(() -> Optional.of(head));
}
Expand All @@ -49,7 +49,7 @@ public static Trampoline<Optional<ParseItem>> findItemAtOffset(final ImmutableLi
return complete(() -> Optional.of(head));
}
}
return intermediate(() -> findItemAtOffset(items.tail, offset, source));
return intermediate(() -> findItemAtOffset(items.tail(), offset, source));
}

private static boolean matchesLocation(final ParseValue value, final BigInteger offset, final Source source) {
Expand All @@ -60,11 +60,11 @@ private static Trampoline<ParseValue> getLowestOffsetValue(final ImmutableList<P
if (graphList.isEmpty()) {
return complete(() -> lowest);
}
final ParseGraph graph = graphList.head;
final ParseGraph graph = graphList.head();
if (graph.isEmpty() || !graph.getDefinition().isLocal()) {
return intermediate(() -> getLowestOffsetValue(graphList.tail, lowest));
return intermediate(() -> getLowestOffsetValue(graphList.tail(), lowest));
}
return intermediate(() -> getLowestOffsetValue(addIfGraph(graphList.tail.add(graph.tail), graph.head),
return intermediate(() -> getLowestOffsetValue(addIfGraph(graphList.tail().addHead(graph.tail), graph.head),
compareIfValue(lowest, graph.head)));
}

Expand All @@ -77,7 +77,7 @@ private static ParseValue getLowest(final ParseValue lowest, final ParseValue va
}

private static ImmutableList<ParseGraph> addIfGraph(final ImmutableList<ParseGraph> graphList, final ParseItem head) {
return head.isGraph() ? graphList.add(head.asGraph()) : graphList;
return head.isGraph() ? graphList.addHead(head.asGraph()) : graphList;
}

public static ImmutableList<ParseValue> getAllValues(final ParseGraph graph, final Predicate<ParseValue> predicate, final int limit) {
Expand All @@ -89,22 +89,22 @@ public static ImmutableList<ParseValue> getAllValues(final ParseGraph graph, fin
}

private static Trampoline<ImmutableList<ParseValue>> getAllValues(final ImmutableList<ParseGraph> graphList, final ImmutableList<ParseValue> valueList, final Predicate<ParseValue> predicate, final int limit) {
if (graphList.isEmpty() || valueList.size == limit) {
if (graphList.isEmpty() || (long) valueList.size() == limit) {
return complete(() -> valueList);
}
final ParseGraph graph = graphList.head;
final ParseGraph graph = graphList.head();
if (graph.isEmpty()) {
return intermediate(() -> getAllValues(graphList.tail, valueList, predicate, limit));
return intermediate(() -> getAllValues(graphList.tail(), valueList, predicate, limit));
}
return intermediate(() -> getAllValues(addIfGraph(graphList.tail.add(graph.tail), graph.head),
return intermediate(() -> getAllValues(addIfGraph(graphList.tail().addHead(graph.tail), graph.head),
addIfMatchingValue(valueList, graph.head, predicate),
predicate,
limit));
}

private static ImmutableList<ParseValue> addIfMatchingValue(final ImmutableList<ParseValue> valueList, final ParseItem item, final Predicate<ParseValue> predicate) {
if (item.isValue() && predicate.test(item.asValue())) {
return valueList.add(item.asValue());
return valueList.addHead(item.asValue());
}
return valueList;
}
Expand All @@ -113,14 +113,14 @@ public static <T> ImmutableList<T> reverse(final ImmutableList<T> list) {
if (list.isEmpty()) {
return list;
}
return reverse(list.tail, ImmutableList.create(list.head)).computeResult();
return reverse(list.tail(), ImmutableList.create(list.head())).computeResult();
}

private static <T> Trampoline<ImmutableList<T>> reverse(final ImmutableList<T> oldList, final ImmutableList<T> newList) {
if (oldList.isEmpty()) {
return complete(() -> newList);
}
return intermediate(() -> reverse(oldList.tail, newList.add(oldList.head)));
return intermediate(() -> reverse(oldList.tail(), newList.addHead(oldList.head())));
}

public static ImmutableList<ParseItem> getAllRoots(final ParseGraph graph, final Token definition) {
Expand All @@ -131,17 +131,17 @@ private static Trampoline<ImmutableList<ParseItem>> getAllRootsRecursive(final I
if (backlog.isEmpty()) {
return complete(() -> rootList);
}
final ParseItem item = backlog.head.item;
final ParseGraph parent = backlog.head.parent;
final ImmutableList<ParseItem> nextResult = item.getDefinition().equals(definition) && (parent == null || !parent.getDefinition().equals(definition)) ? rootList.add(item) : rootList;
final ParseItem item = backlog.head().item;
final ParseGraph parent = backlog.head().parent;
final ImmutableList<ParseItem> nextResult = item.getDefinition().equals(definition) && (parent == null || !parent.getDefinition().equals(definition)) ? rootList.addHead(item) : rootList;
if (item.isGraph() && !item.asGraph().isEmpty()) {
final ParseGraph itemGraph = item.asGraph();
return intermediate(() -> getAllRootsRecursive(backlog.tail.add(new Pair(itemGraph.head, itemGraph))
.add(new Pair(itemGraph.tail, itemGraph)),
return intermediate(() -> getAllRootsRecursive(backlog.tail().addHead(new Pair(itemGraph.head, itemGraph))
.addHead(new Pair(itemGraph.tail, itemGraph)),
definition,
nextResult));
}
return intermediate(() -> getAllRootsRecursive(backlog.tail, definition, nextResult));
return intermediate(() -> getAllRootsRecursive(backlog.tail(), definition, nextResult));
}

static class Pair {
Expand Down
Loading

0 comments on commit c7aedb1

Please sign in to comment.