diff --git a/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java index 9ed1414..fbe3400 100644 --- a/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java +++ b/lucene/core/src/java/org/apache/lucene/codecs/BlockTreeTermsReader.java @@ -1707,7 +1707,6 @@ public class BlockTreeTermsReader extends FieldsProducer { if (arc.output != NO_OUTPUT) { output = fstOutputs.add(output, arc.output); } - // if (DEBUG) { // System.out.println(" index: follow label=" + toHex(target.bytes[target.offset + targetUpto]&0xff) + " arc.output=" + arc.output + " arc.nfo=" + arc.nextFinalOutput); // } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/ByteSequenceOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/ByteSequenceOutputs.java index 0f8ade4..8441f2b 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/ByteSequenceOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/ByteSequenceOutputs.java @@ -137,4 +137,13 @@ public final class ByteSequenceOutputs extends Outputs { public String outputToString(BytesRef output) { return output.toString(); } + + @Override + public BytesRef deepCopy(BytesRef other) { + if (other == NO_OUTPUT) { + assert NO_OUTPUT.length == 0; + return NO_OUTPUT; + } + return BytesRef.deepCopyOf(other); + } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/CharSequenceOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/CharSequenceOutputs.java index c4bed38..c47c3c9 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/CharSequenceOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/CharSequenceOutputs.java @@ -142,4 +142,13 @@ public final class CharSequenceOutputs extends Outputs { public String outputToString(CharsRef output) { return output.toString(); } + + @Override + public CharsRef deepCopy(CharsRef other) { + if (other == NO_OUTPUT) { + assert NO_OUTPUT.length == 0; + return NO_OUTPUT; + } + return CharsRef.deepCopyOf(other); + } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/FST.java b/lucene/core/src/java/org/apache/lucene/util/fst/FST.java index d049235..30ab8d0 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/FST.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/FST.java @@ -171,6 +171,8 @@ public final class FST { private final boolean allowArrayArcs; private Arc cachedRootArcs[]; + private Arc assertingCachedRootArcs[]; // only set wit assert + /** Represents a single arc. */ public final static class Arc { @@ -213,6 +215,15 @@ public final class FST { } return this; } + + final void makeDeepCopy(Outputs outputs) { + final T old = output; + output = outputs.deepCopy(old); + assert old.equals(output) : old.toString() + " != " + output.toString(); + final T oldFinal = nextFinalOutput; + nextFinalOutput = outputs.deepCopy(oldFinal); + assert oldFinal.equals(nextFinalOutput); + } boolean flag(int flag) { return FST.flag(flags, flag); @@ -420,11 +431,18 @@ public final class FST { return node; } } - + // Caches first 128 labels @SuppressWarnings({"rawtypes","unchecked"}) private void cacheRootArcs() throws IOException { cachedRootArcs = (Arc[]) new Arc[0x80]; + readRootArcs(cachedRootArcs); + + assert setAssertingRootArcs(cachedRootArcs); + assert assertRootArcs(); + } + + public void readRootArcs(Arc[] arcs) throws IOException { final Arc arc = new Arc(); getFirstArc(arc); final BytesReader in = getBytesReader(); @@ -433,7 +451,7 @@ public final class FST { while(true) { assert arc.label != END_LABEL; if (arc.label < cachedRootArcs.length) { - cachedRootArcs[arc.label] = new Arc().copyFrom(arc); + arcs[arc.label] = new Arc().copyFrom(arc); } else { break; } @@ -444,6 +462,38 @@ public final class FST { } } } + + @SuppressWarnings({"rawtypes","unchecked"}) + private boolean setAssertingRootArcs(Arc[] arcs) throws IOException { + assertingCachedRootArcs = (Arc[]) new Arc[arcs.length]; + readRootArcs(assertingCachedRootArcs); + return true; + } + + private boolean assertRootArcs() { + assert cachedRootArcs != null; + assert assertingCachedRootArcs != null; + for (int i = 0; i < cachedRootArcs.length; i++) { + final Arc root = cachedRootArcs[i]; + final Arc asserting = assertingCachedRootArcs[i]; + if (root != null) { + assert root.arcIdx == asserting.arcIdx; + assert root.bytesPerArc == asserting.bytesPerArc; + assert root.flags == asserting.flags; + assert root.label == asserting.label; + assert root.nextArc == asserting.nextArc; + assert root.nextFinalOutput.equals(asserting.nextFinalOutput); + assert root.node == asserting.node; + assert root.numArcs == asserting.numArcs; + assert root.output.equals(asserting.output); + assert root.posArcsStart == asserting.posArcsStart; + assert root.target == asserting.target; + } else { + assert root == null && asserting == null; + } + } + return true; + } public T getEmptyOutput() { return emptyOutput; @@ -1099,7 +1149,7 @@ public final class FST { /** Finds an arc leaving the incoming arc, replacing the arc in place. * This returns null if the arc was not found, else the incoming arc. */ public Arc findTargetArc(int labelToMatch, Arc follow, Arc arc, BytesReader in) throws IOException { - assert cachedRootArcs != null; + assert assertRootArcs(); if (labelToMatch == END_LABEL) { if (follow.isFinal()) { @@ -1123,9 +1173,10 @@ public final class FST { if (follow.target == startNode && labelToMatch < cachedRootArcs.length) { final Arc result = cachedRootArcs[labelToMatch]; if (result == null) { - return result; + return null; } else { arc.copyFrom(result); + arc.makeDeepCopy(outputs); return arc; } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/IntSequenceOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/IntSequenceOutputs.java index 919fcea..93f8b15 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/IntSequenceOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/IntSequenceOutputs.java @@ -141,4 +141,13 @@ public final class IntSequenceOutputs extends Outputs { public String outputToString(IntsRef output) { return output.toString(); } + + @Override + public IntsRef deepCopy(IntsRef other) { + if (other == NO_OUTPUT) { + assert NO_OUTPUT.length == 0; + return NO_OUTPUT; + } + return IntsRef.deepCopyOf(other); + } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/NoOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/NoOutputs.java index 39d2330..f035ece 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/NoOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/NoOutputs.java @@ -101,4 +101,9 @@ public final class NoOutputs extends Outputs { public String outputToString(Object output) { return ""; } + + @Override + public Object deepCopy(Object other) { + return other; + } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/Outputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/Outputs.java index a06d53b..11ad75e 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/Outputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/Outputs.java @@ -82,4 +82,11 @@ public abstract class Outputs { public T merge(T first, T second) { throw new UnsupportedOperationException(); } + + /** + * Returns a deep copy of the given value unless + * the output class is immutable or the given instance is the same + * instance returned from {@link #getNoOutput()} + */ + public abstract T deepCopy(T other); } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/PairOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/PairOutputs.java index e625ca0..6fe35bf 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/PairOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/PairOutputs.java @@ -164,4 +164,12 @@ public class PairOutputs extends Outputs> { public String toString() { return "PairOutputs<" + outputs1 + "," + outputs2 + ">"; } + + @Override + public Pair deepCopy(Pair other) { + if (other == NO_OUTPUT) { + return NO_OUTPUT; + } + return newPair(outputs1.deepCopy(other.output1), outputs2.deepCopy(other.output2)); + } } diff --git a/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java b/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java index d13648a..9d19b03 100644 --- a/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java +++ b/lucene/core/src/java/org/apache/lucene/util/fst/PositiveIntOutputs.java @@ -119,4 +119,9 @@ public final class PositiveIntOutputs extends Outputs { public String toString() { return "PositiveIntOutputs"; } + + @Override + public Long deepCopy(Long other) { + return other; + } } diff --git a/lucene/misc/src/java/org/apache/lucene/util/fst/ListOfOutputs.java b/lucene/misc/src/java/org/apache/lucene/util/fst/ListOfOutputs.java index 99b2f3f..7eedf7d 100644 --- a/lucene/misc/src/java/org/apache/lucene/util/fst/ListOfOutputs.java +++ b/lucene/misc/src/java/org/apache/lucene/util/fst/ListOfOutputs.java @@ -195,4 +195,14 @@ public final class ListOfOutputs extends Outputs { return (List) output; } } + + @Override + public Object deepCopy(Object other) { + List list = asList(other); + ArrayList outputList = new ArrayList<>(list.size()); + for (T t : list) { + outputList.add(outputs.deepCopy(t)); + } + return outputList; + } } diff --git a/lucene/misc/src/java/org/apache/lucene/util/fst/UpToTwoPositiveIntOutputs.java b/lucene/misc/src/java/org/apache/lucene/util/fst/UpToTwoPositiveIntOutputs.java index 78e2715..1329ee3 100644 --- a/lucene/misc/src/java/org/apache/lucene/util/fst/UpToTwoPositiveIntOutputs.java +++ b/lucene/misc/src/java/org/apache/lucene/util/fst/UpToTwoPositiveIntOutputs.java @@ -232,4 +232,9 @@ public final class UpToTwoPositiveIntOutputs extends Outputs { assert valid(second, false); return new TwoLongs((Long) first, (Long) second); } + + @Override + public Object deepCopy(Object other) { + return other; // immutable + } }