blob: 0bbf2c6faf6494a8640dc7ce1c6facdd23195bdb [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.codecs.asserting;
import java.io.IOException;
import java.util.Collection;
import org.apache.lucene.codecs.PointsFormat;
import org.apache.lucene.codecs.PointsReader;
import org.apache.lucene.codecs.PointsWriter;
import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.index.MergeState;
import org.apache.lucene.index.PointValues.IntersectVisitor;
import org.apache.lucene.index.PointValues.Relation;
import org.apache.lucene.index.PointValues;
import org.apache.lucene.index.SegmentReadState;
import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.StringHelper;
import org.apache.lucene.util.TestUtil;
/**
* Just like the default point format but with additional asserts.
*/
public final class AssertingPointsFormat extends PointsFormat {
private final PointsFormat in;
/** Create a new AssertingPointsFormat */
public AssertingPointsFormat() {
this(TestUtil.getDefaultCodec().pointsFormat());
}
/**
* Expert: Create an AssertingPointsFormat.
* This is only intended to pass special parameters for testing.
*/
// TODO: can we randomize this a cleaner way? e.g. stored fields and vectors do
// this with a separate codec...
public AssertingPointsFormat(PointsFormat in) {
this.in = in;
}
@Override
public PointsWriter fieldsWriter(SegmentWriteState state) throws IOException {
return new AssertingPointsWriter(state, in.fieldsWriter(state));
}
@Override
public PointsReader fieldsReader(SegmentReadState state) throws IOException {
return new AssertingPointsReader(state.segmentInfo.maxDoc(), in.fieldsReader(state));
}
/** 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 numDims;
final int bytesPerDim;
final byte[] lastDocValue;
final byte[] lastMinPackedValue;
final byte[] lastMaxPackedValue;
private Relation lastCompareResult;
private int lastDocID = -1;
private int docBudget;
public AssertingIntersectVisitor(int numDims, int bytesPerDim, IntersectVisitor in) {
this.in = in;
this.numDims = numDims;
this.bytesPerDim = bytesPerDim;
lastMaxPackedValue = new byte[numDims*bytesPerDim];
lastMinPackedValue = new byte[numDims*bytesPerDim];
if (numDims == 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<numDims;dim++) {
assert StringHelper.compare(bytesPerDim, lastMinPackedValue, dim*bytesPerDim, packedValue, dim*bytesPerDim) <= 0: "dim=" + dim + " of " + numDims + " value=" + new BytesRef(packedValue);
assert StringHelper.compare(bytesPerDim, lastMaxPackedValue, dim*bytesPerDim, packedValue, dim*bytesPerDim) >= 0: "dim=" + dim + " of " + numDims + " value=" + new BytesRef(packedValue);
}
// TODO: we should assert that this "matches" whatever relation the last call to compare had returned
assert packedValue.length == numDims * bytesPerDim;
if (numDims == 1) {
int cmp = StringHelper.compare(bytesPerDim, lastDocValue, 0, packedValue, 0);
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<numDims;dim++) {
assert StringHelper.compare(bytesPerDim, minPackedValue, dim*bytesPerDim, maxPackedValue, dim*bytesPerDim) <= 0;
}
System.arraycopy(maxPackedValue, 0, lastMaxPackedValue, 0, numDims*bytesPerDim);
System.arraycopy(minPackedValue, 0, lastMinPackedValue, 0, numDims*bytesPerDim);
lastCompareResult = in.compare(minPackedValue, maxPackedValue);
return lastCompareResult;
}
}
static class AssertingPointsReader extends PointsReader {
private final PointsReader in;
private final int maxDoc;
AssertingPointsReader(int maxDoc, PointsReader in) {
this.in = in;
this.maxDoc = maxDoc;
// do a few simple checks on init
assert toString() != null;
assert ramBytesUsed() >= 0;
assert getChildResources() != null;
}
@Override
public void close() throws IOException {
in.close();
in.close(); // close again
}
@Override
public void intersect(String fieldName, IntersectVisitor visitor) throws IOException {
in.intersect(fieldName,
new AssertingIntersectVisitor(in.getNumDimensions(fieldName), in.getBytesPerDimension(fieldName), visitor));
}
@Override
public long ramBytesUsed() {
long v = in.ramBytesUsed();
assert v >= 0;
return v;
}
@Override
public Collection<Accountable> getChildResources() {
Collection<Accountable> res = in.getChildResources();
TestUtil.checkReadOnly(res);
return res;
}
@Override
public void checkIntegrity() throws IOException {
in.checkIntegrity();
}
@Override
public PointsReader getMergeInstance() throws IOException {
return new AssertingPointsReader(maxDoc, in.getMergeInstance());
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + in.toString() + ")";
}
@Override
public byte[] getMinPackedValue(String fieldName) throws IOException {
assertStats(fieldName);
return in.getMinPackedValue(fieldName);
}
@Override
public byte[] getMaxPackedValue(String fieldName) throws IOException {
assertStats(fieldName);
return in.getMaxPackedValue(fieldName);
}
@Override
public int getNumDimensions(String fieldName) throws IOException {
assertStats(fieldName);
return in.getNumDimensions(fieldName);
}
@Override
public int getBytesPerDimension(String fieldName) throws IOException {
assertStats(fieldName);
return in.getBytesPerDimension(fieldName);
}
@Override
public long size(String fieldName) {
assertStats(fieldName);
return in.size(fieldName);
}
@Override
public int getDocCount(String fieldName) {
assertStats(fieldName);
return in.getDocCount(fieldName);
}
private void assertStats(String fieldName) {
assert in.size(fieldName) >= 0;
assert in.getDocCount(fieldName) >= 0;
assert in.getDocCount(fieldName) <= in.size(fieldName);
assert in.getDocCount(fieldName) <= maxDoc;
}
}
static class AssertingPointsWriter extends PointsWriter {
private final PointsWriter in;
AssertingPointsWriter(SegmentWriteState writeState, PointsWriter in) {
this.in = in;
}
@Override
public void writeField(FieldInfo fieldInfo, PointsReader values, double maxMBSortInHeap) throws IOException {
if (fieldInfo.getPointDimensionCount() == 0) {
throw new IllegalArgumentException("writing field=\"" + fieldInfo.name + "\" but pointDimensionalCount is 0");
}
in.writeField(fieldInfo, values, maxMBSortInHeap);
}
@Override
public void merge(MergeState mergeState) throws IOException {
in.merge(mergeState);
}
@Override
public void finish() throws IOException {
in.finish();
}
@Override
public void close() throws IOException {
in.close();
in.close(); // close again
}
}
}