LUCENE-7966: Build Multi-Release JARs to enable usage of optimized intrinsic methods from Java 9 for index bounds checking and array comparison/mismatch

This commit is contained in:
Uwe Schindler 2018-02-03 09:17:59 +01:00
parent ce5932fc37
commit 9c0797d9f4
43 changed files with 1072 additions and 438 deletions

View File

@ -874,4 +874,5 @@ Test args: [${args}]</echo>
<!-- useless targets (override common-build.xml): --> <!-- useless targets (override common-build.xml): -->
<target name="generate-test-reports"/> <target name="generate-test-reports"/>
<target name="patch-mrjar-classes"/>
</project> </project>

View File

@ -135,6 +135,12 @@ Improvements
* LUCENE-8129: A Unicode set filter can now be specified when using ICUFoldingFilter. * LUCENE-8129: A Unicode set filter can now be specified when using ICUFoldingFilter.
(Ere Maijala) (Ere Maijala)
* LUCENE-7966: Build Multi-Release JARs to enable usage of optimized intrinsic methods
from Java 9 for index bounds checking and array comparison/mismatch. This change
introduces Java 8 replacements for those Java 9 methods and patches the compiled
classes to use the optimized variants through the MR-JAR mechanism.
(Uwe Schindler, Robert Muir, Adrien Grand, Mike McCandless)
Bug Fixes Bug Fixes
* LUCENE-8077: Fixed bug in how CheckIndex verifies doc-value iterators. * LUCENE-8077: Fixed bug in how CheckIndex verifies doc-value iterators.

View File

@ -557,7 +557,7 @@
</target> </target>
<!-- Override common-build.xml definition to check for the jar already being up-to-date --> <!-- Override common-build.xml definition to check for the jar already being up-to-date -->
<target name="jar-core" depends="check-lucene-core-uptodate,compile-lucene-core" unless="lucene-core.uptodate"> <target name="jar-core" depends="resolve-groovy,check-lucene-core-uptodate,compile-lucene-core" unless="lucene-core.uptodate">
<ant dir="${common.dir}/core" target="jar-core" inheritAll="false"> <ant dir="${common.dir}/core" target="jar-core" inheritAll="false">
<propertyset refid="uptodate.and.compiled.properties"/> <propertyset refid="uptodate.and.compiled.properties"/>
</ant> </ant>

View File

@ -150,7 +150,13 @@ public class FixedGapTermsIndexWriter extends TermsIndexWriterBase {
@Override @Override
public void add(BytesRef text, TermStats stats, long termsFilePointer) throws IOException { public void add(BytesRef text, TermStats stats, long termsFilePointer) throws IOException {
final int indexedTermLength = indexedTermPrefixLength(lastTerm.get(), text); final int indexedTermLength;
if (numIndexTerms == 0) {
// no previous term: no bytes to write
indexedTermLength = 0;
} else {
indexedTermLength = indexedTermPrefixLength(lastTerm.get(), text);
}
//System.out.println("FGW: add text=" + text.utf8ToString() + " " + text + " fp=" + termsFilePointer); //System.out.println("FGW: add text=" + text.utf8ToString() + " " + text + " fp=" + termsFilePointer);
// write only the min prefix that shows the diff // write only the min prefix that shows the diff

View File

@ -36,6 +36,7 @@ import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.CharsRef; import org.apache.lucene.util.CharsRef;
import org.apache.lucene.util.CharsRefBuilder; import org.apache.lucene.util.CharsRefBuilder;
import org.apache.lucene.util.FutureArrays;
import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.RamUsageEstimator; import org.apache.lucene.util.RamUsageEstimator;
import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.StringHelper;
@ -202,8 +203,8 @@ public class SimpleTextStoredFieldsReader extends StoredFieldsReader {
} }
private boolean equalsAt(BytesRef a, BytesRef b, int bOffset) { private boolean equalsAt(BytesRef a, BytesRef b, int bOffset) {
return a.length == b.length - bOffset && return a.length == b.length - bOffset &&
ArrayUtil.equals(a.bytes, a.offset, b.bytes, b.offset + bOffset, b.length - bOffset); FutureArrays.equals(a.bytes, a.offset, a.offset + a.length, b.bytes, b.offset + bOffset, b.offset + b.length);
} }
@Override @Override

View File

@ -160,6 +160,10 @@
<!-- Display at most this many failures as a summary at the end of junit4 run. --> <!-- Display at most this many failures as a summary at the end of junit4 run. -->
<property name="tests.showNumFailures" value="10" /> <property name="tests.showNumFailures" value="10" />
<!-- If we detect Java 9+, should we test the patched classes of the
multi-release JAR or still run with our own classes? -->
<property name="tests.withJava9Patches" value="true" />
<property name="javac.deprecation" value="off"/> <property name="javac.deprecation" value="off"/>
<property name="javac.debug" value="on"/> <property name="javac.debug" value="on"/>
@ -546,10 +550,48 @@
<!-- convenience target to compile core --> <!-- convenience target to compile core -->
</target> </target>
<target name="jar-core" depends="compile-core"> <!-- Special targets to patch all class files by replacing some method calls with new Java 9 methods: -->
<target name="-mrjar-classes-uptodate">
<uptodate property="mrjar-classes-uptodate" targetfile="${build.dir}/patch-mrjar.stamp">
<srcfiles dir= "${build.dir}/classes/java" includes="**/*.class"/>
</uptodate>
</target>
<target xmlns:ivy="antlib:org.apache.ivy.ant" name="patch-mrjar-classes" depends="-mrjar-classes-uptodate,ivy-availability-check,ivy-configure,resolve-groovy,compile-core"
unless="mrjar-classes-uptodate" description="Patches compiled class files for usage with Java 9 in MR-JAR">
<loadproperties prefix="ivyversions" srcFile="${common.dir}/ivy-versions.properties"/>
<ivy:cachepath organisation="org.ow2.asm" module="asm-commons" revision="${ivyversions./org.ow2.asm/asm-commons}"
inline="true" conf="default" transitive="true" log="download-only" pathid="asm.classpath"/>
<groovy classpathref="asm.classpath" src="${common.dir}/tools/src/groovy/patch-mrjar-classes.groovy"/>
<touch file="${build.dir}/patch-mrjar.stamp"/>
</target>
<target name="-mrjar-check" depends="patch-mrjar-classes">
<zipfileset id="mrjar-patched-files" prefix="META-INF/versions/9" dir="${build.dir}/classes/java9"/>
<condition property="has-mrjar-patched-files">
<resourcecount refid="mrjar-patched-files" when="greater" count="0" />
</condition>
</target>
<target name="-mrjar-core" depends="-mrjar-check" if="has-mrjar-patched-files">
<jarify>
<filesets>
<zipfileset refid="mrjar-patched-files"/>
</filesets>
<jarify-additional-manifest-attributes>
<attribute name="Multi-Release" value="true"/>
</jarify-additional-manifest-attributes>
</jarify>
</target>
<target name="-jar-core" depends="-mrjar-check" unless="has-mrjar-patched-files">
<jarify/> <jarify/>
</target> </target>
<target name="jar-core" depends="-mrjar-core,-jar-core"/>
<!-- Packaging targets: -->
<property name="lucene.tgz.file" location="${common.dir}/dist/lucene-${version}.tgz"/> <property name="lucene.tgz.file" location="${common.dir}/dist/lucene-${version}.tgz"/>
<available file="${lucene.tgz.file}" property="lucene.tgz.exists"/> <available file="${lucene.tgz.file}" property="lucene.tgz.exists"/>
<property name="lucene.tgz.unpack.dir" location="${common.build.dir}/lucene.tgz.unpacked"/> <property name="lucene.tgz.unpack.dir" location="${common.build.dir}/lucene.tgz.unpacked"/>
@ -1453,8 +1495,8 @@ ${tests-output}/junit4-*.suites - per-JVM executed suites
</taskdef> </taskdef>
</target> </target>
<target name="test" depends="clover,compile-test,install-junit4-taskdef,validate,-init-totals,-test,-check-totals" description="Runs unit tests"/> <target name="test" depends="clover,compile-test,patch-mrjar-classes,install-junit4-taskdef,validate,-init-totals,-test,-check-totals" description="Runs unit tests"/>
<target name="beast" depends="clover,compile-test,install-junit4-taskdef,validate,-init-totals,-beast,-check-totals" description="Runs unit tests in a loop (-Dbeast.iters=n)"/> <target name="beast" depends="clover,compile-test,patch-mrjar-classes,install-junit4-taskdef,validate,-init-totals,-beast,-check-totals" description="Runs unit tests in a loop (-Dbeast.iters=n)"/>
<target name="test-nocompile" depends="-clover.disable,install-junit4-taskdef,-init-totals,-test,-check-totals" <target name="test-nocompile" depends="-clover.disable,install-junit4-taskdef,-init-totals,-test,-check-totals"
description="Only runs unit tests. Jars are not downloaded; compilation is not updated; and Clover is not enabled."/> description="Only runs unit tests. Jars are not downloaded; compilation is not updated; and Clover is not enabled."/>

View File

@ -31,10 +31,19 @@
<path id="classpath"/> <path id="classpath"/>
<!-- if we run with Java 9+, we refer to the java9 classes directory and insert this before the main classpath (to "emulate" a MR-JAR): -->
<condition property="-test.classpath.java9.addon" value="${build.dir}/classes/java9" else="${build.dir}/classes/java">
<and>
<not><equals arg1="${build.java.runtime}" arg2="1.8"/></not>
<istrue value="${tests.withJava9Patches}"/>
</and>
</condition>
<path id="test.classpath"> <path id="test.classpath">
<pathelement location="${common.dir}/build/codecs/classes/java"/> <pathelement location="${common.dir}/build/codecs/classes/java"/>
<pathelement location="${common.dir}/build/test-framework/classes/java"/> <pathelement location="${common.dir}/build/test-framework/classes/java"/>
<path refid="junit-path"/> <path refid="junit-path"/>
<pathelement location="${-test.classpath.java9.addon}"/><!-- if it's a duplicate it gets removed by Ant! -->
<pathelement location="${build.dir}/classes/java"/> <pathelement location="${build.dir}/classes/java"/>
<pathelement location="${build.dir}/classes/test"/> <pathelement location="${build.dir}/classes/test"/>
</path> </path>

View File

@ -23,6 +23,7 @@ import org.apache.lucene.util.AttributeImpl;
import org.apache.lucene.util.AttributeReflector; import org.apache.lucene.util.AttributeReflector;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.FutureObjects;
/** Default implementation of {@link CharTermAttribute}. */ /** Default implementation of {@link CharTermAttribute}. */
public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttribute, TermToBytesRefAttribute, Cloneable { public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttribute, TermToBytesRefAttribute, Cloneable {
@ -71,11 +72,7 @@ public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttr
@Override @Override
public final CharTermAttribute setLength(int length) { public final CharTermAttribute setLength(int length) {
if (length < 0) { FutureObjects.checkFromIndexSize(0, length, termBuffer.length);
throw new IllegalArgumentException("length " + length + " must not be negative");
}
if (length > termBuffer.length)
throw new IllegalArgumentException("length " + length + " exceeds the size of the termBuffer (" + termBuffer.length + ")");
termLength = length; termLength = length;
return this; return this;
} }
@ -102,15 +99,13 @@ public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttr
@Override @Override
public final char charAt(int index) { public final char charAt(int index) {
if (index >= termLength) FutureObjects.checkIndex(index, termLength);
throw new IndexOutOfBoundsException();
return termBuffer[index]; return termBuffer[index];
} }
@Override @Override
public final CharSequence subSequence(final int start, final int end) { public final CharSequence subSequence(final int start, final int end) {
if (start > termLength || end > termLength) FutureObjects.checkFromToIndex(start, end, termLength);
throw new IndexOutOfBoundsException();
return new String(termBuffer, start, end - start); return new String(termBuffer, start, end - start);
} }
@ -127,9 +122,9 @@ public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttr
public final CharTermAttribute append(CharSequence csq, int start, int end) { public final CharTermAttribute append(CharSequence csq, int start, int end) {
if (csq == null) // needed for Appendable compliance if (csq == null) // needed for Appendable compliance
csq = "null"; csq = "null";
final int len = end - start, csqlen = csq.length(); // TODO: the optimized cases (jdk methods) will already do such checks, maybe re-organize this?
if (len < 0 || start > csqlen || end > csqlen) FutureObjects.checkFromToIndex(start, end, csq.length());
throw new IndexOutOfBoundsException(); final int len = end - start;
if (len == 0) if (len == 0)
return this; return this;
resizeBuffer(termLength + len); resizeBuffer(termLength + len);

View File

@ -293,7 +293,13 @@ public final class CompressingTermVectorsWriter extends TermVectorsWriter {
@Override @Override
public void startTerm(BytesRef term, int freq) throws IOException { public void startTerm(BytesRef term, int freq) throws IOException {
assert freq >= 1; assert freq >= 1;
final int prefix = StringHelper.bytesDifference(lastTerm, term); final int prefix;
if (lastTerm.length == 0) {
// no previous term: no bytes to write
prefix = 0;
} else {
prefix = StringHelper.bytesDifference(lastTerm, term);
}
curField.addTerm(freq, prefix, term.length - prefix); curField.addTerm(freq, prefix, term.length - prefix);
termSuffixes.writeBytes(term.bytes, term.offset + prefix, term.length - prefix); termSuffixes.writeBytes(term.bytes, term.offset + prefix, term.length - prefix);
// copy last term // copy last term

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import org.apache.lucene.store.DataInput; import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.DataOutput; import org.apache.lucene.store.DataOutput;
import org.apache.lucene.util.FutureArrays;
import org.apache.lucene.util.packed.PackedInts; import org.apache.lucene.util.packed.PackedInts;
/** /**
@ -61,11 +62,8 @@ final class LZ4 {
private static int commonBytes(byte[] b, int o1, int o2, int limit) { private static int commonBytes(byte[] b, int o1, int o2, int limit) {
assert o1 < o2; assert o1 < o2;
int count = 0; // never -1 because lengths always differ
while (o2 < limit && b[o1++] == b[o2++]) { return FutureArrays.mismatch(b, o1, limit, b, o2, limit);
++count;
}
return count;
} }
private static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { private static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) {

View File

@ -504,7 +504,13 @@ final class Lucene70DocValuesConsumer extends DocValuesConsumer implements Close
for (BytesRef term = iterator.next(); term != null; term = iterator.next()) { for (BytesRef term = iterator.next(); term != null; term = iterator.next()) {
if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == 0) { if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == 0) {
writer.add(offset); writer.add(offset);
int sortKeyLength = StringHelper.sortKeyLength(previous.get(), term); final int sortKeyLength;
if (ord == 0) {
// no previous term: no bytes to write
sortKeyLength = 0;
} else {
sortKeyLength = StringHelper.sortKeyLength(previous.get(), term);
}
offset += sortKeyLength; offset += sortKeyLength;
data.writeBytes(term.bytes, term.offset, sortKeyLength); data.writeBytes(term.bytes, term.offset, sortKeyLength);
} else if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) { } else if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) {

View File

@ -26,7 +26,7 @@ import org.apache.lucene.search.PointInSetQuery;
import org.apache.lucene.search.PointRangeQuery; import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.StringHelper; import org.apache.lucene.util.FutureArrays;
/** /**
* An indexed binary field for fast range filters. If you also * An indexed binary field for fast range filters. If you also
@ -222,7 +222,7 @@ public final class BinaryPoint extends Field {
new Comparator<byte[]>() { new Comparator<byte[]>() {
@Override @Override
public int compare(byte[] a, byte[] b) { public int compare(byte[] a, byte[] b) {
return StringHelper.compare(a.length, a, 0, b, 0); return FutureArrays.compareUnsigned(a, 0, a.length, b, 0, b.length);
} }
}); });

View File

@ -19,6 +19,7 @@ package org.apache.lucene.document;
import org.apache.lucene.document.RangeFieldQuery.QueryType; import org.apache.lucene.document.RangeFieldQuery.QueryType;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
/** /**
@ -147,10 +148,7 @@ public class DoubleRange extends Field {
* @return the decoded min value * @return the decoded min value
*/ */
public double getMin(int dimension) { public double getMin(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMin(((BytesRef)fieldsData).bytes, dimension); return decodeMin(((BytesRef)fieldsData).bytes, dimension);
} }
@ -160,10 +158,7 @@ public class DoubleRange extends Field {
* @return the decoded max value * @return the decoded max value
*/ */
public double getMax(int dimension) { public double getMax(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMax(((BytesRef)fieldsData).bytes, dimension); return decodeMax(((BytesRef)fieldsData).bytes, dimension);
} }

View File

@ -19,6 +19,7 @@ package org.apache.lucene.document;
import org.apache.lucene.document.RangeFieldQuery.QueryType; import org.apache.lucene.document.RangeFieldQuery.QueryType;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
/** /**
@ -147,10 +148,7 @@ public class FloatRange extends Field {
* @return the decoded min value * @return the decoded min value
*/ */
public float getMin(int dimension) { public float getMin(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMin(((BytesRef)fieldsData).bytes, dimension); return decodeMin(((BytesRef)fieldsData).bytes, dimension);
} }
@ -160,10 +158,7 @@ public class FloatRange extends Field {
* @return the decoded max value * @return the decoded max value
*/ */
public float getMax(int dimension) { public float getMax(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMax(((BytesRef)fieldsData).bytes, dimension); return decodeMax(((BytesRef)fieldsData).bytes, dimension);
} }

View File

@ -19,6 +19,7 @@ package org.apache.lucene.document;
import org.apache.lucene.document.RangeFieldQuery.QueryType; import org.apache.lucene.document.RangeFieldQuery.QueryType;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
/** /**
@ -147,10 +148,7 @@ public class IntRange extends Field {
* @return the decoded min value * @return the decoded min value
*/ */
public int getMin(int dimension) { public int getMin(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMin(((BytesRef)fieldsData).bytes, dimension); return decodeMin(((BytesRef)fieldsData).bytes, dimension);
} }
@ -160,10 +158,7 @@ public class IntRange extends Field {
* @return the decoded max value * @return the decoded max value
*/ */
public int getMax(int dimension) { public int getMax(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMax(((BytesRef)fieldsData).bytes, dimension); return decodeMax(((BytesRef)fieldsData).bytes, dimension);
} }

View File

@ -19,6 +19,7 @@ package org.apache.lucene.document;
import org.apache.lucene.document.RangeFieldQuery.QueryType; import org.apache.lucene.document.RangeFieldQuery.QueryType;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
/** /**
@ -145,10 +146,7 @@ public class LongRange extends Field {
* @return the decoded min value * @return the decoded min value
*/ */
public long getMin(int dimension) { public long getMin(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMin(((BytesRef)fieldsData).bytes, dimension); return decodeMin(((BytesRef)fieldsData).bytes, dimension);
} }
@ -158,10 +156,7 @@ public class LongRange extends Field {
* @return the decoded max value * @return the decoded max value
*/ */
public long getMax(int dimension) { public long getMax(int dimension) {
if (dimension < 0 || dimension >= type.pointDimensionCount()/2) { FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
throw new IllegalArgumentException("dimension request (" + dimension +
") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
}
return decodeMax(((BytesRef)fieldsData).bytes, dimension); return decodeMax(((BytesRef)fieldsData).bytes, dimension);
} }

View File

@ -17,6 +17,7 @@
package org.apache.lucene.index; package org.apache.lucene.index;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FutureObjects;
/** /**
@ -39,10 +40,7 @@ final class BitsSlice implements Bits {
@Override @Override
public boolean get(int doc) { public boolean get(int doc) {
if (doc >= length) { FutureObjects.checkIndex(doc, length);
throw new RuntimeException("doc " + doc + " is out of bounds 0 .. " + (length-1));
}
assert doc < length: "doc=" + doc + " length=" + length;
return parent.get(doc+start); return parent.get(doc+start);
} }

View File

@ -31,6 +31,7 @@ import org.apache.lucene.codecs.StoredFieldsReader;
import org.apache.lucene.codecs.TermVectorsReader; import org.apache.lucene.codecs.TermVectorsReader;
import org.apache.lucene.util.Accountable; import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.Accountables; import org.apache.lucene.util.Accountables;
import org.apache.lucene.util.FutureObjects;
/** /**
* LeafReader implemented by codec APIs. * LeafReader implemented by codec APIs.
@ -94,9 +95,7 @@ public abstract class CodecReader extends LeafReader implements Accountable {
} }
private void checkBounds(int docID) { private void checkBounds(int docID) {
if (docID < 0 || docID >= maxDoc()) { FutureObjects.checkIndex(docID, maxDoc());
throw new IndexOutOfBoundsException("docID must be >= 0 and < maxDoc=" + maxDoc() + " (got docID=" + docID + ")");
}
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import org.apache.lucene.codecs.NormsProducer;
import org.apache.lucene.codecs.StoredFieldsReader; import org.apache.lucene.codecs.StoredFieldsReader;
import org.apache.lucene.codecs.TermVectorsReader; import org.apache.lucene.codecs.TermVectorsReader;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FutureObjects;
/** This is a hack to make index sorting fast, with a {@link LeafReader} that always returns merge instances when you ask for the codec readers. */ /** This is a hack to make index sorting fast, with a {@link LeafReader} that always returns merge instances when you ask for the codec readers. */
class MergeReaderWrapper extends LeafReader { class MergeReaderWrapper extends LeafReader {
@ -226,9 +227,7 @@ class MergeReaderWrapper extends LeafReader {
} }
private void checkBounds(int docID) { private void checkBounds(int docID) {
if (docID < 0 || docID >= maxDoc()) { FutureObjects.checkIndex(docID, maxDoc());
throw new IndexOutOfBoundsException("docID must be >= 0 and < maxDoc=" + maxDoc() + " (got docID=" + docID + ")");
}
} }
@Override @Override

View File

@ -26,6 +26,7 @@ import org.apache.lucene.store.RAMOutputStream;
import org.apache.lucene.util.Accountable; import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.StringHelper;
/** /**
* Prefix codes term instances (prefixes are shared). This is expected to be * Prefix codes term instances (prefixes are shared). This is expected to be
@ -74,14 +75,19 @@ public class PrefixCodedTerms implements Accountable {
assert lastTerm.equals(new Term("")) || new Term(field, bytes).compareTo(lastTerm) > 0; assert lastTerm.equals(new Term("")) || new Term(field, bytes).compareTo(lastTerm) > 0;
try { try {
int prefix = sharedPrefix(lastTerm.bytes, bytes); final int prefix;
int suffix = bytes.length - prefix; if (size > 0 && field.equals(lastTerm.field)) {
if (field.equals(lastTerm.field)) { // same field as the last term
prefix = StringHelper.bytesDifference(lastTerm.bytes, bytes);
output.writeVInt(prefix << 1); output.writeVInt(prefix << 1);
} else { } else {
output.writeVInt(prefix << 1 | 1); // field change
prefix = 0;
output.writeVInt(1);
output.writeString(field); output.writeString(field);
} }
int suffix = bytes.length - prefix;
output.writeVInt(suffix); output.writeVInt(suffix);
output.writeBytes(bytes.bytes, bytes.offset + prefix, suffix); output.writeBytes(bytes.bytes, bytes.offset + prefix, suffix);
lastTermBytes.copyBytes(bytes); lastTermBytes.copyBytes(bytes);
@ -102,20 +108,6 @@ public class PrefixCodedTerms implements Accountable {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
private int sharedPrefix(BytesRef term1, BytesRef term2) {
int pos1 = 0;
int pos1End = pos1 + Math.min(term1.length, term2.length);
int pos2 = 0;
while(pos1 < pos1End) {
if (term1.bytes[term1.offset + pos1] != term2.bytes[term2.offset + pos2]) {
return pos1;
}
pos1++;
pos2++;
}
return pos1;
}
} }
/** An iterator over the list of terms stored in a {@link PrefixCodedTerms}. */ /** An iterator over the list of terms stored in a {@link PrefixCodedTerms}. */

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.apache.lucene.util.Bits; import org.apache.lucene.util.Bits;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.PriorityQueue; import org.apache.lucene.util.PriorityQueue;
/** /**
@ -105,9 +106,7 @@ final class BooleanScorer extends BulkScorer {
} }
public BulkScorerAndDoc get(int i) { public BulkScorerAndDoc get(int i) {
if (i < 0 || i >= size()) { FutureObjects.checkIndex(i, size());
throw new IndexOutOfBoundsException();
}
return (BulkScorerAndDoc) getHeapArray()[1 + i]; return (BulkScorerAndDoc) getHeapArray()[1 + i];
} }

View File

@ -313,58 +313,6 @@ public final class ArrayUtil {
code = code * 31 + array[i]; code = code * 31 + array[i];
return code; return code;
} }
// Since Arrays.equals doesn't implement offsets for equals
/**
* See if two array slices are the same.
*
* @param left The left array to compare
* @param offsetLeft The offset into the array. Must be positive
* @param right The right array to compare
* @param offsetRight the offset into the right array. Must be positive
* @param length The length of the section of the array to compare
* @return true if the two arrays, starting at their respective offsets, are equal
*
* @see java.util.Arrays#equals(byte[], byte[])
*/
public static boolean equals(byte[] left, int offsetLeft, byte[] right, int offsetRight, int length) {
if ((offsetLeft + length <= left.length) && (offsetRight + length <= right.length)) {
for (int i = 0; i < length; i++) {
if (left[offsetLeft + i] != right[offsetRight + i]) {
return false;
}
}
return true;
}
return false;
}
// Since Arrays.equals doesn't implement offsets for equals
/**
* See if two array slices are the same.
*
* @param left The left array to compare
* @param offsetLeft The offset into the array. Must be positive
* @param right The right array to compare
* @param offsetRight the offset into the right array. Must be positive
* @param length The length of the section of the array to compare
* @return true if the two arrays, starting at their respective offsets, are equal
*
* @see java.util.Arrays#equals(char[], char[])
*/
public static boolean equals(int[] left, int offsetLeft, int[] right, int offsetRight, int length) {
if ((offsetLeft + length <= left.length) && (offsetRight + length <= right.length)) {
for (int i = 0; i < length; i++) {
if (left[offsetLeft + i] != right[offsetRight + i]) {
return false;
}
}
return true;
}
return false;
}
/** Swap values stored in slots <code>i</code> and <code>j</code> */ /** Swap values stored in slots <code>i</code> and <code>j</code> */
public static <T> void swap(T[] arr, int i, int j) { public static <T> void swap(T[] arr, int i, int j) {

View File

@ -96,20 +96,8 @@ public final class BytesRef implements Comparable<BytesRef>,Cloneable {
* @lucene.internal * @lucene.internal
*/ */
public boolean bytesEquals(BytesRef other) { public boolean bytesEquals(BytesRef other) {
assert other != null; return FutureArrays.equals(this.bytes, this.offset, this.offset + this.length,
if (length == other.length) { other.bytes, other.offset, other.offset + other.length);
int otherUpto = other.offset;
final byte[] otherBytes = other.bytes;
final int end = offset + length;
for(int upto=offset;upto<end;upto++,otherUpto++) {
if (bytes[upto] != otherBytes[otherUpto]) {
return false;
}
}
return true;
} else {
return false;
}
} }
/** /**
@ -172,27 +160,8 @@ public final class BytesRef implements Comparable<BytesRef>,Cloneable {
/** Unsigned byte order comparison */ /** Unsigned byte order comparison */
@Override @Override
public int compareTo(BytesRef other) { public int compareTo(BytesRef other) {
// TODO: Once we are on Java 9 replace this by java.util.Arrays#compareUnsigned() return FutureArrays.compareUnsigned(this.bytes, this.offset, this.offset + this.length,
// which is implemented by a Hotspot intrinsic! Also consider building a other.bytes, other.offset, other.offset + other.length);
// Multi-Release-JAR!
final byte[] aBytes = this.bytes;
int aUpto = this.offset;
final byte[] bBytes = other.bytes;
int bUpto = other.offset;
final int aStop = aUpto + Math.min(this.length, other.length);
while(aUpto < aStop) {
int aByte = aBytes[aUpto++] & 0xff;
int bByte = bBytes[bUpto++] & 0xff;
int diff = aByte - bByte;
if (diff != 0) {
return diff;
}
}
// One is a prefix of the other, or, they are equal:
return this.length - other.length;
} }
/** /**

View File

@ -93,34 +93,28 @@ public final class BytesRefArray implements SortableBytesRefArray {
* @return the <i>n'th</i> element of this {@link BytesRefArray} * @return the <i>n'th</i> element of this {@link BytesRefArray}
*/ */
public BytesRef get(BytesRefBuilder spare, int index) { public BytesRef get(BytesRefBuilder spare, int index) {
if (lastElement > index) { FutureObjects.checkIndex(index, lastElement);
int offset = offsets[index]; int offset = offsets[index];
int length = index == lastElement - 1 ? currentOffset - offset int length = index == lastElement - 1 ? currentOffset - offset
: offsets[index + 1] - offset; : offsets[index + 1] - offset;
spare.grow(length); spare.grow(length);
spare.setLength(length); spare.setLength(length);
pool.readBytes(offset, spare.bytes(), 0, spare.length()); pool.readBytes(offset, spare.bytes(), 0, spare.length());
return spare.get(); return spare.get();
}
throw new IndexOutOfBoundsException("index " + index
+ " must be less than the size: " + lastElement);
} }
/** Used only by sort below, to set a {@link BytesRef} with the specified slice, avoiding copying bytes in the common case when the slice /** Used only by sort below, to set a {@link BytesRef} with the specified slice, avoiding copying bytes in the common case when the slice
* is contained in a single block in the byte block pool. */ * is contained in a single block in the byte block pool. */
private void setBytesRef(BytesRefBuilder spare, BytesRef result, int index) { private void setBytesRef(BytesRefBuilder spare, BytesRef result, int index) {
if (index < lastElement) { FutureObjects.checkIndex(index, lastElement);
int offset = offsets[index]; int offset = offsets[index];
int length; int length;
if (index == lastElement - 1) { if (index == lastElement - 1) {
length = currentOffset - offset; length = currentOffset - offset;
} else {
length = offsets[index + 1] - offset;
}
pool.setBytesRef(spare, result, offset, length);
} else { } else {
throw new IndexOutOfBoundsException("index " + index + " must be less than the size: " + lastElement); length = offsets[index + 1] - offset;
} }
pool.setBytesRef(spare, result, offset, length);
} }
private int[] sort(final Comparator<BytesRef> comp) { private int[] sort(final Comparator<BytesRef> comp) {

View File

@ -107,46 +107,15 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
} }
public boolean charsEquals(CharsRef other) { public boolean charsEquals(CharsRef other) {
if (length == other.length) { return FutureArrays.equals(this.chars, this.offset, this.offset + this.length,
int otherUpto = other.offset; other.chars, other.offset, other.offset + other.length);
final char[] otherChars = other.chars;
final int end = offset + length;
for (int upto = offset; upto < end; upto++, otherUpto++) {
if (chars[upto] != otherChars[otherUpto]) {
return false;
}
}
return true;
} else {
return false;
}
} }
/** Signed int order comparison */ /** Signed int order comparison */
@Override @Override
public int compareTo(CharsRef other) { public int compareTo(CharsRef other) {
if (this == other) return FutureArrays.compare(this.chars, this.offset, this.offset + this.length,
return 0; other.chars, other.offset, other.offset + other.length);
final char[] aChars = this.chars;
int aUpto = this.offset;
final char[] bChars = other.chars;
int bUpto = other.offset;
final int aStop = aUpto + Math.min(this.length, other.length);
while (aUpto < aStop) {
int aInt = aChars[aUpto++];
int bInt = bChars[bUpto++];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return this.length - other.length;
} }
@Override @Override
@ -162,18 +131,14 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
@Override @Override
public char charAt(int index) { public char charAt(int index) {
// NOTE: must do a real check here to meet the specs of CharSequence // NOTE: must do a real check here to meet the specs of CharSequence
if (index < 0 || index >= length) { FutureObjects.checkIndex(index, length);
throw new IndexOutOfBoundsException();
}
return chars[offset + index]; return chars[offset + index];
} }
@Override @Override
public CharSequence subSequence(int start, int end) { public CharSequence subSequence(int start, int end) {
// NOTE: must do a real check here to meet the specs of CharSequence // NOTE: must do a real check here to meet the specs of CharSequence
if (start < 0 || end > length || start > end) { FutureObjects.checkFromToIndex(start, end, length);
throw new IndexOutOfBoundsException();
}
return new CharsRef(chars, offset + start, end - start); return new CharsRef(chars, offset + start, end - start);
} }
@ -195,40 +160,33 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
@Override @Override
public int compare(CharsRef a, CharsRef b) { public int compare(CharsRef a, CharsRef b) {
if (a == b) int aEnd = a.offset + a.length;
return 0; int bEnd = b.offset + b.length;
int i = FutureArrays.mismatch(a.chars, a.offset, aEnd,
b.chars, b.offset, bEnd);
final char[] aChars = a.chars; if (i >= 0 && i < Math.min(a.length, b.length)) {
int aUpto = a.offset; // http://icu-project.org/docs/papers/utf16_code_point_order.html
final char[] bChars = b.chars;
int bUpto = b.offset;
final int aStop = aUpto + Math.min(a.length, b.length); char aChar = a.chars[a.offset + i];
char bChar = b.chars[b.offset + i];
while (aUpto < aStop) { /* aChar != bChar, fix up each one if they're both in or above the surrogate range, then compare them */
char aChar = aChars[aUpto++]; if (aChar >= 0xd800 && bChar >= 0xd800) {
char bChar = bChars[bUpto++]; if (aChar >= 0xe000) {
if (aChar != bChar) { aChar -= 0x800;
// http://icu-project.org/docs/papers/utf16_code_point_order.html } else {
aChar += 0x2000;
/* aChar != bChar, fix up each one if they're both in or above the surrogate range, then compare them */
if (aChar >= 0xd800 && bChar >= 0xd800) {
if (aChar >= 0xe000) {
aChar -= 0x800;
} else {
aChar += 0x2000;
}
if (bChar >= 0xe000) {
bChar -= 0x800;
} else {
bChar += 0x2000;
}
} }
/* now aChar and bChar are in code point order */ if (bChar >= 0xe000) {
return (int)aChar - (int)bChar; /* int must be 32 bits wide */ bChar -= 0x800;
} else {
bChar += 0x2000;
}
} }
/* now aChar and bChar are in code point order */
return (int)aChar - (int)bChar; /* int must be 32 bits wide */
} }
// One is a prefix of the other, or, they are equal: // One is a prefix of the other, or, they are equal:

View File

@ -0,0 +1,268 @@
/*
* 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.util;
/**
* Additional methods from Java 9's <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Arrays.html">
* {@code java.util.Arrays}</a>.
* <p>
* This class will be removed when Java 9 is minimum requirement.
* Currently any bytecode is patched to use the Java 9 native
* classes through MR-JAR (Multi-Release JAR) mechanism.
* In Java 8 it will use THIS implementation.
* Because of patching, inside the Java source files we always
* refer to the Lucene implementations, but the final Lucene
* JAR files will use the native Java 9 class names when executed
* with Java 9.
* @lucene.internal
*/
public final class FutureArrays {
private FutureArrays() {} // no instance
// methods in Arrays are defined stupid: they cannot use Objects.checkFromToIndex
// they throw IAE (vs IOOBE) in the case of fromIndex > toIndex.
// so this method works just like checkFromToIndex, but with that stupidity added.
private static void checkFromToIndex(int fromIndex, int toIndex, int length) {
if (fromIndex > toIndex) {
throw new IllegalArgumentException("fromIndex " + fromIndex + " > toIndex " + toIndex);
}
if (fromIndex < 0 || toIndex > length) {
throw new IndexOutOfBoundsException("Range [" + fromIndex + ", " + toIndex + ") out-of-bounds for length " + length);
}
}
// byte[]
/**
* Behaves like Java 9's Arrays.mismatch
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#mismatch-byte:A-int-int-byte:A-int-int-">Arrays.mismatch</a>
*/
public static int mismatch(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++)
if (a[i+aFromIndex] != b[i+bFromIndex])
return i;
return aLen == bLen ? -1 : len;
}
/**
* Behaves like Java 9's Arrays.compareUnsigned
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#compareUnsigned-byte:A-int-int-byte:A-int-int-">Arrays.compareUnsigned</a>
*/
public static int compareUnsigned(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++) {
int aByte = a[i+aFromIndex] & 0xFF;
int bByte = b[i+bFromIndex] & 0xFF;
int diff = aByte - bByte;
if (diff != 0) {
return diff;
}
}
// One is a prefix of the other, or, they are equal:
return aLen - bLen;
}
/**
* Behaves like Java 9's Arrays.equals
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#equals-byte:A-int-int-byte:A-int-int-">Arrays.equals</a>
*/
public static boolean equals(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
// lengths differ: cannot be equal
if (aLen != bLen) {
return false;
}
for (int i = 0; i < aLen; i++) {
if (a[i+aFromIndex] != b[i+bFromIndex]) {
return false;
}
}
return true;
}
// char[]
/**
* Behaves like Java 9's Arrays.mismatch
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#mismatch-char:A-int-int-char:A-int-int-">Arrays.mismatch</a>
*/
public static int mismatch(char[] a, int aFromIndex, int aToIndex, char[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++)
if (a[i+aFromIndex] != b[i+bFromIndex])
return i;
return aLen == bLen ? -1 : len;
}
/**
* Behaves like Java 9's Arrays.compare
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#compare-char:A-int-int-char:A-int-int-">Arrays.compare</a>
*/
public static int compare(char[] a, int aFromIndex, int aToIndex, char[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++) {
int aInt = a[i+aFromIndex];
int bInt = b[i+bFromIndex];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return aLen - bLen;
}
/**
* Behaves like Java 9's Arrays.equals
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#equals-char:A-int-int-char:A-int-int-">Arrays.equals</a>
*/
public static boolean equals(char[] a, int aFromIndex, int aToIndex, char[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
// lengths differ: cannot be equal
if (aLen != bLen) {
return false;
}
for (int i = 0; i < aLen; i++) {
if (a[i+aFromIndex] != b[i+bFromIndex]) {
return false;
}
}
return true;
}
// int[]
/**
* Behaves like Java 9's Arrays.compare
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#compare-int:A-int-int-int:A-int-int-">Arrays.compare</a>
*/
public static int compare(int[] a, int aFromIndex, int aToIndex, int[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++) {
int aInt = a[i+aFromIndex];
int bInt = b[i+bFromIndex];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return aLen - bLen;
}
/**
* Behaves like Java 9's Arrays.equals
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#equals-int:A-int-int-int:A-int-int-">Arrays.equals</a>
*/
public static boolean equals(int[] a, int aFromIndex, int aToIndex, int[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
// lengths differ: cannot be equal
if (aLen != bLen) {
return false;
}
for (int i = 0; i < aLen; i++) {
if (a[i+aFromIndex] != b[i+bFromIndex]) {
return false;
}
}
return true;
}
// long[]
/**
* Behaves like Java 9's Arrays.compare
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#compare-long:A-int-int-long:A-int-int-">Arrays.compare</a>
*/
public static int compare(long[] a, int aFromIndex, int aToIndex, long[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
int len = Math.min(aLen, bLen);
for (int i = 0; i < len; i++) {
long aInt = a[i+aFromIndex];
long bInt = b[i+bFromIndex];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return aLen - bLen;
}
/**
* Behaves like Java 9's Arrays.equals
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Arrays.html#equals-long:A-int-int-long:A-int-int-">Arrays.equals</a>
*/
public static boolean equals(long[] a, int aFromIndex, int aToIndex, long[] b, int bFromIndex, int bToIndex) {
checkFromToIndex(aFromIndex, aToIndex, a.length);
checkFromToIndex(bFromIndex, bToIndex, b.length);
int aLen = aToIndex - aFromIndex;
int bLen = bToIndex - bFromIndex;
// lengths differ: cannot be equal
if (aLen != bLen) {
return false;
}
for (int i = 0; i < aLen; i++) {
if (a[i+aFromIndex] != b[i+bFromIndex]) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.util;
/**
* Additional methods from Java 9's <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Objects.html">
* {@code java.util.Objects}</a>.
* <p>
* This class will be removed when Java 9 is minimum requirement.
* Currently any bytecode is patched to use the Java 9 native
* classes through MR-JAR (Multi-Release JAR) mechanism.
* In Java 8 it will use THIS implementation.
* Because of patching, inside the Java source files we always
* refer to the Lucene implementations, but the final Lucene
* JAR files will use the native Java 9 class names when executed
* with Java 9.
* @lucene.internal
*/
public final class FutureObjects {
private FutureObjects() {} // no instance
/**
* Behaves like Java 9's Objects.checkIndex
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Objects.html#checkIndex-int-int-">Objects.checkIndex</a>
*/
public static int checkIndex(int index, int length) {
if (index < 0 || index >= length) {
throw new IndexOutOfBoundsException("Index " + index + " out-of-bounds for length " + length);
}
return index;
}
/**
* Behaves like Java 9's Objects.checkFromToIndex
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Objects.html#checkFromToIndex-int-int-int-">Objects.checkFromToIndex</a>
*/
public static int checkFromToIndex(int fromIndex, int toIndex, int length) {
if (fromIndex < 0 || fromIndex > toIndex || toIndex > length) {
throw new IndexOutOfBoundsException("Range [" + fromIndex + ", " + toIndex + ") out-of-bounds for length " + length);
}
return fromIndex;
}
/**
* Behaves like Java 9's Objects.checkFromIndexSize
* @see <a href="http://download.java.net/java/jdk9/docs/api/java/util/Objects.html#checkFromIndexSize-int-int-int-">Objects.checkFromIndexSize</a>
*/
public static int checkFromIndexSize(int fromIndex, int size, int length) {
int end = fromIndex + size;
if (fromIndex < 0 || fromIndex > end || end > length) {
throw new IndexOutOfBoundsException("Range [" + fromIndex + ", " + fromIndex + " + " + size + ") out-of-bounds for length " + length);
}
return fromIndex;
}
}

View File

@ -93,45 +93,15 @@ public final class IntsRef implements Comparable<IntsRef>, Cloneable {
} }
public boolean intsEquals(IntsRef other) { public boolean intsEquals(IntsRef other) {
if (length == other.length) { return FutureArrays.equals(this.ints, this.offset, this.offset + this.length,
int otherUpto = other.offset; other.ints, other.offset, other.offset + other.length);
final int[] otherInts = other.ints;
final int end = offset + length;
for(int upto=offset;upto<end;upto++,otherUpto++) {
if (ints[upto] != otherInts[otherUpto]) {
return false;
}
}
return true;
} else {
return false;
}
} }
/** Signed int order comparison */ /** Signed int order comparison */
@Override @Override
public int compareTo(IntsRef other) { public int compareTo(IntsRef other) {
if (this == other) return 0; return FutureArrays.compare(this.ints, this.offset, this.offset + this.length,
other.ints, other.offset, other.offset + other.length);
final int[] aInts = this.ints;
int aUpto = this.offset;
final int[] bInts = other.ints;
int bUpto = other.offset;
final int aStop = aUpto + Math.min(this.length, other.length);
while(aUpto < aStop) {
int aInt = aInts[aUpto++];
int bInt = bInts[bUpto++];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return this.length - other.length;
} }
@Override @Override

View File

@ -92,45 +92,15 @@ public final class LongsRef implements Comparable<LongsRef>, Cloneable {
} }
public boolean longsEquals(LongsRef other) { public boolean longsEquals(LongsRef other) {
if (length == other.length) { return FutureArrays.equals(this.longs, this.offset, this.offset + this.length,
int otherUpto = other.offset; other.longs, other.offset, other.offset + other.length);
final long[] otherInts = other.longs;
final long end = offset + length;
for(int upto=offset; upto<end; upto++,otherUpto++) {
if (longs[upto] != otherInts[otherUpto]) {
return false;
}
}
return true;
} else {
return false;
}
} }
/** Signed int order comparison */ /** Signed int order comparison */
@Override @Override
public int compareTo(LongsRef other) { public int compareTo(LongsRef other) {
if (this == other) return 0; return FutureArrays.compare(this.longs, this.offset, this.offset + this.length,
other.longs, other.offset, other.offset + other.length);
final long[] aInts = this.longs;
int aUpto = this.offset;
final long[] bInts = other.longs;
int bUpto = other.offset;
final long aStop = aUpto + Math.min(this.length, other.length);
while(aUpto < aStop) {
long aInt = aInts[aUpto++];
long bInt = bInts[bUpto++];
if (aInt > bInt) {
return 1;
} else if (aInt < bInt) {
return -1;
}
}
// One is a prefix of the other, or, they are equal:
return this.length - other.length;
} }
@Override @Override

View File

@ -34,21 +34,19 @@ public abstract class StringHelper {
/** /**
* Compares two {@link BytesRef}, element by element, and returns the * Compares two {@link BytesRef}, element by element, and returns the
* number of elements common to both arrays (from the start of each). * number of elements common to both arrays (from the start of each).
* This method assumes currentTerm comes after priorTerm.
* *
* @param left The first {@link BytesRef} to compare * @param priorTerm The first {@link BytesRef} to compare
* @param right The second {@link BytesRef} to compare * @param currentTerm The second {@link BytesRef} to compare
* @return The number of common elements (from the start of each). * @return The number of common elements (from the start of each).
*/ */
public static int bytesDifference(BytesRef left, BytesRef right) { public static int bytesDifference(BytesRef priorTerm, BytesRef currentTerm) {
int len = left.length < right.length ? left.length : right.length; int mismatch = FutureArrays.mismatch(priorTerm.bytes, priorTerm.offset, priorTerm.offset + priorTerm.length,
final byte[] bytesLeft = left.bytes; currentTerm.bytes, currentTerm.offset, currentTerm.offset + currentTerm.length);
final int offLeft = left.offset; if (mismatch < 0) {
byte[] bytesRight = right.bytes; throw new IllegalArgumentException("terms out of order: priorTerm=" + priorTerm + ",currentTerm=" + currentTerm);
final int offRight = right.offset; }
for (int i = 0; i < len; i++) return mismatch;
if (bytesLeft[i+offLeft] != bytesRight[i+offRight])
return i;
return len;
} }
/** /**
@ -57,15 +55,7 @@ public abstract class StringHelper {
* This method assumes currentTerm comes after priorTerm. * This method assumes currentTerm comes after priorTerm.
*/ */
public static int sortKeyLength(final BytesRef priorTerm, final BytesRef currentTerm) { public static int sortKeyLength(final BytesRef priorTerm, final BytesRef currentTerm) {
final int currentTermOffset = currentTerm.offset; return bytesDifference(priorTerm, currentTerm) + 1;
final int priorTermOffset = priorTerm.offset;
final int limit = Math.min(priorTerm.length, currentTerm.length);
for (int i = 0; i < limit; i++) {
if (priorTerm.bytes[priorTermOffset+i] != currentTerm.bytes[currentTermOffset+i]) {
return i+1;
}
}
return Math.min(1+priorTerm.length, currentTerm.length);
} }
private StringHelper() { private StringHelper() {
@ -83,17 +73,12 @@ public abstract class StringHelper {
* Otherwise <code>false</code>. * Otherwise <code>false</code>.
*/ */
public static boolean startsWith(byte[] ref, BytesRef prefix) { public static boolean startsWith(byte[] ref, BytesRef prefix) {
// not long enough to start with the prefix
if (ref.length < prefix.length) { if (ref.length < prefix.length) {
return false; return false;
} }
return FutureArrays.equals(ref, 0, prefix.length,
for(int i=0;i<prefix.length;i++) { prefix.bytes, prefix.offset, prefix.offset + prefix.length);
if (ref[i] != prefix.bytes[prefix.offset+i]) {
return false;
}
}
return true;
} }
/** /**
@ -108,7 +93,12 @@ public abstract class StringHelper {
* Otherwise <code>false</code>. * Otherwise <code>false</code>.
*/ */
public static boolean startsWith(BytesRef ref, BytesRef prefix) { public static boolean startsWith(BytesRef ref, BytesRef prefix) {
return sliceEquals(ref, prefix, 0); // not long enough to start with the prefix
if (ref.length < prefix.length) {
return false;
}
return FutureArrays.equals(ref.bytes, ref.offset, ref.offset + prefix.length,
prefix.bytes, prefix.offset, prefix.offset + prefix.length);
} }
/** /**
@ -123,24 +113,13 @@ public abstract class StringHelper {
* Otherwise <code>false</code>. * Otherwise <code>false</code>.
*/ */
public static boolean endsWith(BytesRef ref, BytesRef suffix) { public static boolean endsWith(BytesRef ref, BytesRef suffix) {
return sliceEquals(ref, suffix, ref.length - suffix.length); int startAt = ref.length - suffix.length;
} // not long enough to start with the suffix
if (startAt < 0) {
private static boolean sliceEquals(BytesRef sliceToTest, BytesRef other, int pos) {
if (pos < 0 || sliceToTest.length - pos < other.length) {
return false; return false;
} }
int i = sliceToTest.offset + pos; return FutureArrays.equals(ref.bytes, ref.offset + startAt, ref.offset + startAt + suffix.length,
int j = other.offset; suffix.bytes, suffix.offset, suffix.offset + suffix.length);
final int k = other.offset + other.length;
while (j < k) {
if (sliceToTest.bytes[i++] != other.bytes[j++]) {
return false;
}
}
return true;
} }
/** Pass this as the seed to {@link #murmurhash3_x86_32}. */ /** Pass this as the seed to {@link #murmurhash3_x86_32}. */
@ -375,16 +354,12 @@ public abstract class StringHelper {
/** Compares a fixed length slice of two byte arrays interpreted as /** Compares a fixed length slice of two byte arrays interpreted as
* big-endian unsigned values. Returns positive int if a &gt; b, * big-endian unsigned values. Returns positive int if a &gt; b,
* negative int if a &lt; b and 0 if a == b */ * negative int if a &lt; b and 0 if a == b
*
* @deprecated Use FutureArrays.compareUnsigned instead.
*/
@Deprecated
public static int compare(int count, byte[] a, int aOffset, byte[] b, int bOffset) { public static int compare(int count, byte[] a, int aOffset, byte[] b, int bOffset) {
// TODO: dedup this w/ BytesRef.compareTo? return FutureArrays.compareUnsigned(a, aOffset, aOffset + count, b, bOffset, bOffset + count);
for(int i=0;i<count;i++) {
int cmp = (a[aOffset+i]&0xff) - (b[bOffset+i]&0xff);
if (cmp != 0) {
return cmp;
}
}
return 0;
} }
} }

View File

@ -27,6 +27,7 @@ import java.util.Set;
import org.apache.lucene.util.Accountable; import org.apache.lucene.util.Accountable;
import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.ArrayUtil;
import org.apache.lucene.util.FutureObjects;
import org.apache.lucene.util.InPlaceMergeSorter; import org.apache.lucene.util.InPlaceMergeSorter;
import org.apache.lucene.util.RamUsageEstimator; import org.apache.lucene.util.RamUsageEstimator;
import org.apache.lucene.util.Sorter; import org.apache.lucene.util.Sorter;
@ -114,14 +115,8 @@ public class Automaton implements Accountable {
/** Set or clear this state as an accept state. */ /** Set or clear this state as an accept state. */
public void setAccept(int state, boolean accept) { public void setAccept(int state, boolean accept) {
if (state >= getNumStates()) { FutureObjects.checkIndex(state, getNumStates());
throw new IllegalArgumentException("state=" + state + " is out of bounds (numStates=" + getNumStates() + ")"); isAccept.set(state, accept);
}
if (accept) {
isAccept.set(state);
} else {
isAccept.clear(state);
}
} }
/** Sugar to get all transitions for all states. This is /** Sugar to get all transitions for all states. This is
@ -161,12 +156,9 @@ public class Automaton implements Accountable {
public void addTransition(int source, int dest, int min, int max) { public void addTransition(int source, int dest, int min, int max) {
assert nextTransition%3 == 0; assert nextTransition%3 == 0;
if (source >= nextState/2) { int bounds = nextState/2;
throw new IllegalArgumentException("source=" + source + " is out of bounds (maxState is " + (nextState/2-1) + ")"); FutureObjects.checkIndex(source, bounds);
} FutureObjects.checkIndex(dest, bounds);
if (dest >= nextState/2) {
throw new IllegalArgumentException("dest=" + dest + " is out of bounds (max state is " + (nextState/2-1) + ")");
}
growTransitions(); growTransitions();
if (curState != source) { if (curState != source) {
@ -842,10 +834,7 @@ public class Automaton implements Accountable {
/** Set or clear this state as an accept state. */ /** Set or clear this state as an accept state. */
public void setAccept(int state, boolean accept) { public void setAccept(int state, boolean accept) {
if (state >= getNumStates()) { FutureObjects.checkIndex(state, getNumStates());
throw new IllegalArgumentException("state=" + state + " is out of bounds (numStates=" + getNumStates() + ")");
}
this.isAccept.set(state, accept); this.isAccept.set(state, accept);
} }

View File

@ -46,10 +46,9 @@ public class TestCharTermAttributeImpl extends LuceneTestCase {
CharTermAttributeImpl t = new CharTermAttributeImpl(); CharTermAttributeImpl t = new CharTermAttributeImpl();
char[] content = "hello".toCharArray(); char[] content = "hello".toCharArray();
t.copyBuffer(content, 0, content.length); t.copyBuffer(content, 0, content.length);
IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { expectThrows(IndexOutOfBoundsException.class, () -> {
t.setLength(-1); t.setLength(-1);
}); });
assertTrue(expected.getMessage().contains("must not be negative"));
} }
public void testGrow() { public void testGrow() {

View File

@ -16,8 +16,6 @@
*/ */
package org.apache.lucene.util; package org.apache.lucene.util;
import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -107,20 +105,6 @@ public class TestArrayUtil extends LuceneTestCase {
test = ArrayUtil.parseInt("foo 1923 bar".toCharArray(), 4, 4); test = ArrayUtil.parseInt("foo 1923 bar".toCharArray(), 4, 4);
assertTrue(test + " does not equal: " + 1923, test == 1923); assertTrue(test + " does not equal: " + 1923, test == 1923);
} }
public void testSliceEquals() {
String left = "this is equal";
String right = left;
byte[] leftChars = left.getBytes(StandardCharsets.UTF_8);
byte[] rightChars = right.getBytes(StandardCharsets.UTF_8);
assertTrue(left + " does not equal: " + right, ArrayUtil.equals(leftChars, 0, rightChars, 0, left.length()));
assertFalse(left + " does not equal: " + right, ArrayUtil.equals(leftChars, 1, rightChars, 0, left.length()));
assertFalse(left + " does not equal: " + right, ArrayUtil.equals(leftChars, 1, rightChars, 2, left.length()));
assertFalse(left + " does not equal: " + right, ArrayUtil.equals(leftChars, 25, rightChars, 0, left.length()));
assertFalse(left + " does not equal: " + right, ArrayUtil.equals(leftChars, 12, rightChars, 0, left.length()));
}
private Integer[] createRandomArray(int maxSize) { private Integer[] createRandomArray(int maxSize) {
final Random rnd = random(); final Random rnd = random();

View File

@ -0,0 +1,305 @@
/*
* 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.util;
import java.nio.charset.StandardCharsets;
/** Test java 8-compatible implementations of {@code java.util.Arrays} methods */
public class TestFutureArrays extends LuceneTestCase {
public void testByteMismatch() {
assertEquals(1, FutureArrays.mismatch(bytes("ab"), 0, 2, bytes("ac"), 0, 2));
assertEquals(0, FutureArrays.mismatch(bytes("ab"), 0, 2, bytes("b"), 0, 1));
assertEquals(-1, FutureArrays.mismatch(bytes("ab"), 0, 2, bytes("ab"), 0, 2));
assertEquals(1, FutureArrays.mismatch(bytes("ab"), 0, 2, bytes("a"), 0, 1));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.mismatch(bytes("ab"), 2, 1, bytes("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.mismatch(bytes("ab"), 2, 1, bytes("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.mismatch(null, 0, 2, bytes("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.mismatch(bytes("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.mismatch(bytes("ab"), 0, 3, bytes("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.mismatch(bytes("ab"), 0, 2, bytes("a"), 0, 2);
});
}
public void testCharMismatch() {
assertEquals(1, FutureArrays.mismatch(chars("ab"), 0, 2, chars("ac"), 0, 2));
assertEquals(0, FutureArrays.mismatch(chars("ab"), 0, 2, chars("b"), 0, 1));
assertEquals(-1, FutureArrays.mismatch(chars("ab"), 0, 2, chars("ab"), 0, 2));
assertEquals(1, FutureArrays.mismatch(chars("ab"), 0, 2, chars("a"), 0, 1));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.mismatch(chars("ab"), 2, 1, chars("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.mismatch(chars("ab"), 2, 1, chars("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.mismatch(null, 0, 2, chars("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.mismatch(chars("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.mismatch(chars("ab"), 0, 3, chars("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.mismatch(chars("ab"), 0, 2, chars("a"), 0, 2);
});
}
public void testByteCompareUnsigned() {
assertEquals(1, Integer.signum(FutureArrays.compareUnsigned(bytes("ab"), 0, 2, bytes("a"), 0, 1)));
assertEquals(1, Integer.signum(FutureArrays.compareUnsigned(bytes("ab"), 0, 2, bytes("aa"), 0, 2)));
assertEquals(0, Integer.signum(FutureArrays.compareUnsigned(bytes("ab"), 0, 2, bytes("ab"), 0, 2)));
assertEquals(-1, Integer.signum(FutureArrays.compareUnsigned(bytes("a"), 0, 1, bytes("ab"), 0, 2)));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compareUnsigned(bytes("ab"), 2, 1, bytes("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compareUnsigned(bytes("ab"), 2, 1, bytes("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compareUnsigned(null, 0, 2, bytes("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compareUnsigned(bytes("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compareUnsigned(bytes("ab"), 0, 3, bytes("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compareUnsigned(bytes("ab"), 0, 2, bytes("a"), 0, 2);
});
}
public void testCharCompare() {
assertEquals(1, Integer.signum(FutureArrays.compare(chars("ab"), 0, 2, chars("a"), 0, 1)));
assertEquals(1, Integer.signum(FutureArrays.compare(chars("ab"), 0, 2, chars("aa"), 0, 2)));
assertEquals(0, Integer.signum(FutureArrays.compare(chars("ab"), 0, 2, chars("ab"), 0, 2)));
assertEquals(-1, Integer.signum(FutureArrays.compare(chars("a"), 0, 1, chars("ab"), 0, 2)));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(chars("ab"), 2, 1, chars("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(chars("ab"), 2, 1, chars("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(null, 0, 2, chars("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(chars("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(chars("ab"), 0, 3, chars("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(chars("ab"), 0, 2, chars("a"), 0, 2);
});
}
public void testIntCompare() {
assertEquals(1, Integer.signum(FutureArrays.compare(ints("ab"), 0, 2, ints("a"), 0, 1)));
assertEquals(1, Integer.signum(FutureArrays.compare(ints("ab"), 0, 2, ints("aa"), 0, 2)));
assertEquals(0, Integer.signum(FutureArrays.compare(ints("ab"), 0, 2, ints("ab"), 0, 2)));
assertEquals(-1, Integer.signum(FutureArrays.compare(ints("a"), 0, 1, ints("ab"), 0, 2)));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(ints("ab"), 2, 1, ints("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(ints("ab"), 2, 1, ints("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(null, 0, 2, ints("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(ints("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(ints("ab"), 0, 3, ints("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(ints("ab"), 0, 2, ints("a"), 0, 2);
});
}
public void testLongCompare() {
assertEquals(1, Integer.signum(FutureArrays.compare(longs("ab"), 0, 2, longs("a"), 0, 1)));
assertEquals(1, Integer.signum(FutureArrays.compare(longs("ab"), 0, 2, longs("aa"), 0, 2)));
assertEquals(0, Integer.signum(FutureArrays.compare(longs("ab"), 0, 2, longs("ab"), 0, 2)));
assertEquals(-1, Integer.signum(FutureArrays.compare(longs("a"), 0, 1, longs("ab"), 0, 2)));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(longs("ab"), 2, 1, longs("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.compare(longs("ab"), 2, 1, longs("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(null, 0, 2, longs("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.compare(longs("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(longs("ab"), 0, 3, longs("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.compare(longs("ab"), 0, 2, longs("a"), 0, 2);
});
}
public void testByteEquals() {
assertFalse(FutureArrays.equals(bytes("ab"), 0, 2, bytes("a"), 0, 1));
assertFalse(FutureArrays.equals(bytes("ab"), 0, 2, bytes("aa"), 0, 2));
assertTrue(FutureArrays.equals(bytes("ab"), 0, 2, bytes("ab"), 0, 2));
assertFalse(FutureArrays.equals(bytes("a"), 0, 1, bytes("ab"), 0, 2));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(bytes("ab"), 2, 1, bytes("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(bytes("ab"), 2, 1, bytes("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(null, 0, 2, bytes("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(bytes("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(bytes("ab"), 0, 3, bytes("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(bytes("ab"), 0, 2, bytes("a"), 0, 2);
});
}
public void testCharEquals() {
assertFalse(FutureArrays.equals(chars("ab"), 0, 2, chars("a"), 0, 1));
assertFalse(FutureArrays.equals(chars("ab"), 0, 2, chars("aa"), 0, 2));
assertTrue(FutureArrays.equals(chars("ab"), 0, 2, chars("ab"), 0, 2));
assertFalse(FutureArrays.equals(chars("a"), 0, 1, chars("ab"), 0, 2));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(chars("ab"), 2, 1, chars("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(chars("ab"), 2, 1, chars("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(null, 0, 2, chars("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(chars("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(chars("ab"), 0, 3, chars("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(chars("ab"), 0, 2, chars("a"), 0, 2);
});
}
public void testIntEquals() {
assertFalse(FutureArrays.equals(ints("ab"), 0, 2, ints("a"), 0, 1));
assertFalse(FutureArrays.equals(ints("ab"), 0, 2, ints("aa"), 0, 2));
assertTrue(FutureArrays.equals(ints("ab"), 0, 2, ints("ab"), 0, 2));
assertFalse(FutureArrays.equals(ints("a"), 0, 1, ints("ab"), 0, 2));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(ints("ab"), 2, 1, ints("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(ints("ab"), 2, 1, ints("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(null, 0, 2, ints("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(ints("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(ints("ab"), 0, 3, ints("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(ints("ab"), 0, 2, ints("a"), 0, 2);
});
}
public void testLongEquals() {
assertFalse(FutureArrays.equals(longs("ab"), 0, 2, longs("a"), 0, 1));
assertFalse(FutureArrays.equals(longs("ab"), 0, 2, longs("aa"), 0, 2));
assertTrue(FutureArrays.equals(longs("ab"), 0, 2, longs("ab"), 0, 2));
assertFalse(FutureArrays.equals(longs("a"), 0, 1, longs("ab"), 0, 2));
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(longs("ab"), 2, 1, longs("a"), 0, 1);
});
expectThrows(IllegalArgumentException.class, () -> {
FutureArrays.equals(longs("ab"), 2, 1, longs("a"), 1, 0);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(null, 0, 2, longs("a"), 0, 1);
});
expectThrows(NullPointerException.class, () -> {
FutureArrays.equals(longs("ab"), 0, 2, null, 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(longs("ab"), 0, 3, longs("a"), 0, 1);
});
expectThrows(IndexOutOfBoundsException.class, () -> {
FutureArrays.equals(longs("ab"), 0, 2, longs("a"), 0, 2);
});
}
private byte[] bytes(String s) {
return s.getBytes(StandardCharsets.UTF_8);
}
private char[] chars(String s) {
return s.toCharArray();
}
private int[] ints(String s) {
int ints[] = new int[s.length()];
for (int i = 0; i < s.length(); i++) {
ints[i] = s.charAt(i);
}
return ints;
}
private long[] longs(String s) {
long longs[] = new long[s.length()];
for (int i = 0; i < s.length(); i++) {
longs[i] = s.charAt(i);
}
return longs;
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.util;
/** Test java 8-compatible implementations of {@code java.util.Objects} methods */
public class TestFutureObjects extends LuceneTestCase {
public void testCheckIndex() {
assertEquals(0, FutureObjects.checkIndex(0, 1));
assertEquals(1, FutureObjects.checkIndex(1, 2));
Exception e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkIndex(-1, 0);
});
assertEquals("Index -1 out-of-bounds for length 0", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkIndex(0, 0);
});
assertEquals("Index 0 out-of-bounds for length 0", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkIndex(1, 0);
});
assertEquals("Index 1 out-of-bounds for length 0", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkIndex(0, -1);
});
assertEquals("Index 0 out-of-bounds for length -1", e.getMessage());
}
public void testCheckFromToIndex() {
assertEquals(0, FutureObjects.checkFromToIndex(0, 0, 0));
assertEquals(1, FutureObjects.checkFromToIndex(1, 2, 2));
Exception e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromToIndex(-1, 0, 0);
});
assertEquals("Range [-1, 0) out-of-bounds for length 0", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromToIndex(1, 0, 2);
});
assertEquals("Range [1, 0) out-of-bounds for length 2", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromToIndex(1, 3, 2);
});
assertEquals("Range [1, 3) out-of-bounds for length 2", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromToIndex(0, 0, -1);
});
assertEquals("Range [0, 0) out-of-bounds for length -1", e.getMessage());
}
public void testCheckFromIndexSize() {
assertEquals(0, FutureObjects.checkFromIndexSize(0, 0, 0));
assertEquals(1, FutureObjects.checkFromIndexSize(1, 2, 3));
Exception e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromIndexSize(-1, 0, 1);
});
assertEquals("Range [-1, -1 + 0) out-of-bounds for length 1", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromIndexSize(0, -1, 1);
});
assertEquals("Range [0, 0 + -1) out-of-bounds for length 1", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromIndexSize(0, 2, 1);
});
assertEquals("Range [0, 0 + 2) out-of-bounds for length 1", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromIndexSize(1, Integer.MAX_VALUE, Integer.MAX_VALUE);
});
assertEquals("Range [1, 1 + 2147483647) out-of-bounds for length 2147483647", e.getMessage());
e = expectThrows(IndexOutOfBoundsException.class, () -> {
FutureObjects.checkFromIndexSize(0, 0, -1);
});
assertEquals("Range [0, 0 + 0) out-of-bounds for length -1", e.getMessage());
}
}

View File

@ -23,6 +23,15 @@ public class TestStringHelper extends LuceneTestCase {
BytesRef left = new BytesRef("foobar"); BytesRef left = new BytesRef("foobar");
BytesRef right = new BytesRef("foozo"); BytesRef right = new BytesRef("foozo");
assertEquals(3, StringHelper.bytesDifference(left, right)); assertEquals(3, StringHelper.bytesDifference(left, right));
assertEquals(2, StringHelper.bytesDifference(new BytesRef("foo"), new BytesRef("for")));
assertEquals(2, StringHelper.bytesDifference(new BytesRef("foo1234"), new BytesRef("for1234")));
assertEquals(1, StringHelper.bytesDifference(new BytesRef("foo"), new BytesRef("fz")));
assertEquals(0, StringHelper.bytesDifference(new BytesRef("foo"), new BytesRef("g")));
assertEquals(3, StringHelper.bytesDifference(new BytesRef("foo"), new BytesRef("food")));
// we can detect terms are out of order if we see a duplicate
expectThrows(IllegalArgumentException.class, () -> {
StringHelper.bytesDifference(new BytesRef("ab"), new BytesRef("ab"));
});
} }
public void testStartsWith() { public void testStartsWith() {
@ -63,5 +72,9 @@ public class TestStringHelper extends LuceneTestCase {
assertEquals(2, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("fz"))); assertEquals(2, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("fz")));
assertEquals(1, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("g"))); assertEquals(1, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("g")));
assertEquals(4, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("food"))); assertEquals(4, StringHelper.sortKeyLength(new BytesRef("foo"), new BytesRef("food")));
// we can detect terms are out of order if we see a duplicate
expectThrows(IllegalArgumentException.class, () -> {
StringHelper.sortKeyLength(new BytesRef("ab"), new BytesRef("ab"));
});
} }
} }

View File

@ -16,7 +16,6 @@
*/ */
package org.apache.lucene.util; package org.apache.lucene.util;
/* /*
* Some of this code came from the excellent Unicode * Some of this code came from the excellent Unicode
* conversion examples from: * conversion examples from:
@ -143,7 +142,7 @@ public class TestUnicodeUtil extends LuceneTestCase {
final int utf32Len = UnicodeUtil.UTF8toUTF32(new BytesRef(utf8, 0, utf8Len), utf32); final int utf32Len = UnicodeUtil.UTF8toUTF32(new BytesRef(utf8, 0, utf8Len), utf32);
int[] codePoints = s.codePoints().toArray(); int[] codePoints = s.codePoints().toArray();
if (!ArrayUtil.equals(codePoints, 0, utf32, 0, codePoints.length)) { if (!FutureArrays.equals(codePoints, 0, codePoints.length, utf32, 0, codePoints.length)) {
System.out.println("FAILED"); System.out.println("FAILED");
for(int j=0;j<s.length();j++) { for(int j=0;j<s.length();j++) {
System.out.println(" char[" + j + "]=" + Integer.toHexString(s.charAt(j))); System.out.println(" char[" + j + "]=" + Integer.toHexString(s.charAt(j)));

View File

@ -79,7 +79,7 @@ public interface TaxonomyWriter extends Closeable, TwoPhaseCommit {
* If the given ordinal is the ROOT_ORDINAL, an INVALID_ORDINAL is returned. * If the given ordinal is the ROOT_ORDINAL, an INVALID_ORDINAL is returned.
* If the given ordinal is a top-level category, the ROOT_ORDINAL is returned. * If the given ordinal is a top-level category, the ROOT_ORDINAL is returned.
* If an invalid ordinal is given (negative or beyond the last available * If an invalid ordinal is given (negative or beyond the last available
* ordinal), an ArrayIndexOutOfBoundsException is thrown. However, it is * ordinal), an IndexOutOfBoundsException is thrown. However, it is
* expected that getParent will only be called for ordinals which are * expected that getParent will only be called for ordinals which are
* already known to be in the taxonomy. * already known to be in the taxonomy.
* TODO (Facet): instead of a getParent(ordinal) method, consider having a * TODO (Facet): instead of a getParent(ordinal) method, consider having a

View File

@ -61,6 +61,7 @@ import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.Directory; import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException; import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.FutureObjects;
/** /**
* {@link TaxonomyWriter} which uses a {@link Directory} to store the taxonomy * {@link TaxonomyWriter} which uses a {@link Directory} to store the taxonomy
@ -763,9 +764,7 @@ public class DirectoryTaxonomyWriter implements TaxonomyWriter {
// Note: the following if() just enforces that a user can never ask // Note: the following if() just enforces that a user can never ask
// for the parent of a nonexistant category - even if the parent array // for the parent of a nonexistant category - even if the parent array
// was allocated bigger than it really needs to be. // was allocated bigger than it really needs to be.
if (ordinal >= nextID) { FutureObjects.checkIndex(ordinal, nextID);
throw new ArrayIndexOutOfBoundsException("requested ordinal is bigger than the largest ordinal in the taxonomy");
}
int[] parents = getTaxoArrays().parents(); int[] parents = getTaxoArrays().parents();
assert ordinal < parents.length : "requested ordinal (" + ordinal + "); parents.length (" + parents.length + ") !"; assert ordinal < parents.length : "requested ordinal (" + ordinal + "); parents.length (" + parents.length + ") !";

View File

@ -490,13 +490,13 @@ public class TestTaxonomyCombined extends FacetTestCase {
} }
// check parent of of invalid ordinals: // check parent of of invalid ordinals:
expectThrows(ArrayIndexOutOfBoundsException.class, () -> { expectThrows(IndexOutOfBoundsException.class, () -> {
tw.getParent(-1); tw.getParent(-1);
}); });
expectThrows(ArrayIndexOutOfBoundsException.class, () -> { expectThrows(IndexOutOfBoundsException.class, () -> {
tw.getParent(TaxonomyReader.INVALID_ORDINAL); tw.getParent(TaxonomyReader.INVALID_ORDINAL);
}); });
expectThrows(ArrayIndexOutOfBoundsException.class, () -> { expectThrows(IndexOutOfBoundsException.class, () -> {
tw.getParent(tr.getSize()); tw.getParent(tr.getSize());
}); });
} }

View File

@ -34,11 +34,20 @@
<!-- default classpath refid, can be overridden by contrib's build.xml (use the above base.classpath as basis): --> <!-- default classpath refid, can be overridden by contrib's build.xml (use the above base.classpath as basis): -->
<path id="classpath" refid="base.classpath"/> <path id="classpath" refid="base.classpath"/>
<!-- if we run with Java 9+, we refer to the java9 classes directory and insert this before the main classpath (to "emulate" a MR-JAR): -->
<condition property="-test.classpath.java9.addon" value="${build.dir}/classes/java9" else="${build.dir}/classes/java">
<and>
<not><equals arg1="${build.java.runtime}" arg2="1.8"/></not>
<istrue value="${tests.withJava9Patches}"/>
</and>
</condition>
<path id="test.base.classpath"> <path id="test.base.classpath">
<pathelement location="${common.dir}/build/test-framework/classes/java"/> <pathelement location="${common.dir}/build/test-framework/classes/java"/>
<pathelement location="${common.dir}/build/codecs/classes/java"/> <pathelement location="${common.dir}/build/codecs/classes/java"/>
<path refid="classpath"/> <path refid="classpath"/>
<path refid="junit-path"/> <path refid="junit-path"/>
<pathelement location="${-test.classpath.java9.addon}"/><!-- if it's a duplicate it gets removed by Ant! -->
<pathelement location="${build.dir}/classes/java"/> <pathelement location="${build.dir}/classes/java"/>
</path> </path>

View File

@ -0,0 +1,73 @@
/*
* 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.
*/
import org.apache.tools.ant.Project;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;
def mappings = [
'org/apache/lucene/util/FutureObjects': 'java/util/Objects',
'org/apache/lucene/util/FutureArrays': 'java/util/Arrays',
];
File inputDir = new File(properties['build.dir'], 'classes/java');
File outputDir = new File(properties['build.dir'], 'classes/java9');
outputDir.mkdirs();
def scanner = ant.fileScanner {
fileset(dir:inputDir) {
include(name:"**/*.class")
}
}
int count = 0;
for (f in scanner) {
ClassReader reader = new ClassReader(f.getBytes());
if (mappings.containsKey(reader.className)) {
// we do not remap our replacements! :-)
continue;
}
ClassWriter writer = new ClassWriter(0 /* no recalculations needed */);
boolean remapped = false;
ClassRemapper remapper = new ClassRemapper(writer, new Remapper() {
@Override
public String map(String typeName) {
if (mappings.containsKey(typeName)) {
remapped = true;
return mappings.get(typeName);
}
return typeName;
}
});
reader.accept(remapper, 0 /* keep everything as-is*/);
if (remapped) {
task.log("Remapped: "+reader.className, Project.MSG_INFO);
File output = new File(outputDir, reader.className + '.class');
output.parentFile.mkdirs();
output.setBytes(writer.toByteArray());
count++;
}
}
task.log("Remapped $count class files for Java 9 to: $outputDir", Project.MSG_INFO);

View File

@ -555,4 +555,7 @@
<target name="test" unless="tests.disable-solr"> <target name="test" unless="tests.disable-solr">
<antcall target="common.test" inheritrefs="true" inheritall="true"/> <antcall target="common.test" inheritrefs="true" inheritall="true"/>
</target> </target>
<!-- In Solr we do not generate MR-JARs yet; disable completely so we do not accidentally patch -->
<target name="patch-mrjar-classes"/>
</project> </project>