Skip to content

Commit

Permalink
remove DirectoryTreeConfigSource.java,
Browse files Browse the repository at this point in the history
detect actual circular references rather than just previously visited/duplicated nodes, performance improvements
  • Loading branch information
Steanky committed Jul 25, 2023
1 parent 8846307 commit 9f1da8c
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 369 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ plugins {
}

group 'com.github.steanky'
version '0.20.1'
version '0.21.0'

publishing {
publications {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.github.steanky.toolkit.collection.Iterators;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.Function;
Expand Down Expand Up @@ -91,7 +92,7 @@ public static <TIn, TOut, TKey, TVisit> TOut process(TIn rootInput,
@NotNull Function<? super TIn, ? extends Node<TIn, TOut, TKey>> nodeFunction,
@NotNull Predicate<? super TIn> containerPredicate, @NotNull Function<? super TIn, ? extends TOut> scalarMapper,
Function<? super TIn, ? extends TVisit> visitKeyMapper,
Supplier<? extends Map<? super TVisit, TOut>> visitedSupplier,
Supplier<? extends Map<Object, TOut>> visitedSupplier,
@NotNull Supplier<? extends Deque<Node<TIn, TOut, TKey>>> stackSupplier, int flags) {
if (!containerPredicate.test(rootInput)) {
//if rootInput is a scalar, just return whatever the scalar mapper produces
Expand All @@ -113,10 +114,13 @@ public static <TIn, TOut, TKey, TVisit> TOut process(TIn rootInput,

//don't initialize the visitation map if there is no support for circular references
//make sure usages of this map check circularRefSupport to avoid NPE
Map<? super TVisit, TOut> visited = null;
if (circularRefSupport) {
visited = visitedSupplier.get();
visited.put(visitKeyMapper.apply(rootInput), rootNode.output.data);
rootNode.identity = visitKeyMapper.apply(rootInput);
}

Map<Object, TOut> scalarMap = null;
if (trackScalarReference) {
scalarMap = visitedSupplier.get();
}

Deque<Node<TIn, TOut, TKey>> stack = stackSupplier.get();
Expand All @@ -137,24 +141,25 @@ public static <TIn, TOut, TKey, TVisit> TOut process(TIn rootInput,
//nodes that aren't containers have no children, so we can immediately add them to the accumulator
if (hasOutput) {
TOut out;
boolean circular;
boolean visited;

//keep track of scalar references in the same way as nodes, if enabled
if (circularRefSupport && trackScalarReference) {
if (trackScalarReference) {
TVisit visit = visitKeyMapper.apply(entryInput);
if (visited.containsKey(visit)) {
out = visited.get(visit);
circular = true;
} else {
out = scalarMapper.apply(entryInput);
circular = false;
if (scalarMap.containsKey(visit)) {
out = scalarMap.get(visit);
visited = true;
}
else {
scalarMap.put(visit, out = scalarMapper.apply(entryInput));
visited = false;
}
} else {
}
else {
out = scalarMapper.apply(entryInput);
circular = false;
visited = false;
}

node.output.accumulator.accept(entryKey, out, circular);
node.output.accumulator.accept(entryKey, out, visited);
}

continue;
Expand All @@ -166,15 +171,15 @@ public static <TIn, TOut, TKey, TVisit> TOut process(TIn rootInput,
visit = visitKeyMapper.apply(entryInput);

//check containsKey, null values are allowed in the map
if (visited.containsKey(visit)) {
if (node.hasParent(visit)) {
/*
already-visited references are immediately added to the accumulator. if these references are
nodes, their output might not have been fully constructed yet. it might not even be possible to
ensure that it is constructed, in the case of circular references. therefore, immediately add
them to the accumulator, and let it know the reference is circular
*/
if (hasOutput) {
node.output.accumulator.accept(entryKey, visited.get(visit), true);
node.output.accumulator.accept(entryKey, node.getParent(visit), true);
}

continue;
Expand All @@ -183,7 +188,8 @@ public static <TIn, TOut, TKey, TVisit> TOut process(TIn rootInput,

Node<TIn, TOut, TKey> newNode = nodeFunction.apply(entryInput);
if (circularRefSupport) {
visited.put(visit, newNode.output.data);
newNode.parent = node;
newNode.identity = visit;
}

if (isEmpty(newNode)) {
Expand Down Expand Up @@ -369,9 +375,9 @@ public interface Accumulator<TKey, TOut> {
*
* @param key the key component of the value
* @param out the value component
* @param visited whether this input has been visited before
* @param circular whether this input is a circular reference
*/
void accept(TKey key, TOut out, boolean visited);
void accept(TKey key, TOut out, boolean circular);
}

/**
Expand Down Expand Up @@ -444,12 +450,41 @@ public static final class Node<TIn, TOut, TKey> {

private NodeResult<TKey, TOut> result;

private Node<TIn, TOut, TKey> parent;
private Object identity;

private Node(@NotNull Iterator<? extends Map.Entry<? extends TKey, ? extends TIn>> inputIterator,
@NotNull Output<TOut, ? super TKey> output) {
this.inputIterator = inputIterator;
this.output = output;
}

private TOut getParent(@Nullable Object identity) {
Node<TIn, TOut, TKey> current = this;
while (current != null) {
if (current.identity == identity) {
return current.output.data;
}

current = current.parent;
}

return null;
}

private boolean hasParent(@Nullable Object identity) {
Node<TIn, TOut, TKey> current = this;
while (current != null) {
if (current.identity == identity) {
return true;
}

current = current.parent;
}

return false;
}

private boolean hasResult() {
return result != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ final class ConfigContainers {
private int i;

@Override
public void accept(String s, ConfigElement element, boolean visited) {
public void accept(String s, ConfigElement element, boolean circular) {
underlyingArray[i++] = element;
}
});
Expand Down
Loading

0 comments on commit 9f1da8c

Please sign in to comment.