You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by us...@apache.org on 2018/02/04 08:53:14 UTC

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

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


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/05a105a9
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/05a105a9
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/05a105a9

Branch: refs/heads/branch_7x
Commit: 05a105a9f76b73b597931894dff392ad44243aa0
Parents: 7fbdf5d
Author: Uwe Schindler <us...@apache.org>
Authored: Sat Feb 3 09:17:59 2018 +0100
Committer: Uwe Schindler <us...@apache.org>
Committed: Sun Feb 4 09:51:40 2018 +0100

----------------------------------------------------------------------
 build.xml                                       |   1 +
 lucene/CHANGES.txt                              |   6 +
 lucene/build.xml                                |   2 +-
 .../blockterms/FixedGapTermsIndexWriter.java    |   8 +-
 .../SimpleTextStoredFieldsReader.java           |   5 +-
 lucene/common-build.xml                         |  48 ++-
 lucene/core/build.xml                           |   9 +
 .../tokenattributes/CharTermAttributeImpl.java  |  19 +-
 .../CompressingTermVectorsWriter.java           |   8 +-
 .../apache/lucene/codecs/compressing/LZ4.java   |   8 +-
 .../lucene70/Lucene70DocValuesConsumer.java     |   8 +-
 .../org/apache/lucene/document/BinaryPoint.java |   4 +-
 .../org/apache/lucene/document/DoubleRange.java |  11 +-
 .../org/apache/lucene/document/FloatRange.java  |  11 +-
 .../org/apache/lucene/document/IntRange.java    |  11 +-
 .../org/apache/lucene/document/LongRange.java   |  11 +-
 .../java/org/apache/lucene/index/BitsSlice.java |   6 +-
 .../org/apache/lucene/index/CodecReader.java    |   5 +-
 .../apache/lucene/index/MergeReaderWrapper.java |   5 +-
 .../apache/lucene/index/PrefixCodedTerms.java   |  28 +-
 .../org/apache/lucene/search/BooleanScorer.java |   5 +-
 .../java/org/apache/lucene/util/ArrayUtil.java  |  52 ----
 .../java/org/apache/lucene/util/BytesRef.java   |  39 +--
 .../org/apache/lucene/util/BytesRefArray.java   |  36 +--
 .../java/org/apache/lucene/util/CharsRef.java   | 102 ++-----
 .../org/apache/lucene/util/FutureArrays.java    | 268 ++++++++++++++++
 .../org/apache/lucene/util/FutureObjects.java   |  70 +++++
 .../java/org/apache/lucene/util/IntsRef.java    |  38 +--
 .../java/org/apache/lucene/util/LongsRef.java   |  38 +--
 .../org/apache/lucene/util/StringHelper.java    |  87 ++----
 .../apache/lucene/util/automaton/Automaton.java |  25 +-
 .../TestCharTermAttributeImpl.java              |   3 +-
 .../org/apache/lucene/util/TestArrayUtil.java   |  16 -
 .../apache/lucene/util/TestFutureArrays.java    | 305 +++++++++++++++++++
 .../apache/lucene/util/TestFutureObjects.java   | 102 +++++++
 .../apache/lucene/util/TestStringHelper.java    |  13 +
 .../org/apache/lucene/util/TestUnicodeUtil.java |   3 +-
 .../lucene/facet/taxonomy/TaxonomyWriter.java   |   2 +-
 .../directory/DirectoryTaxonomyWriter.java      |   5 +-
 .../facet/taxonomy/TestTaxonomyCombined.java    |   6 +-
 lucene/module-build.xml                         |   9 +
 .../tools/src/groovy/patch-mrjar-classes.groovy |  73 +++++
 solr/common-build.xml                           |   3 +
 43 files changed, 1074 insertions(+), 440 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index a100e12..2d300b5 100755
--- a/build.xml
+++ b/build.xml
@@ -874,4 +874,5 @@ Test args: [${args}]</echo>
 
   <!-- useless targets (override common-build.xml): -->
   <target name="generate-test-reports"/>
+  <target name="patch-mrjar-classes"/>
 </project>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 7a27718..f59768d 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -54,6 +54,12 @@ Improvements
 * LUCENE-8129: A Unicode set filter can now be specified when using ICUFoldingFilter.
   (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
 
 * LUCENE-8077: Fixed bug in how CheckIndex verifies doc-value iterators.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/build.xml
----------------------------------------------------------------------
diff --git a/lucene/build.xml b/lucene/build.xml
index 3aa4e42..f2ce68d 100644
--- a/lucene/build.xml
+++ b/lucene/build.xml
@@ -557,7 +557,7 @@
   </target>
 
   <!-- 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">
       <propertyset refid="uptodate.and.compiled.properties"/>
     </ant>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/FixedGapTermsIndexWriter.java
----------------------------------------------------------------------
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/FixedGapTermsIndexWriter.java b/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/FixedGapTermsIndexWriter.java
index cdfd962..abb4cb0 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/FixedGapTermsIndexWriter.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/blockterms/FixedGapTermsIndexWriter.java
@@ -150,7 +150,13 @@ public class FixedGapTermsIndexWriter extends TermsIndexWriterBase {
 
     @Override
     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);
 
       // write only the min prefix that shows the diff

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
----------------------------------------------------------------------
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
index ead8ecc..b457ca5 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextStoredFieldsReader.java
@@ -36,6 +36,7 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
 import org.apache.lucene.util.CharsRef;
 import org.apache.lucene.util.CharsRefBuilder;
+import org.apache.lucene.util.FutureArrays;
 import org.apache.lucene.util.IOUtils;
 import org.apache.lucene.util.RamUsageEstimator;
 import org.apache.lucene.util.StringHelper;
@@ -202,8 +203,8 @@ public class SimpleTextStoredFieldsReader extends StoredFieldsReader {
   }
   
   private boolean equalsAt(BytesRef a, BytesRef b, int bOffset) {
-    return a.length == b.length - bOffset && 
-        ArrayUtil.equals(a.bytes, a.offset, b.bytes, b.offset + bOffset, b.length - bOffset);
+    return a.length == b.length - bOffset &&
+        FutureArrays.equals(a.bytes, a.offset, a.offset + a.length, b.bytes, b.offset + bOffset, b.offset + b.length);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/common-build.xml
----------------------------------------------------------------------
diff --git a/lucene/common-build.xml b/lucene/common-build.xml
index f6f4da3..7ce8d6f 100644
--- a/lucene/common-build.xml
+++ b/lucene/common-build.xml
@@ -160,6 +160,10 @@
 
   <!-- Display at most this many failures as a summary at the end of junit4 run. -->
   <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.debug" value="on"/>
@@ -546,10 +550,48 @@
     <!-- convenience target to compile core -->
   </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/>
   </target>
+  
+  <target name="jar-core" depends="-mrjar-core,-jar-core"/>
 
+  <!-- Packaging targets: -->
+  
   <property name="lucene.tgz.file" location="${common.dir}/dist/lucene-${version}.tgz"/>
   <available file="${lucene.tgz.file}" property="lucene.tgz.exists"/>
   <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>
   </target>
 
-  <target name="test" depends="clover,compile-test,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="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,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"
           description="Only runs unit tests.  Jars are not downloaded; compilation is not updated; and Clover is not enabled."/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/build.xml
----------------------------------------------------------------------
diff --git a/lucene/core/build.xml b/lucene/core/build.xml
index d968798..46183b0 100644
--- a/lucene/core/build.xml
+++ b/lucene/core/build.xml
@@ -31,10 +31,19 @@
 
   <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">
     <pathelement location="${common.dir}/build/codecs/classes/java"/>
     <pathelement location="${common.dir}/build/test-framework/classes/java"/>
     <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/test"/>
   </path>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java
index 355f417..f28cc78 100644
--- a/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java
+++ b/lucene/core/src/java/org/apache/lucene/analysis/tokenattributes/CharTermAttributeImpl.java
@@ -23,6 +23,7 @@ import org.apache.lucene.util.AttributeImpl;
 import org.apache.lucene.util.AttributeReflector;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.FutureObjects;
 
 /** Default implementation of {@link CharTermAttribute}. */
 public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttribute, TermToBytesRefAttribute, Cloneable {
@@ -71,11 +72,7 @@ public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttr
 
   @Override
   public final CharTermAttribute setLength(int length) {
-    if (length < 0) {
-      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 + ")");
+    FutureObjects.checkFromIndexSize(0, length, termBuffer.length);
     termLength = length;
     return this;
   }
@@ -102,15 +99,13 @@ public class CharTermAttributeImpl extends AttributeImpl implements CharTermAttr
   
   @Override
   public final char charAt(int index) {
-    if (index >= termLength)
-      throw new IndexOutOfBoundsException();
+    FutureObjects.checkIndex(index, termLength);
     return termBuffer[index];
   }
   
   @Override
   public final CharSequence subSequence(final int start, final int end) {
-    if (start > termLength || end > termLength)
-      throw new IndexOutOfBoundsException();
+    FutureObjects.checkFromToIndex(start, end, termLength);
     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) {
     if (csq == null) // needed for Appendable compliance
       csq = "null";
-    final int len = end - start, csqlen = csq.length();
-    if (len < 0 || start > csqlen || end > csqlen)
-      throw new IndexOutOfBoundsException();
+    // TODO: the optimized cases (jdk methods) will already do such checks, maybe re-organize this?
+    FutureObjects.checkFromToIndex(start, end, csq.length());
+    final int len = end - start;
     if (len == 0)
       return this;
     resizeBuffer(termLength + len);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsWriter.java b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsWriter.java
index 26fe890..ee948c3 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsWriter.java
@@ -293,7 +293,13 @@ public final class CompressingTermVectorsWriter extends TermVectorsWriter {
   @Override
   public void startTerm(BytesRef term, int freq) throws IOException {
     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);
     termSuffixes.writeBytes(term.bytes, term.offset + prefix, term.length - prefix);
     // copy last term

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/codecs/compressing/LZ4.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/compressing/LZ4.java b/lucene/core/src/java/org/apache/lucene/codecs/compressing/LZ4.java
index 44bc82c..cba625f 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/compressing/LZ4.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/compressing/LZ4.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 
 import org.apache.lucene.store.DataInput;
 import org.apache.lucene.store.DataOutput;
+import org.apache.lucene.util.FutureArrays;
 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) {
     assert o1 < o2;
-    int count = 0;
-    while (o2 < limit && b[o1++] == b[o2++]) {
-      ++count;
-    }
-    return count;
+    // never -1 because lengths always differ
+    return FutureArrays.mismatch(b, o1, limit, b, o2, limit);
   }
 
   private static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesConsumer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesConsumer.java
index 2dd68e9..e25e8e7 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesConsumer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesConsumer.java
@@ -504,7 +504,13 @@ final class Lucene70DocValuesConsumer extends DocValuesConsumer implements Close
     for (BytesRef term = iterator.next(); term != null; term = iterator.next()) {
       if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == 0) {
         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;
         data.writeBytes(term.bytes, term.offset, sortKeyLength);
       } else if ((ord & Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) == Lucene70DocValuesFormat.TERMS_DICT_REVERSE_INDEX_MASK) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/document/BinaryPoint.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/BinaryPoint.java b/lucene/core/src/java/org/apache/lucene/document/BinaryPoint.java
index 81ef092..693a00f 100644
--- a/lucene/core/src/java/org/apache/lucene/document/BinaryPoint.java
+++ b/lucene/core/src/java/org/apache/lucene/document/BinaryPoint.java
@@ -26,7 +26,7 @@ import org.apache.lucene.search.PointInSetQuery;
 import org.apache.lucene.search.PointRangeQuery;
 import org.apache.lucene.search.Query;
 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
@@ -222,7 +222,7 @@ public final class BinaryPoint extends Field {
                 new Comparator<byte[]>() {
                   @Override
                   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);
                   }
                 });
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java b/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
index 90a8eb9..c1d2dc5 100644
--- a/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
+++ b/lucene/core/src/java/org/apache/lucene/document/DoubleRange.java
@@ -19,6 +19,7 @@ package org.apache.lucene.document;
 import org.apache.lucene.document.RangeFieldQuery.QueryType;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.NumericUtils;
 
 /**
@@ -147,10 +148,7 @@ public class DoubleRange extends Field {
    * @return the decoded min value
    */
   public double getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMin(((BytesRef)fieldsData).bytes, dimension);
   }
 
@@ -160,10 +158,7 @@ public class DoubleRange extends Field {
    * @return the decoded max value
    */
   public double getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMax(((BytesRef)fieldsData).bytes, dimension);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/FloatRange.java b/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
index 8b40538..facf23b 100644
--- a/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
+++ b/lucene/core/src/java/org/apache/lucene/document/FloatRange.java
@@ -19,6 +19,7 @@ package org.apache.lucene.document;
 import org.apache.lucene.document.RangeFieldQuery.QueryType;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.NumericUtils;
 
 /**
@@ -147,10 +148,7 @@ public class FloatRange extends Field {
    * @return the decoded min value
    */
   public float getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMin(((BytesRef)fieldsData).bytes, dimension);
   }
 
@@ -160,10 +158,7 @@ public class FloatRange extends Field {
    * @return the decoded max value
    */
   public float getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMax(((BytesRef)fieldsData).bytes, dimension);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/document/IntRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/IntRange.java b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
index 6d2b71c..b426613 100644
--- a/lucene/core/src/java/org/apache/lucene/document/IntRange.java
+++ b/lucene/core/src/java/org/apache/lucene/document/IntRange.java
@@ -19,6 +19,7 @@ package org.apache.lucene.document;
 import org.apache.lucene.document.RangeFieldQuery.QueryType;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.NumericUtils;
 
 /**
@@ -147,10 +148,7 @@ public class IntRange extends Field {
    * @return the decoded min value
    */
   public int getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMin(((BytesRef)fieldsData).bytes, dimension);
   }
 
@@ -160,10 +158,7 @@ public class IntRange extends Field {
    * @return the decoded max value
    */
   public int getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMax(((BytesRef)fieldsData).bytes, dimension);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/document/LongRange.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/LongRange.java b/lucene/core/src/java/org/apache/lucene/document/LongRange.java
index 009f4a1..5c1c763 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LongRange.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LongRange.java
@@ -19,6 +19,7 @@ package org.apache.lucene.document;
 import org.apache.lucene.document.RangeFieldQuery.QueryType;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.NumericUtils;
 
 /**
@@ -145,10 +146,7 @@ public class LongRange extends Field {
    * @return the decoded min value
    */
   public long getMin(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMin(((BytesRef)fieldsData).bytes, dimension);
   }
 
@@ -158,10 +156,7 @@ public class LongRange extends Field {
    * @return the decoded max value
    */
   public long getMax(int dimension) {
-    if (dimension < 0 || dimension >= type.pointDimensionCount()/2) {
-      throw new IllegalArgumentException("dimension request (" + dimension +
-          ") out of bounds for field (name=" + name + " dimensions=" + type.pointDimensionCount()/2 + "). ");
-    }
+    FutureObjects.checkIndex(dimension, type.pointDimensionCount()/2);
     return decodeMax(((BytesRef)fieldsData).bytes, dimension);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/index/BitsSlice.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/BitsSlice.java b/lucene/core/src/java/org/apache/lucene/index/BitsSlice.java
index 216d8f9..19bf9c1 100644
--- a/lucene/core/src/java/org/apache/lucene/index/BitsSlice.java
+++ b/lucene/core/src/java/org/apache/lucene/index/BitsSlice.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.index;
 
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FutureObjects;
 
 
 /**
@@ -39,10 +40,7 @@ final class BitsSlice implements Bits {
     
   @Override
   public boolean get(int doc) {
-    if (doc >= length) {
-      throw new RuntimeException("doc " + doc + " is out of bounds 0 .. " + (length-1));
-    }
-    assert doc < length: "doc=" + doc + " length=" + length;
+    FutureObjects.checkIndex(doc, length);
     return parent.get(doc+start);
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/index/CodecReader.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/CodecReader.java b/lucene/core/src/java/org/apache/lucene/index/CodecReader.java
index 9efcf25..50aaa42 100644
--- a/lucene/core/src/java/org/apache/lucene/index/CodecReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/CodecReader.java
@@ -31,6 +31,7 @@ import org.apache.lucene.codecs.StoredFieldsReader;
 import org.apache.lucene.codecs.TermVectorsReader;
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.Accountables;
+import org.apache.lucene.util.FutureObjects;
 
 /**
  * LeafReader implemented by codec APIs.
@@ -94,9 +95,7 @@ public abstract class CodecReader extends LeafReader implements Accountable {
   }
   
   private void checkBounds(int docID) {
-    if (docID < 0 || docID >= maxDoc()) {       
-      throw new IndexOutOfBoundsException("docID must be >= 0 and < maxDoc=" + maxDoc() + " (got docID=" + docID + ")");
-    }
+    FutureObjects.checkIndex(docID, maxDoc());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/index/MergeReaderWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/MergeReaderWrapper.java b/lucene/core/src/java/org/apache/lucene/index/MergeReaderWrapper.java
index 565bdd4..1f35e45 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MergeReaderWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MergeReaderWrapper.java
@@ -25,6 +25,7 @@ import org.apache.lucene.codecs.NormsProducer;
 import org.apache.lucene.codecs.StoredFieldsReader;
 import org.apache.lucene.codecs.TermVectorsReader;
 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. */
 class MergeReaderWrapper extends LeafReader {
@@ -226,9 +227,7 @@ class MergeReaderWrapper extends LeafReader {
   }
 
   private void checkBounds(int docID) {
-    if (docID < 0 || docID >= maxDoc()) {       
-      throw new IndexOutOfBoundsException("docID must be >= 0 and < maxDoc=" + maxDoc() + " (got docID=" + docID + ")");
-    }
+    FutureObjects.checkIndex(docID, maxDoc());
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/index/PrefixCodedTerms.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/PrefixCodedTerms.java b/lucene/core/src/java/org/apache/lucene/index/PrefixCodedTerms.java
index ba56c2a..7a65d71 100644
--- a/lucene/core/src/java/org/apache/lucene/index/PrefixCodedTerms.java
+++ b/lucene/core/src/java/org/apache/lucene/index/PrefixCodedTerms.java
@@ -26,6 +26,7 @@ import org.apache.lucene.store.RAMOutputStream;
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.StringHelper;
 
 /**
  * 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;
 
       try {
-        int prefix = sharedPrefix(lastTerm.bytes, bytes);
-        int suffix = bytes.length - prefix;
-        if (field.equals(lastTerm.field)) {
+        final int prefix;
+        if (size > 0 && field.equals(lastTerm.field)) {
+          // same field as the last term
+          prefix = StringHelper.bytesDifference(lastTerm.bytes, bytes);
           output.writeVInt(prefix << 1);
         } else {
-          output.writeVInt(prefix << 1 | 1);
+          // field change
+          prefix = 0;
+          output.writeVInt(1);
           output.writeString(field);
         }
+
+        int suffix = bytes.length - prefix;
         output.writeVInt(suffix);
         output.writeBytes(bytes.bytes, bytes.offset + prefix, suffix);
         lastTermBytes.copyBytes(bytes);
@@ -102,20 +108,6 @@ public class PrefixCodedTerms implements Accountable {
         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}. */

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
index 59b6be5..0e075b4 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
 import java.util.Collection;
 
 import org.apache.lucene.util.Bits;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.PriorityQueue;
 
 /**
@@ -105,9 +106,7 @@ final class BooleanScorer extends BulkScorer {
     }
 
     public BulkScorerAndDoc get(int i) {
-      if (i < 0 || i >= size()) {
-        throw new IndexOutOfBoundsException();
-      }
+      FutureObjects.checkIndex(i, size());
       return (BulkScorerAndDoc) getHeapArray()[1 + i];
     }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java b/lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java
index 3bc65ef..3c5897f 100644
--- a/lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java
+++ b/lucene/core/src/java/org/apache/lucene/util/ArrayUtil.java
@@ -313,58 +313,6 @@ public final class ArrayUtil {
       code = code * 31 + array[i];
     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> */
   public static <T> void swap(T[] arr, int i, int j) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/BytesRef.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/BytesRef.java b/lucene/core/src/java/org/apache/lucene/util/BytesRef.java
index 2fcf28a..42c4e87 100644
--- a/lucene/core/src/java/org/apache/lucene/util/BytesRef.java
+++ b/lucene/core/src/java/org/apache/lucene/util/BytesRef.java
@@ -96,20 +96,8 @@ public final class BytesRef implements Comparable<BytesRef>,Cloneable {
    * @lucene.internal
    */
   public boolean bytesEquals(BytesRef other) {
-    assert other != null;
-    if (length == 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;
-    }
+    return FutureArrays.equals(this.bytes, this.offset, this.offset + this.length, 
+                               other.bytes, other.offset, other.offset + other.length);
   }
 
   /**
@@ -172,27 +160,8 @@ public final class BytesRef implements Comparable<BytesRef>,Cloneable {
   /** Unsigned byte order comparison */
   @Override
   public int compareTo(BytesRef other) {
-    // TODO: Once we are on Java 9 replace this by java.util.Arrays#compareUnsigned()
-    // which is implemented by a Hotspot intrinsic! Also consider building a
-    // 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;
+    return FutureArrays.compareUnsigned(this.bytes, this.offset, this.offset + this.length, 
+                                        other.bytes, other.offset, other.offset + other.length);
   }
     
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/BytesRefArray.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/BytesRefArray.java b/lucene/core/src/java/org/apache/lucene/util/BytesRefArray.java
index bab3d04..00222f4 100644
--- a/lucene/core/src/java/org/apache/lucene/util/BytesRefArray.java
+++ b/lucene/core/src/java/org/apache/lucene/util/BytesRefArray.java
@@ -93,34 +93,28 @@ public final class BytesRefArray implements SortableBytesRefArray {
    * @return the <i>n'th</i> element of this {@link BytesRefArray}
    */
   public BytesRef get(BytesRefBuilder spare, int index) {
-    if (lastElement > index) {
-      int offset = offsets[index];
-      int length = index == lastElement - 1 ? currentOffset - offset
-          : offsets[index + 1] - offset;
-      spare.grow(length);
-      spare.setLength(length);
-      pool.readBytes(offset, spare.bytes(), 0, spare.length());
-      return spare.get();
-    }
-    throw new IndexOutOfBoundsException("index " + index
-        + " must be less than the size: " + lastElement);
+    FutureObjects.checkIndex(index, lastElement);
+    int offset = offsets[index];
+    int length = index == lastElement - 1 ? currentOffset - offset
+        : offsets[index + 1] - offset;
+    spare.grow(length);
+    spare.setLength(length);
+    pool.readBytes(offset, spare.bytes(), 0, spare.length());
+    return spare.get();
   }
 
   /** 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. */
   private void setBytesRef(BytesRefBuilder spare, BytesRef result, int index) {
-    if (index < lastElement) {
-      int offset = offsets[index];
-      int length;
-      if (index == lastElement - 1) {
-        length = currentOffset - offset;
-      } else {
-        length = offsets[index + 1] - offset;
-      }
-      pool.setBytesRef(spare, result, offset, length);
+    FutureObjects.checkIndex(index, lastElement);
+    int offset = offsets[index];
+    int length;
+    if (index == lastElement - 1) {
+      length = currentOffset - offset;
     } 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) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/CharsRef.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/CharsRef.java b/lucene/core/src/java/org/apache/lucene/util/CharsRef.java
index aacdfcc..eb839a8 100644
--- a/lucene/core/src/java/org/apache/lucene/util/CharsRef.java
+++ b/lucene/core/src/java/org/apache/lucene/util/CharsRef.java
@@ -107,46 +107,15 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
   }
 
   public boolean charsEquals(CharsRef other) {
-    if (length == other.length) {
-      int otherUpto = other.offset;
-      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;
-    }
+    return FutureArrays.equals(this.chars, this.offset, this.offset + this.length, 
+                               other.chars, other.offset, other.offset + other.length);
   }
 
   /** Signed int order comparison */
   @Override
   public int compareTo(CharsRef other) {
-    if (this == other)
-      return 0;
-
-    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;
+    return FutureArrays.compare(this.chars, this.offset, this.offset + this.length, 
+                                other.chars, other.offset, other.offset + other.length);
   }
 
   @Override
@@ -162,18 +131,14 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
   @Override
   public char charAt(int index) {
     // NOTE: must do a real check here to meet the specs of CharSequence
-    if (index < 0 || index >= length) {
-      throw new IndexOutOfBoundsException();
-    }
+    FutureObjects.checkIndex(index, length);
     return chars[offset + index];
   }
 
   @Override
   public CharSequence subSequence(int start, int end) {
     // NOTE: must do a real check here to meet the specs of CharSequence
-    if (start < 0 || end > length || start > end) {
-      throw new IndexOutOfBoundsException();
-    }
+    FutureObjects.checkFromToIndex(start, end, length);
     return new CharsRef(chars, offset + start, end - start);
   }
   
@@ -195,40 +160,33 @@ public final class CharsRef implements Comparable<CharsRef>, CharSequence, Clone
 
     @Override
     public int compare(CharsRef a, CharsRef b) {
-      if (a == b)
-        return 0;
-
-      final char[] aChars = a.chars;
-      int aUpto = a.offset;
-      final char[] bChars = b.chars;
-      int bUpto = b.offset;
-
-      final int aStop = aUpto + Math.min(a.length, b.length);
-
-      while (aUpto < aStop) {
-        char aChar = aChars[aUpto++];
-        char bChar = bChars[bUpto++];
-        if (aChar != bChar) {
-          // http://icu-project.org/docs/papers/utf16_code_point_order.html
-          
-          /* 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;
-            }
+      int aEnd = a.offset + a.length;
+      int bEnd = b.offset + b.length;
+      int i = FutureArrays.mismatch(a.chars, a.offset, aEnd, 
+                                    b.chars, b.offset, bEnd);
+
+      if (i >= 0 && i < Math.min(a.length, b.length)) {
+        // http://icu-project.org/docs/papers/utf16_code_point_order.html
+
+        char aChar = a.chars[a.offset + i];
+        char bChar = b.chars[b.offset + i];        
+        /* 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;
           }
           
-          /* now aChar and bChar are in code point order */
-          return (int)aChar - (int)bChar; /* int must be 32 bits wide */
+          if (bChar >= 0xe000) {
+            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:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/FutureArrays.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/FutureArrays.java b/lucene/core/src/java/org/apache/lucene/util/FutureArrays.java
new file mode 100644
index 0000000..0154053
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/util/FutureArrays.java
@@ -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;
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/FutureObjects.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/FutureObjects.java b/lucene/core/src/java/org/apache/lucene/util/FutureObjects.java
new file mode 100644
index 0000000..b8ad4d3
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/util/FutureObjects.java
@@ -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;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/IntsRef.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/IntsRef.java b/lucene/core/src/java/org/apache/lucene/util/IntsRef.java
index 981210d..aa7bbce 100644
--- a/lucene/core/src/java/org/apache/lucene/util/IntsRef.java
+++ b/lucene/core/src/java/org/apache/lucene/util/IntsRef.java
@@ -93,45 +93,15 @@ public final class IntsRef implements Comparable<IntsRef>, Cloneable {
   }
 
   public boolean intsEquals(IntsRef other) {
-    if (length == other.length) {
-      int otherUpto = other.offset;
-      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;
-    }
+    return FutureArrays.equals(this.ints, this.offset, this.offset + this.length, 
+                               other.ints, other.offset, other.offset + other.length);
   }
 
   /** Signed int order comparison */
   @Override
   public int compareTo(IntsRef other) {
-    if (this == other) return 0;
-
-    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;
+    return FutureArrays.compare(this.ints, this.offset, this.offset + this.length, 
+                                other.ints, other.offset, other.offset + other.length);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/LongsRef.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/LongsRef.java b/lucene/core/src/java/org/apache/lucene/util/LongsRef.java
index 61b85cf..952d189 100644
--- a/lucene/core/src/java/org/apache/lucene/util/LongsRef.java
+++ b/lucene/core/src/java/org/apache/lucene/util/LongsRef.java
@@ -92,45 +92,15 @@ public final class LongsRef implements Comparable<LongsRef>, Cloneable {
   }
 
   public boolean longsEquals(LongsRef other) {
-    if (length == other.length) {
-      int otherUpto = other.offset;
-      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;
-    }
+    return FutureArrays.equals(this.longs, this.offset, this.offset + this.length, 
+                               other.longs, other.offset, other.offset + other.length);
   }
 
   /** Signed int order comparison */
   @Override
   public int compareTo(LongsRef other) {
-    if (this == other) return 0;
-
-    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;
+    return FutureArrays.compare(this.longs, this.offset, this.offset + this.length, 
+                                other.longs, other.offset, other.offset + other.length);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/StringHelper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/StringHelper.java b/lucene/core/src/java/org/apache/lucene/util/StringHelper.java
index 6d958ba..4c6d4fa 100644
--- a/lucene/core/src/java/org/apache/lucene/util/StringHelper.java
+++ b/lucene/core/src/java/org/apache/lucene/util/StringHelper.java
@@ -34,21 +34,19 @@ public abstract class StringHelper {
   /**
    * Compares two {@link BytesRef}, element by element, and returns the
    * 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 right The second {@link BytesRef} to compare
+   * @param priorTerm The first {@link BytesRef} to compare
+   * @param currentTerm The second {@link BytesRef} to compare
    * @return The number of common elements (from the start of each).
    */
-  public static int bytesDifference(BytesRef left, BytesRef right) {
-    int len = left.length < right.length ? left.length : right.length;
-    final byte[] bytesLeft = left.bytes;
-    final int offLeft = left.offset;
-    byte[] bytesRight = right.bytes;
-    final int offRight = right.offset;
-    for (int i = 0; i < len; i++)
-      if (bytesLeft[i+offLeft] != bytesRight[i+offRight])
-        return i;
-    return len;
+  public static int bytesDifference(BytesRef priorTerm, BytesRef currentTerm) {
+    int mismatch = FutureArrays.mismatch(priorTerm.bytes, priorTerm.offset, priorTerm.offset + priorTerm.length, 
+                                         currentTerm.bytes, currentTerm.offset, currentTerm.offset + currentTerm.length);
+    if (mismatch < 0) {
+      throw new IllegalArgumentException("terms out of order: priorTerm=" + priorTerm + ",currentTerm=" + currentTerm);
+    }
+    return mismatch;
   }
   
   /** 
@@ -57,15 +55,7 @@ public abstract class StringHelper {
    * This method assumes currentTerm comes after priorTerm.
    */
   public static int sortKeyLength(final BytesRef priorTerm, final BytesRef currentTerm) {
-    final int currentTermOffset = currentTerm.offset;
-    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);
+    return bytesDifference(priorTerm, currentTerm) + 1;
   }
 
   private StringHelper() {
@@ -83,17 +73,12 @@ public abstract class StringHelper {
    *         Otherwise <code>false</code>.
    */
   public static boolean startsWith(byte[] ref, BytesRef prefix) {
+    // not long enough to start with the prefix
     if (ref.length < prefix.length) {
       return false;
     }
-
-    for(int i=0;i<prefix.length;i++) {
-      if (ref[i] != prefix.bytes[prefix.offset+i]) {
-        return false;
-      }
-    }
-
-    return true;
+    return FutureArrays.equals(ref, 0, prefix.length,
+                               prefix.bytes, prefix.offset, prefix.offset + prefix.length);
   }
 
   /**
@@ -108,7 +93,12 @@ public abstract class StringHelper {
    *         Otherwise <code>false</code>.
    */
   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>.
    */
   public static boolean endsWith(BytesRef ref, BytesRef suffix) {
-    return sliceEquals(ref, suffix, ref.length - suffix.length);
-  }
-
-  private static boolean sliceEquals(BytesRef sliceToTest, BytesRef other, int pos) {
-    if (pos < 0 || sliceToTest.length - pos < other.length) {
+    int startAt = ref.length - suffix.length;
+    // not long enough to start with the suffix
+    if (startAt < 0) {
       return false;
     }
-    int i = sliceToTest.offset + pos;
-    int j = other.offset;
-    final int k = other.offset + other.length;
-    
-    while (j < k) {
-      if (sliceToTest.bytes[i++] != other.bytes[j++]) {
-        return false;
-      }
-    }
-    
-    return true;
+    return FutureArrays.equals(ref.bytes, ref.offset + startAt, ref.offset + startAt + suffix.length,
+                               suffix.bytes, suffix.offset, suffix.offset + suffix.length);
   }
 
   /** 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
    *  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) {
-    // TODO: dedup this w/ BytesRef.compareTo?
-    for(int i=0;i<count;i++) {
-      int cmp = (a[aOffset+i]&0xff) - (b[bOffset+i]&0xff);
-      if (cmp != 0) {
-        return cmp;
-      }
-    }
-
-    return 0;
+    return FutureArrays.compareUnsigned(a, aOffset, aOffset + count, b, bOffset, bOffset + count);
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/java/org/apache/lucene/util/automaton/Automaton.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/util/automaton/Automaton.java b/lucene/core/src/java/org/apache/lucene/util/automaton/Automaton.java
index e4a5bd9..9ef31e8 100644
--- a/lucene/core/src/java/org/apache/lucene/util/automaton/Automaton.java
+++ b/lucene/core/src/java/org/apache/lucene/util/automaton/Automaton.java
@@ -27,6 +27,7 @@ import java.util.Set;
 
 import org.apache.lucene.util.Accountable;
 import org.apache.lucene.util.ArrayUtil;
+import org.apache.lucene.util.FutureObjects;
 import org.apache.lucene.util.InPlaceMergeSorter;
 import org.apache.lucene.util.RamUsageEstimator;
 import org.apache.lucene.util.Sorter;
@@ -114,14 +115,8 @@ public class Automaton implements Accountable {
 
   /** Set or clear this state as an accept state. */
   public void setAccept(int state, boolean accept) {
-    if (state >= getNumStates()) {
-      throw new IllegalArgumentException("state=" + state + " is out of bounds (numStates=" + getNumStates() + ")");
-    }
-    if (accept) {
-      isAccept.set(state);
-    } else {
-      isAccept.clear(state);
-    }
+    FutureObjects.checkIndex(state, getNumStates());
+    isAccept.set(state, accept);
   }
 
   /** 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) {
     assert nextTransition%3 == 0;
 
-    if (source >= nextState/2) {
-      throw new IllegalArgumentException("source=" + source + " is out of bounds (maxState is " + (nextState/2-1) + ")");
-    }
-    if (dest >= nextState/2) {
-      throw new IllegalArgumentException("dest=" + dest + " is out of bounds (max state is " + (nextState/2-1) + ")");
-    }
+    int bounds = nextState/2;
+    FutureObjects.checkIndex(source, bounds);
+    FutureObjects.checkIndex(dest, bounds);
 
     growTransitions();
     if (curState != source) {
@@ -842,10 +834,7 @@ public class Automaton implements Accountable {
 
     /** Set or clear this state as an accept state. */
     public void setAccept(int state, boolean accept) {
-      if (state >= getNumStates()) {
-        throw new IllegalArgumentException("state=" + state + " is out of bounds (numStates=" + getNumStates() + ")");
-      }
-      
+      FutureObjects.checkIndex(state, getNumStates());      
       this.isAccept.set(state, accept);
     }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/test/org/apache/lucene/analysis/tokenattributes/TestCharTermAttributeImpl.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/analysis/tokenattributes/TestCharTermAttributeImpl.java b/lucene/core/src/test/org/apache/lucene/analysis/tokenattributes/TestCharTermAttributeImpl.java
index 3082129..9bf3621 100644
--- a/lucene/core/src/test/org/apache/lucene/analysis/tokenattributes/TestCharTermAttributeImpl.java
+++ b/lucene/core/src/test/org/apache/lucene/analysis/tokenattributes/TestCharTermAttributeImpl.java
@@ -46,10 +46,9 @@ public class TestCharTermAttributeImpl extends LuceneTestCase {
     CharTermAttributeImpl t = new CharTermAttributeImpl();
     char[] content = "hello".toCharArray();
     t.copyBuffer(content, 0, content.length);
-    IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
+    expectThrows(IndexOutOfBoundsException.class, () -> {
       t.setLength(-1);
     });
-    assertTrue(expected.getMessage().contains("must not be negative"));
   }
 
   public void testGrow() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/05a105a9/lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java b/lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java
index 79f4cbd..0cda337 100644
--- a/lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java
+++ b/lucene/core/src/test/org/apache/lucene/util/TestArrayUtil.java
@@ -16,8 +16,6 @@
  */
 package org.apache.lucene.util;
 
-
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
@@ -107,20 +105,6 @@ public class TestArrayUtil extends LuceneTestCase {
     test = ArrayUtil.parseInt("foo 1923 bar".toCharArray(), 4, 4);
     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) {
     final Random rnd = random();