ICU-20373 simpler state saving for Java string tries via long not object
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java b/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
index b5e0db3..10ff5ba 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
@@ -87,6 +87,40 @@
}
/**
+ * Returns the state of this trie as a 64-bit integer.
+ * The state value is never 0.
+ *
+ * @return opaque state value
+ * @see #resetToState64
+ * @draft ICU 64
+ * @provisional This API might change or be removed in a future release.
+ */
+ public long getState64() {
+ return ((long)remainingMatchLength_ << 32) | pos_;
+ }
+
+ /**
+ * Resets this trie to the saved state.
+ * Unlike {@link #resetToState(State)}, the 64-bit state value
+ * must be from {@link #getState64()} from the same trie object or
+ * from one initialized the exact same way.
+ * Because of no validation, this method is faster.
+ *
+ * @param state The opaque trie state value from getState64().
+ * @return this
+ * @see #getState64
+ * @see #resetToState
+ * @see #reset
+ * @draft ICU 64
+ * @provisional This API might change or be removed in a future release.
+ */
+ public BytesTrie resetToState64(long state) {
+ remainingMatchLength_ = (int)(state >> 32);
+ pos_ = (int)state;
+ return this;
+ }
+
+ /**
* BytesTrie state object, for saving a trie's current state
* and resetting the trie back to this state later.
* @stable ICU 4.8
@@ -120,6 +154,8 @@
/**
* Resets this trie to the saved state.
+ * Slower than {@link #resetToState64(long)} which does not validate the state value.
+ *
* @param state The State object which holds a saved trie state.
* @return this
* @throws IllegalArgumentException if the state object contains no state,
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java b/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
index 8b93423..bb3f47a 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
@@ -90,6 +90,40 @@
}
/**
+ * Returns the state of this trie as a 64-bit integer.
+ * The state value is never 0.
+ *
+ * @return opaque state value
+ * @see #resetToState64
+ * @draft ICU 64
+ * @provisional This API might change or be removed in a future release.
+ */
+ public long getState64() {
+ return ((long)remainingMatchLength_ << 32) | pos_;
+ }
+
+ /**
+ * Resets this trie to the saved state.
+ * Unlike {@link #resetToState(State)}, the 64-bit state value
+ * must be from {@link #getState64()} from the same trie object or
+ * from one initialized the exact same way.
+ * Because of no validation, this method is faster.
+ *
+ * @param state The opaque trie state value from getState64().
+ * @return this
+ * @see #getState64
+ * @see #resetToState
+ * @see #reset
+ * @draft ICU 64
+ * @provisional This API might change or be removed in a future release.
+ */
+ public CharsTrie resetToState64(long state) {
+ remainingMatchLength_ = (int)(state >> 32);
+ pos_ = (int)state;
+ return this;
+ }
+
+ /**
* CharsTrie state object, for saving a trie's current state
* and resetting the trie back to this state later.
* @stable ICU 4.8
@@ -123,6 +157,8 @@
/**
* Resets this trie to the saved state.
+ * Slower than {@link #resetToState64(long)} which does not validate the state value.
+ *
* @param state The State object which holds a saved trie state.
* @return this
* @throws IllegalArgumentException if the state object contains no state,
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
index 37af016..6cca357 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
@@ -547,6 +547,7 @@
checkFirst(trie, data, dataLength);
checkNext(trie, data, dataLength);
checkNextWithState(trie, data, dataLength);
+ checkNextWithState64(trie, data, dataLength);
checkNextString(trie, data, dataLength);
checkIterator(trie, data, dataLength);
}
@@ -739,6 +740,54 @@
}
}
+ private void checkNextWithState64(BytesTrie trie, StringAndValue data[], int dataLength) {
+ assertNotEquals("trie(initial state).getState64()!=0", 0, trie.getState64());
+ for(int i=0; i<dataLength; ++i) {
+ byte[] expectedString=data[i].bytes;
+ int stringLength=data[i].s.length();
+ int partialLength=stringLength/3;
+ for(int j=0; j<partialLength; ++j) {
+ if(!trie.next(expectedString[j]).matches()) {
+ errln("trie.next()=BytesTrie.Result.NO_MATCH for a prefix of "+data[i].s);
+ return;
+ }
+ }
+ long state = trie.getState64();
+ assertNotEquals("trie.getState64()!=0", 0, state);
+ BytesTrie.Result resultAtState=trie.current();
+ BytesTrie.Result result;
+ int valueAtState=-99;
+ if(resultAtState.hasValue()) {
+ valueAtState=trie.getValue();
+ }
+ result=trie.next(0); // mismatch
+ if(result!=BytesTrie.Result.NO_MATCH || result!=trie.current()) {
+ errln("trie.next(0) matched after part of "+data[i].s);
+ }
+ if( resultAtState!=trie.resetToState64(state).current() ||
+ (resultAtState.hasValue() && valueAtState!=trie.getValue())
+ ) {
+ errln("trie.next(part of "+data[i].s+") changes current()/getValue() after "+
+ "saveState/next(0)/resetToState");
+ } else if(!(result=trie.next(expectedString, partialLength, stringLength)).hasValue() ||
+ result!=trie.current()) {
+ errln("trie.next(rest of "+data[i].s+") does not seem to contain "+data[i].s+" after "+
+ "saveState/next(0)/resetToState");
+ } else if(!(result=trie.resetToState64(state).
+ next(expectedString, partialLength, stringLength)).hasValue() ||
+ result!=trie.current()) {
+ errln("trie does not seem to contain "+data[i].s+
+ " after saveState/next(rest)/resetToState");
+ } else if(trie.getValue()!=data[i].value) {
+ errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
+ data[i].s,
+ trie.getValue(), trie.getValue(),
+ data[i].value, data[i].value));
+ }
+ trie.reset();
+ }
+ }
+
// next(string) is also tested in other functions,
// but here we try to go partway through the string, and then beyond it.
private void checkNextString(BytesTrie trie, StringAndValue data[], int dataLength) {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
index d153987..149e7bf 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
@@ -674,6 +674,7 @@
checkFirst(trie, data, dataLength);
checkNext(trie, data, dataLength);
checkNextWithState(trie, data, dataLength);
+ checkNextWithState64(trie, data, dataLength);
checkNextString(trie, data, dataLength);
checkIterator(trie, data, dataLength);
}
@@ -885,6 +886,54 @@
}
}
+ private void checkNextWithState64(CharsTrie trie, StringAndValue[] data, int dataLength) {
+ assertNotEquals("trie(initial state).getState64()!=0", 0, trie.getState64());
+ for(int i=0; i<dataLength; ++i) {
+ String expectedString=data[i].s;
+ int stringLength=expectedString.length();
+ int partialLength=stringLength/3;
+ for(int j=0; j<partialLength; ++j) {
+ if(!trie.next(expectedString.charAt(j)).matches()) {
+ errln("trie.next()=BytesTrie.Result.NO_MATCH for a prefix of "+data[i].s);
+ return;
+ }
+ }
+ long state = trie.getState64();
+ assertNotEquals("trie.getState64()!=0", 0, state);
+ BytesTrie.Result resultAtState=trie.current();
+ BytesTrie.Result result;
+ int valueAtState=-99;
+ if(resultAtState.hasValue()) {
+ valueAtState=trie.getValue();
+ }
+ result=trie.next(0); // mismatch
+ if(result!=BytesTrie.Result.NO_MATCH || result!=trie.current()) {
+ errln("trie.next(0) matched after part of "+data[i].s);
+ }
+ if( resultAtState!=trie.resetToState64(state).current() ||
+ (resultAtState.hasValue() && valueAtState!=trie.getValue())
+ ) {
+ errln("trie.next(part of "+data[i].s+") changes current()/getValue() after "+
+ "saveState/next(0)/resetToState");
+ } else if(!(result=trie.next(expectedString, partialLength, stringLength)).hasValue() ||
+ result!=trie.current()) {
+ errln("trie.next(rest of "+data[i].s+") does not seem to contain "+data[i].s+" after "+
+ "saveState/next(0)/resetToState");
+ } else if(!(result=trie.resetToState64(state).
+ next(expectedString, partialLength, stringLength)).hasValue() ||
+ result!=trie.current()) {
+ errln("trie does not seem to contain "+data[i].s+
+ " after saveState/next(rest)/resetToState");
+ } else if(trie.getValue()!=data[i].value) {
+ errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
+ data[i].s,
+ trie.getValue(), trie.getValue(),
+ data[i].value, data[i].value));
+ }
+ trie.reset();
+ }
+ }
+
// next(string) is also tested in other functions,
// but here we try to go partway through the string, and then beyond it.
private void checkNextString(CharsTrie trie, StringAndValue[] data, int dataLength) {