| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.lucene.index; |
| |
| import java.io.IOException; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Objects; |
| |
| import org.apache.lucene.index.PointValues.IntersectVisitor; |
| import org.apache.lucene.index.PointValues.Relation; |
| import org.apache.lucene.search.DocIdSetIterator; |
| import org.apache.lucene.util.Bits; |
| import org.apache.lucene.util.BytesRef; |
| import org.apache.lucene.util.FutureArrays; |
| import org.apache.lucene.util.VirtualMethod; |
| import org.apache.lucene.util.automaton.CompiledAutomaton; |
| |
| /** |
| * A {@link FilterLeafReader} that can be used to apply |
| * additional checks for tests. |
| */ |
| public class AssertingLeafReader extends FilterLeafReader { |
| |
| private static void assertThread(String object, Thread creationThread) { |
| if (creationThread != Thread.currentThread()) { |
| throw new AssertionError(object + " are only supposed to be consumed in " |
| + "the thread in which they have been acquired. But was acquired in " |
| + creationThread + " and consumed in " + Thread.currentThread() + "."); |
| } |
| } |
| |
| public AssertingLeafReader(LeafReader in) { |
| super(in); |
| // check some basic reader sanity |
| assert in.maxDoc() >= 0; |
| assert in.numDocs() <= in.maxDoc(); |
| assert in.numDeletedDocs() + in.numDocs() == in.maxDoc(); |
| assert !in.hasDeletions() || in.numDeletedDocs() > 0 && in.numDocs() < in.maxDoc(); |
| |
| CacheHelper coreCacheHelper = in.getCoreCacheHelper(); |
| if (coreCacheHelper != null) { |
| coreCacheHelper.addClosedListener(cacheKey -> { |
| final Object expectedKey = coreCacheHelper.getKey(); |
| assert expectedKey == cacheKey |
| : "Core closed listener called on a different key " + expectedKey + " <> " + cacheKey; |
| }); |
| } |
| |
| CacheHelper readerCacheHelper = in.getReaderCacheHelper(); |
| if (readerCacheHelper != null) { |
| readerCacheHelper.addClosedListener(cacheKey -> { |
| final Object expectedKey = readerCacheHelper.getKey(); |
| assert expectedKey == cacheKey |
| : "Core closed listener called on a different key " + expectedKey + " <> " + cacheKey; |
| }); |
| } |
| } |
| |
| @Override |
| public Terms terms(String field) throws IOException { |
| Terms terms = super.terms(field); |
| return terms == null ? null : new AssertingTerms(terms); |
| } |
| |
| @Override |
| public Fields getTermVectors(int docID) throws IOException { |
| Fields fields = super.getTermVectors(docID); |
| return fields == null ? null : new AssertingFields(fields); |
| } |
| |
| /** |
| * Wraps a Fields but with additional asserts |
| */ |
| public static class AssertingFields extends FilterFields { |
| public AssertingFields(Fields in) { |
| super(in); |
| } |
| |
| @Override |
| public Iterator<String> iterator() { |
| Iterator<String> iterator = super.iterator(); |
| assert iterator != null; |
| return iterator; |
| } |
| |
| @Override |
| public Terms terms(String field) throws IOException { |
| Terms terms = super.terms(field); |
| return terms == null ? null : new AssertingTerms(terms); |
| } |
| } |
| |
| /** |
| * Wraps a Terms but with additional asserts |
| */ |
| public static class AssertingTerms extends FilterTerms { |
| public AssertingTerms(Terms in) { |
| super(in); |
| } |
| |
| @Override |
| public TermsEnum intersect(CompiledAutomaton automaton, BytesRef bytes) throws IOException { |
| TermsEnum termsEnum = in.intersect(automaton, bytes); |
| assert termsEnum != null; |
| assert bytes == null || bytes.isValid(); |
| return new AssertingTermsEnum(termsEnum, hasFreqs()); |
| } |
| |
| @Override |
| public BytesRef getMin() throws IOException { |
| BytesRef v = in.getMin(); |
| assert v == null || v.isValid(); |
| return v; |
| } |
| |
| @Override |
| public BytesRef getMax() throws IOException { |
| BytesRef v = in.getMax(); |
| assert v == null || v.isValid(); |
| return v; |
| } |
| |
| @Override |
| public int getDocCount() throws IOException { |
| final int docCount = in.getDocCount(); |
| assert docCount > 0; |
| return docCount; |
| } |
| |
| @Override |
| public long getSumDocFreq() throws IOException { |
| final long sumDf = in.getSumDocFreq(); |
| assert sumDf >= getDocCount(); |
| return sumDf; |
| } |
| |
| @Override |
| public long getSumTotalTermFreq() throws IOException { |
| final long sumTtf = in.getSumTotalTermFreq(); |
| if (hasFreqs() == false) { |
| assert sumTtf == in.getSumDocFreq(); |
| } |
| assert sumTtf >= getSumDocFreq(); |
| return sumTtf; |
| } |
| |
| @Override |
| public TermsEnum iterator() throws IOException { |
| TermsEnum termsEnum = super.iterator(); |
| assert termsEnum != null; |
| return new AssertingTermsEnum(termsEnum, hasFreqs()); |
| } |
| |
| @Override |
| public String toString() { |
| return "AssertingTerms(" + in + ")"; |
| } |
| } |
| |
| static final VirtualMethod<TermsEnum> SEEK_EXACT = new VirtualMethod<>(TermsEnum.class, "seekExact", BytesRef.class); |
| |
| static class AssertingTermsEnum extends FilterTermsEnum { |
| private final Thread creationThread = Thread.currentThread(); |
| private enum State {INITIAL, POSITIONED, UNPOSITIONED}; |
| private State state = State.INITIAL; |
| private final boolean delegateOverridesSeekExact; |
| private final boolean hasFreqs; |
| |
| public AssertingTermsEnum(TermsEnum in, boolean hasFreqs) { |
| super(in); |
| delegateOverridesSeekExact = SEEK_EXACT.isOverriddenAsOf(in.getClass()); |
| this.hasFreqs = hasFreqs; |
| } |
| |
| @Override |
| public PostingsEnum postings(PostingsEnum reuse, int flags) throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED: "docs(...) called on unpositioned TermsEnum"; |
| |
| // reuse if the codec reused |
| final PostingsEnum actualReuse; |
| if (reuse instanceof AssertingPostingsEnum) { |
| actualReuse = ((AssertingPostingsEnum) reuse).in; |
| } else { |
| actualReuse = null; |
| } |
| PostingsEnum docs = super.postings(actualReuse, flags); |
| assert docs != null; |
| if (docs == actualReuse) { |
| // codec reused, reset asserting state |
| ((AssertingPostingsEnum)reuse).reset(); |
| return reuse; |
| } else { |
| return new AssertingPostingsEnum(docs); |
| } |
| } |
| |
| @Override |
| public ImpactsEnum impacts(int flags) throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED: "docs(...) called on unpositioned TermsEnum"; |
| assert (flags & PostingsEnum.FREQS) != 0 : "Freqs should be requested on impacts"; |
| |
| return new AssertingImpactsEnum(super.impacts(flags)); |
| } |
| |
| // TODO: we should separately track if we are 'at the end' ? |
| // someone should not call next() after it returns null!!!! |
| @Override |
| public BytesRef next() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.INITIAL || state == State.POSITIONED: "next() called on unpositioned TermsEnum"; |
| BytesRef result = super.next(); |
| if (result == null) { |
| state = State.UNPOSITIONED; |
| } else { |
| assert result.isValid(); |
| state = State.POSITIONED; |
| } |
| return result; |
| } |
| |
| @Override |
| public long ord() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED : "ord() called on unpositioned TermsEnum"; |
| return super.ord(); |
| } |
| |
| @Override |
| public int docFreq() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED : "docFreq() called on unpositioned TermsEnum"; |
| final int df = super.docFreq(); |
| assert df > 0; |
| return df; |
| } |
| |
| @Override |
| public long totalTermFreq() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED : "totalTermFreq() called on unpositioned TermsEnum"; |
| final long ttf = super.totalTermFreq(); |
| if (hasFreqs) { |
| assert ttf >= docFreq(); |
| } else { |
| assert ttf == docFreq(); |
| } |
| return ttf; |
| } |
| |
| @Override |
| public BytesRef term() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED : "term() called on unpositioned TermsEnum"; |
| BytesRef ret = super.term(); |
| assert ret == null || ret.isValid(); |
| return ret; |
| } |
| |
| @Override |
| public void seekExact(long ord) throws IOException { |
| assertThread("Terms enums", creationThread); |
| super.seekExact(ord); |
| state = State.POSITIONED; |
| } |
| |
| @Override |
| public SeekStatus seekCeil(BytesRef term) throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert term.isValid(); |
| SeekStatus result = super.seekCeil(term); |
| if (result == SeekStatus.END) { |
| state = State.UNPOSITIONED; |
| } else { |
| state = State.POSITIONED; |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean seekExact(BytesRef text) throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert text.isValid(); |
| boolean result; |
| if (delegateOverridesSeekExact) { |
| result = in.seekExact(text); |
| } else { |
| result = super.seekExact(text); |
| } |
| if (result) { |
| state = State.POSITIONED; |
| } else { |
| state = State.UNPOSITIONED; |
| } |
| return result; |
| } |
| |
| @Override |
| public TermState termState() throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert state == State.POSITIONED : "termState() called on unpositioned TermsEnum"; |
| return in.termState(); |
| } |
| |
| @Override |
| public void seekExact(BytesRef term, TermState state) throws IOException { |
| assertThread("Terms enums", creationThread); |
| assert term.isValid(); |
| in.seekExact(term, state); |
| this.state = State.POSITIONED; |
| } |
| |
| @Override |
| public String toString() { |
| return "AssertingTermsEnum(" + in + ")"; |
| } |
| |
| void reset() { |
| state = State.INITIAL; |
| } |
| } |
| |
| static enum DocsEnumState { START, ITERATING, FINISHED }; |
| |
| /** Wraps a docsenum with additional checks */ |
| public static class AssertingPostingsEnum extends FilterPostingsEnum { |
| private final Thread creationThread = Thread.currentThread(); |
| private DocsEnumState state = DocsEnumState.START; |
| int positionCount = 0; |
| int positionMax = 0; |
| private int doc; |
| |
| public AssertingPostingsEnum(PostingsEnum in) { |
| super(in); |
| this.doc = in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Docs enums", creationThread); |
| assert state != DocsEnumState.FINISHED : "nextDoc() called after NO_MORE_DOCS"; |
| int nextDoc = super.nextDoc(); |
| assert nextDoc > doc : "backwards nextDoc from " + doc + " to " + nextDoc + " " + in; |
| if (nextDoc == DocIdSetIterator.NO_MORE_DOCS) { |
| state = DocsEnumState.FINISHED; |
| positionMax = 0; |
| } else { |
| state = DocsEnumState.ITERATING; |
| positionMax = super.freq(); |
| } |
| positionCount = 0; |
| assert super.docID() == nextDoc; |
| return doc = nextDoc; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Docs enums", creationThread); |
| assert state != DocsEnumState.FINISHED : "advance() called after NO_MORE_DOCS"; |
| assert target > doc : "target must be > docID(), got " + target + " <= " + doc; |
| int advanced = super.advance(target); |
| assert advanced >= target : "backwards advance from: " + target + " to: " + advanced; |
| if (advanced == DocIdSetIterator.NO_MORE_DOCS) { |
| state = DocsEnumState.FINISHED; |
| positionMax = 0; |
| } else { |
| state = DocsEnumState.ITERATING; |
| positionMax = super.freq(); |
| } |
| positionCount = 0; |
| assert super.docID() == advanced; |
| return doc = advanced; |
| } |
| |
| @Override |
| public int docID() { |
| assertThread("Docs enums", creationThread); |
| assert doc == super.docID() : " invalid docID() in " + in.getClass() + " " + super.docID() + " instead of " + doc; |
| return doc; |
| } |
| |
| @Override |
| public int freq() throws IOException { |
| assertThread("Docs enums", creationThread); |
| assert state != DocsEnumState.START : "freq() called before nextDoc()/advance()"; |
| assert state != DocsEnumState.FINISHED : "freq() called after NO_MORE_DOCS"; |
| int freq = super.freq(); |
| assert freq > 0; |
| return freq; |
| } |
| |
| @Override |
| public int nextPosition() throws IOException { |
| assert state != DocsEnumState.START : "nextPosition() called before nextDoc()/advance()"; |
| assert state != DocsEnumState.FINISHED : "nextPosition() called after NO_MORE_DOCS"; |
| assert positionCount < positionMax : "nextPosition() called more than freq() times!"; |
| int position = super.nextPosition(); |
| assert position >= 0 || position == -1 : "invalid position: " + position; |
| positionCount++; |
| return position; |
| } |
| |
| @Override |
| public int startOffset() throws IOException { |
| assert state != DocsEnumState.START : "startOffset() called before nextDoc()/advance()"; |
| assert state != DocsEnumState.FINISHED : "startOffset() called after NO_MORE_DOCS"; |
| assert positionCount > 0 : "startOffset() called before nextPosition()!"; |
| return super.startOffset(); |
| } |
| |
| @Override |
| public int endOffset() throws IOException { |
| assert state != DocsEnumState.START : "endOffset() called before nextDoc()/advance()"; |
| assert state != DocsEnumState.FINISHED : "endOffset() called after NO_MORE_DOCS"; |
| assert positionCount > 0 : "endOffset() called before nextPosition()!"; |
| return super.endOffset(); |
| } |
| |
| @Override |
| public BytesRef getPayload() throws IOException { |
| assert state != DocsEnumState.START : "getPayload() called before nextDoc()/advance()"; |
| assert state != DocsEnumState.FINISHED : "getPayload() called after NO_MORE_DOCS"; |
| assert positionCount > 0 : "getPayload() called before nextPosition()!"; |
| BytesRef payload = super.getPayload(); |
| assert payload == null || payload.length > 0 : "getPayload() returned payload with invalid length!"; |
| return payload; |
| } |
| |
| void reset() { |
| state = DocsEnumState.START; |
| doc = in.docID(); |
| positionCount = positionMax = 0; |
| } |
| } |
| |
| /** Wraps a {@link ImpactsEnum} with additional checks */ |
| public static class AssertingImpactsEnum extends ImpactsEnum { |
| |
| private final AssertingPostingsEnum assertingPostings; |
| private final ImpactsEnum in; |
| private int lastShallowTarget = -1; |
| |
| AssertingImpactsEnum(ImpactsEnum impacts) { |
| in = impacts; |
| // inherit checks from AssertingPostingsEnum |
| assertingPostings = new AssertingPostingsEnum(impacts); |
| } |
| |
| @Override |
| public void advanceShallow(int target) throws IOException { |
| assert target >= lastShallowTarget : "called on decreasing targets: target = " + target + " < last target = " + lastShallowTarget; |
| assert target >= docID() : "target = " + target + " < docID = " + docID(); |
| lastShallowTarget = target; |
| in.advanceShallow(target); |
| } |
| |
| @Override |
| public Impacts getImpacts() throws IOException { |
| assert docID() >= 0 || lastShallowTarget >= 0 : "Cannot get impacts until the iterator is positioned or advanceShallow has been called"; |
| Impacts impacts = in.getImpacts(); |
| CheckIndex.checkImpacts(impacts, Math.max(docID(), lastShallowTarget)); |
| return new AssertingImpacts(impacts, this); |
| } |
| |
| @Override |
| public int freq() throws IOException { |
| return assertingPostings.freq(); |
| } |
| |
| @Override |
| public int nextPosition() throws IOException { |
| return assertingPostings.nextPosition(); |
| } |
| |
| @Override |
| public int startOffset() throws IOException { |
| return assertingPostings.startOffset(); |
| } |
| |
| @Override |
| public int endOffset() throws IOException { |
| return assertingPostings.endOffset(); |
| } |
| |
| @Override |
| public BytesRef getPayload() throws IOException { |
| return assertingPostings.getPayload(); |
| } |
| |
| @Override |
| public int docID() { |
| return assertingPostings.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assert docID() + 1 >= lastShallowTarget : "target = " + (docID() + 1) + " < last shallow target = " + lastShallowTarget; |
| return assertingPostings.nextDoc(); |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assert target >= lastShallowTarget : "target = " + target + " < last shallow target = " + lastShallowTarget; |
| return assertingPostings.advance(target); |
| } |
| |
| @Override |
| public long cost() { |
| return assertingPostings.cost(); |
| } |
| } |
| |
| static class AssertingImpacts extends Impacts { |
| |
| private final Impacts in; |
| private final AssertingImpactsEnum impactsEnum; |
| private final int validFor; |
| |
| AssertingImpacts(Impacts in, AssertingImpactsEnum impactsEnum) { |
| this.in = in; |
| this.impactsEnum = impactsEnum; |
| validFor = Math.max(impactsEnum.docID(), impactsEnum.lastShallowTarget); |
| } |
| |
| @Override |
| public int numLevels() { |
| assert validFor == Math.max(impactsEnum.docID(), impactsEnum.lastShallowTarget) : "Cannot reuse impacts after advancing the iterator"; |
| return in.numLevels(); |
| } |
| |
| @Override |
| public int getDocIdUpTo(int level) { |
| assert validFor == Math.max(impactsEnum.docID(), impactsEnum.lastShallowTarget) : "Cannot reuse impacts after advancing the iterator"; |
| return in.getDocIdUpTo(level); |
| } |
| |
| @Override |
| public List<Impact> getImpacts(int level) { |
| assert validFor == Math.max(impactsEnum.docID(), impactsEnum.lastShallowTarget) : "Cannot reuse impacts after advancing the iterator"; |
| return in.getImpacts(level); |
| } |
| |
| } |
| |
| /** Wraps a NumericDocValues but with additional asserts */ |
| public static class AssertingNumericDocValues extends NumericDocValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final NumericDocValues in; |
| private final int maxDoc; |
| private int lastDocID = -1; |
| private boolean exists; |
| |
| public AssertingNumericDocValues(NumericDocValues in, int maxDoc) { |
| this.in = in; |
| this.maxDoc = maxDoc; |
| // should start unpositioned: |
| assert in.docID() == -1; |
| } |
| |
| @Override |
| public int docID() { |
| assertThread("Numeric doc values", creationThread); |
| return in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| int docID = in.nextDoc(); |
| assert docID > lastDocID; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| assert docID == in.docID(); |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target > in.docID(); |
| int docID = in.advance(target); |
| assert docID >= target; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public boolean advanceExact(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target >= in.docID(); |
| assert target < maxDoc; |
| exists = in.advanceExact(target); |
| assert in.docID() == target; |
| lastDocID = target; |
| return exists; |
| } |
| |
| @Override |
| public long cost() { |
| assertThread("Numeric doc values", creationThread); |
| long cost = in.cost(); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public long longValue() throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert exists; |
| return in.longValue(); |
| } |
| |
| @Override |
| public String toString() { |
| return "AssertingNumericDocValues(" + in + ")"; |
| } |
| } |
| |
| /** Wraps a BinaryDocValues but with additional asserts */ |
| public static class AssertingBinaryDocValues extends BinaryDocValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final BinaryDocValues in; |
| private final int maxDoc; |
| private int lastDocID = -1; |
| private boolean exists; |
| |
| public AssertingBinaryDocValues(BinaryDocValues in, int maxDoc) { |
| this.in = in; |
| this.maxDoc = maxDoc; |
| // should start unpositioned: |
| assert in.docID() == -1; |
| } |
| |
| @Override |
| public int docID() { |
| assertThread("Binary doc values", creationThread); |
| return in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Binary doc values", creationThread); |
| int docID = in.nextDoc(); |
| assert docID > lastDocID; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| assert docID == in.docID(); |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Binary doc values", creationThread); |
| assert target >= 0; |
| assert target > in.docID(); |
| int docID = in.advance(target); |
| assert docID >= target; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public boolean advanceExact(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target >= in.docID(); |
| assert target < maxDoc; |
| exists = in.advanceExact(target); |
| assert in.docID() == target; |
| lastDocID = target; |
| return exists; |
| } |
| |
| @Override |
| public long cost() { |
| assertThread("Binary doc values", creationThread); |
| long cost = in.cost(); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public BytesRef binaryValue() throws IOException { |
| assertThread("Binary doc values", creationThread); |
| assert exists; |
| return in.binaryValue(); |
| } |
| |
| @Override |
| public String toString() { |
| return "AssertingBinaryDocValues(" + in + ")"; |
| } |
| } |
| |
| /** Wraps a SortedDocValues but with additional asserts */ |
| public static class AssertingSortedDocValues extends SortedDocValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final SortedDocValues in; |
| private final int maxDoc; |
| private final int valueCount; |
| private int lastDocID = -1; |
| private boolean exists; |
| |
| public AssertingSortedDocValues(SortedDocValues in, int maxDoc) { |
| this.in = in; |
| this.maxDoc = maxDoc; |
| this.valueCount = in.getValueCount(); |
| assert valueCount >= 0 && valueCount <= maxDoc; |
| } |
| |
| @Override |
| public int docID() { |
| assertThread("Sorted doc values", creationThread); |
| return in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| int docID = in.nextDoc(); |
| assert docID > lastDocID; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| assert docID == in.docID(); |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| assert target >= 0; |
| assert target > in.docID(); |
| int docID = in.advance(target); |
| assert docID >= target; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| lastDocID = docID; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public boolean advanceExact(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target >= in.docID(); |
| assert target < maxDoc; |
| exists = in.advanceExact(target); |
| assert in.docID() == target; |
| lastDocID = target; |
| return exists; |
| } |
| |
| @Override |
| public long cost() { |
| assertThread("Sorted doc values", creationThread); |
| long cost = in.cost(); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public int ordValue() throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| assert exists; |
| int ord = in.ordValue(); |
| assert ord >= -1 && ord < valueCount; |
| return ord; |
| } |
| |
| @Override |
| public BytesRef lookupOrd(int ord) throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| assert ord >= 0 && ord < valueCount; |
| final BytesRef result = in.lookupOrd(ord); |
| assert result.isValid(); |
| return result; |
| } |
| |
| @Override |
| public int getValueCount() { |
| assertThread("Sorted doc values", creationThread); |
| int valueCount = in.getValueCount(); |
| assert valueCount == this.valueCount; // should not change |
| return valueCount; |
| } |
| |
| @Override |
| public BytesRef binaryValue() throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| final BytesRef result = in.binaryValue(); |
| assert result.isValid(); |
| return result; |
| } |
| |
| @Override |
| public int lookupTerm(BytesRef key) throws IOException { |
| assertThread("Sorted doc values", creationThread); |
| assert key.isValid(); |
| int result = in.lookupTerm(key); |
| assert result < valueCount; |
| assert key.isValid(); |
| return result; |
| } |
| } |
| |
| /** Wraps a SortedNumericDocValues but with additional asserts */ |
| public static class AssertingSortedNumericDocValues extends SortedNumericDocValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final SortedNumericDocValues in; |
| private final int maxDoc; |
| private int lastDocID = -1; |
| private int valueUpto; |
| private boolean exists; |
| |
| private AssertingSortedNumericDocValues(SortedNumericDocValues in, int maxDoc) { |
| this.in = in; |
| this.maxDoc = maxDoc; |
| } |
| |
| public static SortedNumericDocValues create(SortedNumericDocValues in, int maxDoc) { |
| NumericDocValues singleDocValues = DocValues.unwrapSingleton(in); |
| if (singleDocValues == null) { |
| return new AssertingSortedNumericDocValues(in, maxDoc); |
| } else { |
| NumericDocValues assertingDocValues = new AssertingNumericDocValues(singleDocValues, maxDoc); |
| return DocValues.singleton(assertingDocValues); |
| } |
| } |
| |
| @Override |
| public int docID() { |
| return in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Sorted numeric doc values", creationThread); |
| int docID = in.nextDoc(); |
| assert docID > lastDocID; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| assert docID == in.docID(); |
| lastDocID = docID; |
| valueUpto = 0; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Sorted numeric doc values", creationThread); |
| assert target >= 0; |
| assert target > in.docID(); |
| int docID = in.advance(target); |
| assert docID == in.docID(); |
| assert docID >= target; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| lastDocID = docID; |
| valueUpto = 0; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public boolean advanceExact(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target >= in.docID(); |
| assert target < maxDoc; |
| exists = in.advanceExact(target); |
| assert in.docID() == target; |
| lastDocID = target; |
| valueUpto = 0; |
| return exists; |
| } |
| |
| @Override |
| public long cost() { |
| assertThread("Sorted numeric doc values", creationThread); |
| long cost = in.cost(); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public long nextValue() throws IOException { |
| assertThread("Sorted numeric doc values", creationThread); |
| assert exists; |
| assert valueUpto < in.docValueCount(): "valueUpto=" + valueUpto + " in.docValueCount()=" + in.docValueCount(); |
| valueUpto++; |
| return in.nextValue(); |
| } |
| |
| @Override |
| public int docValueCount() { |
| assertThread("Sorted numeric doc values", creationThread); |
| assert exists; |
| assert in.docValueCount() > 0; |
| return in.docValueCount(); |
| } |
| } |
| |
| /** Wraps a SortedSetDocValues but with additional asserts */ |
| public static class AssertingSortedSetDocValues extends SortedSetDocValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final SortedSetDocValues in; |
| private final int maxDoc; |
| private final long valueCount; |
| private int lastDocID = -1; |
| private long lastOrd = NO_MORE_ORDS; |
| private boolean exists; |
| |
| private AssertingSortedSetDocValues(SortedSetDocValues in, int maxDoc) { |
| this.in = in; |
| this.maxDoc = maxDoc; |
| this.valueCount = in.getValueCount(); |
| assert valueCount >= 0; |
| } |
| |
| public static SortedSetDocValues create(SortedSetDocValues in, int maxDoc) { |
| SortedDocValues singleDocValues = DocValues.unwrapSingleton(in); |
| if (singleDocValues == null) { |
| return new AssertingSortedSetDocValues(in, maxDoc); |
| } else { |
| SortedDocValues assertingDocValues = new AssertingSortedDocValues(singleDocValues, maxDoc); |
| return DocValues.singleton(assertingDocValues); |
| } |
| } |
| |
| @Override |
| public int docID() { |
| assertThread("Sorted set doc values", creationThread); |
| return in.docID(); |
| } |
| |
| @Override |
| public int nextDoc() throws IOException { |
| assertThread("Sorted set doc values", creationThread); |
| int docID = in.nextDoc(); |
| assert docID > lastDocID; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| assert docID == in.docID(); |
| lastDocID = docID; |
| lastOrd = -2; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public int advance(int target) throws IOException { |
| assertThread("Sorted set doc values", creationThread); |
| assert target >= 0; |
| assert target > in.docID(); |
| int docID = in.advance(target); |
| assert docID == in.docID(); |
| assert docID >= target; |
| assert docID == NO_MORE_DOCS || docID < maxDoc; |
| lastDocID = docID; |
| lastOrd = -2; |
| exists = docID != NO_MORE_DOCS; |
| return docID; |
| } |
| |
| @Override |
| public boolean advanceExact(int target) throws IOException { |
| assertThread("Numeric doc values", creationThread); |
| assert target >= 0; |
| assert target >= in.docID(); |
| assert target < maxDoc; |
| exists = in.advanceExact(target); |
| assert in.docID() == target; |
| lastDocID = target; |
| lastOrd = -2; |
| return exists; |
| } |
| |
| @Override |
| public long cost() { |
| assertThread("Sorted set doc values", creationThread); |
| long cost = in.cost(); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public long nextOrd() throws IOException { |
| assertThread("Sorted set doc values", creationThread); |
| assert lastOrd != NO_MORE_ORDS; |
| assert exists; |
| long ord = in.nextOrd(); |
| assert ord < valueCount; |
| assert ord == NO_MORE_ORDS || ord > lastOrd; |
| lastOrd = ord; |
| return ord; |
| } |
| |
| @Override |
| public BytesRef lookupOrd(long ord) throws IOException { |
| assertThread("Sorted set doc values", creationThread); |
| assert ord >= 0 && ord < valueCount; |
| final BytesRef result = in.lookupOrd(ord); |
| assert result.isValid(); |
| return result; |
| } |
| |
| @Override |
| public long getValueCount() { |
| assertThread("Sorted set doc values", creationThread); |
| long valueCount = in.getValueCount(); |
| assert valueCount == this.valueCount; // should not change |
| return valueCount; |
| } |
| |
| @Override |
| public long lookupTerm(BytesRef key) throws IOException { |
| assertThread("Sorted set doc values", creationThread); |
| assert key.isValid(); |
| long result = in.lookupTerm(key); |
| assert result < valueCount; |
| assert key.isValid(); |
| return result; |
| } |
| } |
| |
| /** Wraps a SortedSetDocValues but with additional asserts */ |
| public static class AssertingPointValues extends PointValues { |
| private final Thread creationThread = Thread.currentThread(); |
| private final PointValues in; |
| |
| /** Sole constructor. */ |
| public AssertingPointValues(PointValues in, int maxDoc) { |
| this.in = in; |
| assertStats(maxDoc); |
| } |
| |
| public PointValues getWrapped() { return in; } |
| |
| private void assertStats(int maxDoc) { |
| assert in.size() > 0; |
| assert in.getDocCount() > 0; |
| assert in.getDocCount() <= in.size(); |
| assert in.getDocCount() <= maxDoc; |
| } |
| |
| @Override |
| public void intersect(IntersectVisitor visitor) throws IOException { |
| assertThread("Points", creationThread); |
| in.intersect(new AssertingIntersectVisitor(in.getNumDimensions(), in.getNumIndexDimensions(), in.getBytesPerDimension(), visitor)); |
| } |
| |
| @Override |
| public long estimatePointCount(IntersectVisitor visitor) { |
| assertThread("Points", creationThread); |
| long cost = in.estimatePointCount(visitor); |
| assert cost >= 0; |
| return cost; |
| } |
| |
| @Override |
| public byte[] getMinPackedValue() throws IOException { |
| assertThread("Points", creationThread); |
| return Objects.requireNonNull(in.getMinPackedValue()); |
| } |
| |
| @Override |
| public byte[] getMaxPackedValue() throws IOException { |
| assertThread("Points", creationThread); |
| return Objects.requireNonNull(in.getMaxPackedValue()); |
| } |
| |
| @Override |
| public int getNumDimensions() throws IOException { |
| assertThread("Points", creationThread); |
| return in.getNumDimensions(); |
| } |
| |
| @Override |
| public int getNumIndexDimensions() throws IOException { |
| assertThread("Points", creationThread); |
| return in.getNumIndexDimensions(); |
| } |
| |
| @Override |
| public int getBytesPerDimension() throws IOException { |
| assertThread("Points", creationThread); |
| return in.getBytesPerDimension(); |
| } |
| |
| @Override |
| public long size() { |
| assertThread("Points", creationThread); |
| return in.size(); |
| } |
| |
| @Override |
| public int getDocCount() { |
| assertThread("Points", creationThread); |
| return in.getDocCount(); |
| } |
| |
| } |
| |
| /** Validates in the 1D case that all points are visited in order, and point values are in bounds of the last cell checked */ |
| static class AssertingIntersectVisitor implements IntersectVisitor { |
| final IntersectVisitor in; |
| final int numDataDims; |
| final int numIndexDims; |
| final int bytesPerDim; |
| final byte[] lastDocValue; |
| final byte[] lastMinPackedValue; |
| final byte[] lastMaxPackedValue; |
| private Relation lastCompareResult; |
| private int lastDocID = -1; |
| private int docBudget; |
| |
| AssertingIntersectVisitor(int numDataDims, int numIndexDims, int bytesPerDim, IntersectVisitor in) { |
| this.in = in; |
| this.numDataDims = numDataDims; |
| this.numIndexDims = numIndexDims; |
| this.bytesPerDim = bytesPerDim; |
| lastMaxPackedValue = new byte[numDataDims*bytesPerDim]; |
| lastMinPackedValue = new byte[numDataDims*bytesPerDim]; |
| if (numDataDims == 1) { |
| lastDocValue = new byte[bytesPerDim]; |
| } else { |
| lastDocValue = null; |
| } |
| } |
| |
| @Override |
| public void visit(int docID) throws IOException { |
| assert --docBudget >= 0 : "called add() more times than the last call to grow() reserved"; |
| |
| // This method, not filtering each hit, should only be invoked when the cell is inside the query shape: |
| assert lastCompareResult == Relation.CELL_INSIDE_QUERY; |
| in.visit(docID); |
| } |
| |
| @Override |
| public void visit(int docID, byte[] packedValue) throws IOException { |
| assert --docBudget >= 0 : "called add() more times than the last call to grow() reserved"; |
| |
| // This method, to filter each doc's value, should only be invoked when the cell crosses the query shape: |
| assert lastCompareResult == PointValues.Relation.CELL_CROSSES_QUERY; |
| |
| // This doc's packed value should be contained in the last cell passed to compare: |
| for(int dim=0;dim<numIndexDims;dim++) { |
| assert FutureArrays.compareUnsigned(lastMinPackedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim, packedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim) <= 0: "dim=" + dim + " of " + numDataDims + " value=" + new BytesRef(packedValue); |
| assert FutureArrays.compareUnsigned(lastMaxPackedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim, packedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim) >= 0: "dim=" + dim + " of " + numDataDims + " value=" + new BytesRef(packedValue); |
| } |
| |
| // TODO: we should assert that this "matches" whatever relation the last call to compare had returned |
| assert packedValue.length == numDataDims * bytesPerDim; |
| if (numDataDims == 1) { |
| int cmp = FutureArrays.compareUnsigned(lastDocValue, 0, bytesPerDim, packedValue, 0, bytesPerDim); |
| if (cmp < 0) { |
| // ok |
| } else if (cmp == 0) { |
| assert lastDocID <= docID: "doc ids are out of order when point values are the same!"; |
| } else { |
| // out of order! |
| assert false: "point values are out of order"; |
| } |
| System.arraycopy(packedValue, 0, lastDocValue, 0, bytesPerDim); |
| lastDocID = docID; |
| } |
| in.visit(docID, packedValue); |
| } |
| |
| @Override |
| public void grow(int count) { |
| in.grow(count); |
| docBudget = count; |
| } |
| |
| @Override |
| public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) { |
| for(int dim=0;dim<numIndexDims;dim++) { |
| assert FutureArrays.compareUnsigned(minPackedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim, maxPackedValue, dim * bytesPerDim, dim * bytesPerDim + bytesPerDim) <= 0; |
| } |
| System.arraycopy(maxPackedValue, 0, lastMaxPackedValue, 0, numIndexDims*bytesPerDim); |
| System.arraycopy(minPackedValue, 0, lastMinPackedValue, 0, numIndexDims*bytesPerDim); |
| lastCompareResult = in.compare(minPackedValue, maxPackedValue); |
| return lastCompareResult; |
| } |
| } |
| |
| @Override |
| public NumericDocValues getNumericDocValues(String field) throws IOException { |
| NumericDocValues dv = super.getNumericDocValues(field); |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.getDocValuesType() == DocValuesType.NUMERIC; |
| return new AssertingNumericDocValues(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.getDocValuesType() != DocValuesType.NUMERIC; |
| return null; |
| } |
| } |
| |
| @Override |
| public BinaryDocValues getBinaryDocValues(String field) throws IOException { |
| BinaryDocValues dv = super.getBinaryDocValues(field); |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.getDocValuesType() == DocValuesType.BINARY; |
| return new AssertingBinaryDocValues(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.getDocValuesType() != DocValuesType.BINARY; |
| return null; |
| } |
| } |
| |
| @Override |
| public SortedDocValues getSortedDocValues(String field) throws IOException { |
| SortedDocValues dv = super.getSortedDocValues(field); |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.getDocValuesType() == DocValuesType.SORTED; |
| return new AssertingSortedDocValues(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.getDocValuesType() != DocValuesType.SORTED; |
| return null; |
| } |
| } |
| |
| @Override |
| public SortedNumericDocValues getSortedNumericDocValues(String field) throws IOException { |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| SortedNumericDocValues dv = super.getSortedNumericDocValues(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.getDocValuesType() == DocValuesType.SORTED_NUMERIC; |
| return AssertingSortedNumericDocValues.create(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.getDocValuesType() != DocValuesType.SORTED_NUMERIC; |
| return null; |
| } |
| } |
| |
| @Override |
| public SortedSetDocValues getSortedSetDocValues(String field) throws IOException { |
| SortedSetDocValues dv = super.getSortedSetDocValues(field); |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.getDocValuesType() == DocValuesType.SORTED_SET; |
| return new AssertingSortedSetDocValues(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.getDocValuesType() != DocValuesType.SORTED_SET; |
| return null; |
| } |
| } |
| |
| @Override |
| public NumericDocValues getNormValues(String field) throws IOException { |
| NumericDocValues dv = super.getNormValues(field); |
| FieldInfo fi = getFieldInfos().fieldInfo(field); |
| if (dv != null) { |
| assert fi != null; |
| assert fi.hasNorms(); |
| return new AssertingNumericDocValues(dv, maxDoc()); |
| } else { |
| assert fi == null || fi.hasNorms() == false; |
| return null; |
| } |
| } |
| |
| @Override |
| public PointValues getPointValues(String field) throws IOException { |
| PointValues values = in.getPointValues(field); |
| if (values == null) { |
| return null; |
| } |
| return new AssertingPointValues(values, maxDoc()); |
| } |
| |
| /** Wraps a Bits but with additional asserts */ |
| public static class AssertingBits implements Bits { |
| private final Thread creationThread = Thread.currentThread(); |
| final Bits in; |
| |
| public AssertingBits(Bits in) { |
| this.in = in; |
| } |
| |
| @Override |
| public boolean get(int index) { |
| assertThread("Bits", creationThread); |
| assert index >= 0 && index < length(); |
| return in.get(index); |
| } |
| |
| @Override |
| public int length() { |
| assertThread("Bits", creationThread); |
| return in.length(); |
| } |
| } |
| |
| @Override |
| public Bits getLiveDocs() { |
| Bits liveDocs = super.getLiveDocs(); |
| if (liveDocs != null) { |
| assert maxDoc() == liveDocs.length(); |
| liveDocs = new AssertingBits(liveDocs); |
| } else { |
| assert maxDoc() == numDocs(); |
| assert !hasDeletions(); |
| } |
| return liveDocs; |
| } |
| |
| // we don't change behavior of the reader: just validate the API. |
| |
| @Override |
| public CacheHelper getCoreCacheHelper() { |
| return in.getCoreCacheHelper(); |
| } |
| |
| @Override |
| public CacheHelper getReaderCacheHelper() { |
| return in.getReaderCacheHelper(); |
| } |
| } |