blob: b6c01f9adf76dc2d0894dda44982b0f4cb7ffa8c [file] [log] [blame]
/*
* 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();
}
}