diff --git a/src/build/source_templates/xxhash32_streaming.template b/src/build/source_templates/xxhash32_streaming.template index 6166758e..fe1cdc41 100644 --- a/src/build/source_templates/xxhash32_streaming.template +++ b/src/build/source_templates/xxhash32_streaming.template @@ -21,32 +21,41 @@ final class StreamingXXHash32Java${type} extends AbstractStreamingXXHash32Java { return new StreamingXXHash32Java${type}(seed); } + @Override + public StreamingXXHash32 newStreamingHash(XXHash32State savedState) { + return new StreamingXXHash32Java${type}((XXHash32JavaState) savedState); + } + } StreamingXXHash32Java${type}(int seed) { super(seed); } + StreamingXXHash32Java${type}(XXHash32JavaState savedState) { + super(savedState); + } + @Override public int getValue() { int h32; - if (totalLen >= 16) { - h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); + if (state.totalLen >= 16) { + h32 = rotateLeft(state.v1, 1) + rotateLeft(state.v2, 7) + rotateLeft(state.v3, 12) + rotateLeft(state.v4, 18); } else { - h32 = seed + PRIME5; + h32 = state.seed + PRIME5; } - h32 += totalLen; + h32 += state.totalLen; int off = 0; - while (off <= memSize - 4) { - h32 += readIntLE(memory, off) * PRIME3; + while (off <= state.memSize - 4) { + h32 += readIntLE(state.memory, off) * PRIME3; h32 = rotateLeft(h32, 17) * PRIME4; off += 4; } - while (off < memSize) { - h32 += (readByte(memory, off) & 0xFF) * PRIME5; + while (off < state.memSize) { + h32 += (readByte(state.memory, off) & 0xFF) * PRIME5; h32 = rotateLeft(h32, 11) * PRIME1; ++off; } @@ -64,45 +73,45 @@ final class StreamingXXHash32Java${type} extends AbstractStreamingXXHash32Java { public void update(byte[] buf, int off, int len) { checkRange(buf, off, len); - totalLen += len; + state.totalLen += len; - if (memSize + len < 16) { // fill in tmp buffer - System.arraycopy(buf, off, memory, memSize, len); - memSize += len; + if (state.memSize + len < 16) { // fill in tmp buffer + System.arraycopy(buf, off, state.memory, state.memSize, len); + state.memSize += len; return; } final int end = off + len; - if (memSize > 0) { // data left from previous update - System.arraycopy(buf, off, memory, memSize, 16 - memSize); + if (state.memSize > 0) { // data left from previous update + System.arraycopy(buf, off, state.memory, state.memSize, 16 - state.memSize); - v1 += readIntLE(memory, 0) * PRIME2; - v1 = rotateLeft(v1, 13); - v1 *= PRIME1; + state.v1 += readIntLE(state.memory, 0) * PRIME2; + state.v1 = rotateLeft(state.v1, 13); + state.v1 *= PRIME1; - v2 += readIntLE(memory, 4) * PRIME2; - v2 = rotateLeft(v2, 13); - v2 *= PRIME1; + state.v2 += readIntLE(state.memory, 4) * PRIME2; + state.v2 = rotateLeft(state.v2, 13); + state.v2 *= PRIME1; - v3 += readIntLE(memory, 8) * PRIME2; - v3 = rotateLeft(v3, 13); - v3 *= PRIME1; + state.v3 += readIntLE(state.memory, 8) * PRIME2; + state.v3 = rotateLeft(state.v3, 13); + state.v3 *= PRIME1; - v4 += readIntLE(memory, 12) * PRIME2; - v4 = rotateLeft(v4, 13); - v4 *= PRIME1; + state.v4 += readIntLE(state.memory, 12) * PRIME2; + state.v4 = rotateLeft(state.v4, 13); + state.v4 *= PRIME1; - off += 16 - memSize; - memSize = 0; + off += 16 - state.memSize; + state.memSize = 0; } { final int limit = end - 16; - int v1 = this.v1; - int v2 = this.v2; - int v3 = this.v3; - int v4 = this.v4; + int v1 = state.v1; + int v2 = state.v2; + int v3 = state.v3; + int v4 = state.v4; while (off <= limit) { v1 += readIntLE(buf, off) * PRIME2; @@ -126,15 +135,15 @@ final class StreamingXXHash32Java${type} extends AbstractStreamingXXHash32Java { off += 4; } - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - this.v4 = v4; + state.v1 = v1; + state.v2 = v2; + state.v3 = v3; + state.v4 = v4; } if (off < end) { - System.arraycopy(buf, off, memory, 0, end - off); - memSize = end - off; + System.arraycopy(buf, off, state.memory, 0, end - off); + state.memSize = end - off; } } diff --git a/src/build/source_templates/xxhash64_streaming.template b/src/build/source_templates/xxhash64_streaming.template index 2789ae0a..cea3226c 100644 --- a/src/build/source_templates/xxhash64_streaming.template +++ b/src/build/source_templates/xxhash64_streaming.template @@ -21,20 +21,29 @@ final class StreamingXXHash64Java${type} extends AbstractStreamingXXHash64Java { return new StreamingXXHash64Java${type}(seed); } + @Override + public StreamingXXHash64 newStreamingHash(XXHash64State savedState) { + return new StreamingXXHash64Java${type}((XXHash64JavaState) savedState); + } + } StreamingXXHash64Java${type}(long seed) { super(seed); } + StreamingXXHash64Java${type}(XXHash64JavaState savedState) { + super(savedState); + } + @Override public long getValue() { long h64; - if (totalLen >= 32) { - long v1 = this.v1; - long v2 = this.v2; - long v3 = this.v3; - long v4 = this.v4; + if (state.totalLen >= 32) { + long v1 = state.v1; + long v2 = state.v2; + long v3 = state.v3; + long v4 = state.v4; h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); @@ -50,27 +59,27 @@ final class StreamingXXHash64Java${type} extends AbstractStreamingXXHash64Java { v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; h64 = h64*PRIME64_1 + PRIME64_4; } else { - h64 = seed + PRIME64_5; + h64 = state.seed + PRIME64_5; } - h64 += totalLen; + h64 += state.totalLen; int off = 0; - while (off <= memSize - 8) { - long k1 = readLongLE(memory, off); + while (off <= state.memSize - 8) { + long k1 = readLongLE(state.memory, off); k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; off += 8; } - if (off <= memSize - 4) { - h64 ^= (readIntLE(memory, off) & 0xFFFFFFFFL) * PRIME64_1; + if (off <= state.memSize - 4) { + h64 ^= (readIntLE(state.memory, off) & 0xFFFFFFFFL) * PRIME64_1; h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; off += 4; } - while (off < memSize) { - h64 ^= (memory[off] & 0xFF) * PRIME64_5; + while (off < state.memSize) { + h64 ^= (state.memory[off] & 0xFF) * PRIME64_5; h64 = rotateLeft(h64, 11) * PRIME64_1; ++off; } @@ -88,45 +97,45 @@ final class StreamingXXHash64Java${type} extends AbstractStreamingXXHash64Java { public void update(byte[] buf, int off, int len) { checkRange(buf, off, len); - totalLen += len; + state.totalLen += len; - if (memSize + len < 32) { // fill in tmp buffer - System.arraycopy(buf, off, memory, memSize, len); - memSize += len; + if (state.memSize + len < 32) { // fill in tmp buffer + System.arraycopy(buf, off, state.memory, state.memSize, len); + state.memSize += len; return; } final int end = off + len; - if (memSize > 0) { // data left from previous update - System.arraycopy(buf, off, memory, memSize, 32 - memSize); + if (state.memSize > 0) { // data left from previous update + System.arraycopy(buf, off, state.memory, state.memSize, 32 - state.memSize); - v1 += readLongLE(memory, 0) * PRIME64_2; - v1 = rotateLeft(v1, 31); - v1 *= PRIME64_1; + state.v1 += readLongLE(state.memory, 0) * PRIME64_2; + state.v1 = rotateLeft(state.v1, 31); + state.v1 *= PRIME64_1; - v2 += readLongLE(memory, 8) * PRIME64_2; - v2 = rotateLeft(v2, 31); - v2 *= PRIME64_1; + state.v2 += readLongLE(state.memory, 8) * PRIME64_2; + state.v2 = rotateLeft(state.v2, 31); + state.v2 *= PRIME64_1; - v3 += readLongLE(memory, 16) * PRIME64_2; - v3 = rotateLeft(v3, 31); - v3 *= PRIME64_1; + state.v3 += readLongLE(state.memory, 16) * PRIME64_2; + state.v3 = rotateLeft(state.v3, 31); + state.v3 *= PRIME64_1; - v4 += readLongLE(memory, 24) * PRIME64_2; - v4 = rotateLeft(v4, 31); - v4 *= PRIME64_1; + state.v4 += readLongLE(state.memory, 24) * PRIME64_2; + state.v4 = rotateLeft(state.v4, 31); + state.v4 *= PRIME64_1; - off += 32 - memSize; - memSize = 0; + off += 32 - state.memSize; + state.memSize = 0; } { final int limit = end - 32; - long v1 = this.v1; - long v2 = this.v2; - long v3 = this.v3; - long v4 = this.v4; + long v1 = state.v1; + long v2 = state.v2; + long v3 = state.v3; + long v4 = state.v4; while (off <= limit) { v1 += readLongLE(buf, off) * PRIME64_2; @@ -150,15 +159,15 @@ final class StreamingXXHash64Java${type} extends AbstractStreamingXXHash64Java { off += 8; } - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - this.v4 = v4; + state.v1 = v1; + state.v2 = v2; + state.v3 = v3; + state.v4 = v4; } if (off < end) { - System.arraycopy(buf, off, memory, 0, end - off); - memSize = end - off; + System.arraycopy(buf, off, state.memory, 0, end - off); + state.memSize = end - off; } } diff --git a/src/java/net/jpountz/xxhash/AbstractStreamingXXHash32Java.java b/src/java/net/jpountz/xxhash/AbstractStreamingXXHash32Java.java index 3dd3ed4f..de008d7d 100644 --- a/src/java/net/jpountz/xxhash/AbstractStreamingXXHash32Java.java +++ b/src/java/net/jpountz/xxhash/AbstractStreamingXXHash32Java.java @@ -14,29 +14,28 @@ * limitations under the License. */ -import static net.jpountz.xxhash.XXHashConstants.PRIME1; -import static net.jpountz.xxhash.XXHashConstants.PRIME2; - abstract class AbstractStreamingXXHash32Java extends StreamingXXHash32 { - int v1, v2, v3, v4, memSize; - long totalLen; - final byte[] memory; + final XXHash32JavaState state; AbstractStreamingXXHash32Java(int seed) { super(seed); - memory = new byte[16]; - reset(); + state = new XXHash32JavaState(seed); + } + + AbstractStreamingXXHash32Java(XXHash32JavaState savedState) { + super(savedState.seed); + state = new XXHash32JavaState(savedState); } @Override public void reset() { - v1 = seed + PRIME1 + PRIME2; - v2 = seed + PRIME2; - v3 = seed + 0; - v4 = seed - PRIME1; - totalLen = 0; - memSize = 0; + state.reset(); } + @Override + public XXHash32State getState() { + // Return a copy of the internal state + return new XXHash32JavaState(state); + } } diff --git a/src/java/net/jpountz/xxhash/AbstractStreamingXXHash64Java.java b/src/java/net/jpountz/xxhash/AbstractStreamingXXHash64Java.java index 97e294dd..ab467787 100644 --- a/src/java/net/jpountz/xxhash/AbstractStreamingXXHash64Java.java +++ b/src/java/net/jpountz/xxhash/AbstractStreamingXXHash64Java.java @@ -14,30 +14,28 @@ * limitations under the License. */ -import static net.jpountz.xxhash.XXHashConstants.PRIME64_1; -import static net.jpountz.xxhash.XXHashConstants.PRIME64_2; - abstract class AbstractStreamingXXHash64Java extends StreamingXXHash64 { - int memSize; - long v1, v2, v3, v4; - long totalLen; - final byte[] memory; + final XXHash64JavaState state; AbstractStreamingXXHash64Java(long seed) { super(seed); - memory = new byte[32]; - reset(); + state = new XXHash64JavaState(seed); + } + + AbstractStreamingXXHash64Java(XXHash64JavaState savedState) { + super(savedState.seed); + state = new XXHash64JavaState(savedState); } @Override public void reset() { - v1 = seed + PRIME64_1 + PRIME64_2; - v2 = seed + PRIME64_2; - v3 = seed + 0; - v4 = seed - PRIME64_1; - totalLen = 0; - memSize = 0; + state.reset(); } + @Override + public XXHash64State getState() { + // Return a copy of the internal state + return new XXHash64JavaState(state); + } } diff --git a/src/java/net/jpountz/xxhash/StreamingXXHash32.java b/src/java/net/jpountz/xxhash/StreamingXXHash32.java index bbfab1d3..25dc44d6 100644 --- a/src/java/net/jpountz/xxhash/StreamingXXHash32.java +++ b/src/java/net/jpountz/xxhash/StreamingXXHash32.java @@ -44,6 +44,7 @@ interface Factory { StreamingXXHash32 newStreamingHash(int seed); + StreamingXXHash32 newStreamingHash(XXHash32State savedState); } final int seed; @@ -74,6 +75,12 @@ interface Factory { */ public abstract void reset(); + /** + * Returns an opaque object that encapsulates the current state of the hash. This can be used later + * to get a new StreamingXXHash32 object that can continue from there. + */ + public abstract XXHash32State getState(); + @Override public String toString() { return getClass().getSimpleName() + "(seed=" + seed + ")"; diff --git a/src/java/net/jpountz/xxhash/StreamingXXHash32JNI.java b/src/java/net/jpountz/xxhash/StreamingXXHash32JNI.java index e9b58fb4..1b84b7ca 100644 --- a/src/java/net/jpountz/xxhash/StreamingXXHash32JNI.java +++ b/src/java/net/jpountz/xxhash/StreamingXXHash32JNI.java @@ -26,6 +26,11 @@ public StreamingXXHash32 newStreamingHash(int seed) { return new StreamingXXHash32JNI(seed); } + @Override + public StreamingXXHash32 newStreamingHash(XXHash32State savedState) { + throw new UnsupportedOperationException("The JNI implementation does not support resuming from a saved state"); + } + } private long state; @@ -48,6 +53,12 @@ public void reset() { state = XXHashJNI.XXH32_init(seed); } + /** JNI implementation does not support saving the internal state. */ + @Override + public XXHash32State getState() { + throw new UnsupportedOperationException("The JNI implementation does not support resuming from a saved state"); + } + @Override public int getValue() { checkState(); diff --git a/src/java/net/jpountz/xxhash/StreamingXXHash64.java b/src/java/net/jpountz/xxhash/StreamingXXHash64.java index c84569a8..d4b4738e 100644 --- a/src/java/net/jpountz/xxhash/StreamingXXHash64.java +++ b/src/java/net/jpountz/xxhash/StreamingXXHash64.java @@ -44,6 +44,7 @@ interface Factory { StreamingXXHash64 newStreamingHash(long seed); + StreamingXXHash64 newStreamingHash(XXHash64State savedState); } final long seed; @@ -74,6 +75,12 @@ interface Factory { */ public abstract void reset(); + /** + * Returns an opaque object that encapsulates the current state of the hash. This can be used later + * to get a new StreamingXXHash64 object that can continue from there. + */ + public abstract XXHash64State getState(); + @Override public String toString() { return getClass().getSimpleName() + "(seed=" + seed + ")"; diff --git a/src/java/net/jpountz/xxhash/StreamingXXHash64JNI.java b/src/java/net/jpountz/xxhash/StreamingXXHash64JNI.java index b7ab1acd..ae201430 100644 --- a/src/java/net/jpountz/xxhash/StreamingXXHash64JNI.java +++ b/src/java/net/jpountz/xxhash/StreamingXXHash64JNI.java @@ -26,6 +26,11 @@ public StreamingXXHash64 newStreamingHash(long seed) { return new StreamingXXHash64JNI(seed); } + @Override + public StreamingXXHash64 newStreamingHash(XXHash64State savedState) { + throw new UnsupportedOperationException("The JNI implementation does not support resuming from a saved state"); + } + } private long state; @@ -48,6 +53,12 @@ public void reset() { state = XXHashJNI.XXH64_init(seed); } + /** JNI implementation does not support saving the internal state. */ + @Override + public XXHash64State getState() { + throw new UnsupportedOperationException("The JNI implementation does not support resuming from a saved state"); + } + @Override public long getValue() { checkState(); diff --git a/src/java/net/jpountz/xxhash/XXHash32JavaState.java b/src/java/net/jpountz/xxhash/XXHash32JavaState.java new file mode 100644 index 00000000..d9616771 --- /dev/null +++ b/src/java/net/jpountz/xxhash/XXHash32JavaState.java @@ -0,0 +1,50 @@ +package net.jpountz.xxhash; + +import java.util.Arrays; + +import static net.jpountz.xxhash.XXHashConstants.PRIME1; +import static net.jpountz.xxhash.XXHashConstants.PRIME2; + +/** + * Internal state for {@link StreamingXXHash32} java implementations. Meant to be serializable. + */ +public class XXHash32JavaState implements XXHash32State { + final int seed; + final byte[] memory; + + int memSize; + int v1, v2, v3, v4; + long totalLen; + + /** Creates a new "initial" state for the given seed */ + public XXHash32JavaState(int seed) { + this.seed = seed; + memory = new byte[16]; + reset(); + } + + /** Copy constructor. New state is completely detached from original */ + XXHash32JavaState(XXHash32JavaState toCopy) { + this.seed = toCopy.seed; + this.memory = Arrays.copyOf(toCopy.memory, toCopy.memory.length); + + // Mutable fields + this.memSize = toCopy.memSize; + this.totalLen = toCopy.totalLen; + this.v1 = toCopy.v1; + this.v2 = toCopy.v2; + this.v3 = toCopy.v3; + this.v4 = toCopy.v4; + } + + void reset() { + v1 = seed + PRIME1 + PRIME2; + v2 = seed + PRIME2; + v3 = seed + 0; + v4 = seed - PRIME1; + totalLen = 0; + memSize = 0; + } + +} + diff --git a/src/java/net/jpountz/xxhash/XXHash32State.java b/src/java/net/jpountz/xxhash/XXHash32State.java new file mode 100644 index 00000000..e4654a44 --- /dev/null +++ b/src/java/net/jpountz/xxhash/XXHash32State.java @@ -0,0 +1,4 @@ +package net.jpountz.xxhash; + +public interface XXHash32State { +} diff --git a/src/java/net/jpountz/xxhash/XXHash64JavaState.java b/src/java/net/jpountz/xxhash/XXHash64JavaState.java new file mode 100644 index 00000000..1a4a0fb3 --- /dev/null +++ b/src/java/net/jpountz/xxhash/XXHash64JavaState.java @@ -0,0 +1,49 @@ +package net.jpountz.xxhash; + +import java.util.Arrays; + +import static net.jpountz.xxhash.XXHashConstants.PRIME64_1; +import static net.jpountz.xxhash.XXHashConstants.PRIME64_2; + +/** + * Internal state for {@link StreamingXXHash64} java implementations. Meant to be serializable. + */ +public class XXHash64JavaState implements XXHash64State { + final long seed; + final byte[] memory; + + int memSize; + long v1, v2, v3, v4; + long totalLen; + + /** Creates a new "initial" state for the given seed */ + public XXHash64JavaState(long seed) { + this.seed = seed; + memory = new byte[32]; + reset(); + } + + /** Copy constructor. New state is completely detached from original */ + XXHash64JavaState(XXHash64JavaState toCopy) { + this.seed = toCopy.seed; + this.memory = Arrays.copyOf(toCopy.memory, toCopy.memory.length); + + // Mutable fields + this.memSize = toCopy.memSize; + this.totalLen = toCopy.totalLen; + this.v1 = toCopy.v1; + this.v2 = toCopy.v2; + this.v3 = toCopy.v3; + this.v4 = toCopy.v4; + } + + void reset() { + v1 = seed + PRIME64_1 + PRIME64_2; + v2 = seed + PRIME64_2; + v3 = seed + 0; + v4 = seed - PRIME64_1; + totalLen = 0; + memSize = 0; + } + +} diff --git a/src/java/net/jpountz/xxhash/XXHash64State.java b/src/java/net/jpountz/xxhash/XXHash64State.java new file mode 100644 index 00000000..893e9e84 --- /dev/null +++ b/src/java/net/jpountz/xxhash/XXHash64State.java @@ -0,0 +1,4 @@ +package net.jpountz.xxhash; + +public interface XXHash64State { +} diff --git a/src/java/net/jpountz/xxhash/XXHashFactory.java b/src/java/net/jpountz/xxhash/XXHashFactory.java index 9a9b4d15..389ef605 100644 --- a/src/java/net/jpountz/xxhash/XXHashFactory.java +++ b/src/java/net/jpountz/xxhash/XXHashFactory.java @@ -229,6 +229,20 @@ public StreamingXXHash32 newStreamingHash32(int seed) { return streamingHash32Factory.newStreamingHash(seed); } + /** + * Return a new {@link StreamingXXHash32} instance that continues from the saved state. + * + * @param savedState the saved state to use. + * @return a {@link StreamingXXHash32} instance + */ + public StreamingXXHash32 newStreamingHash32(XXHash32State savedState) { + if (this != NATIVE_INSTANCE && savedState instanceof XXHash32JavaState) { + return streamingHash32Factory.newStreamingHash(savedState); + } else { + throw new UnsupportedOperationException("Resumable state is only supported by the Java implementations"); + } + } + /** * Return a new {@link StreamingXXHash64} instance. * @@ -239,6 +253,20 @@ public StreamingXXHash64 newStreamingHash64(long seed) { return streamingHash64Factory.newStreamingHash(seed); } + /** + * Return a new {@link StreamingXXHash64} instance that continues from the saved state. + * + * @param savedState the saved state to use. + * @return a {@link StreamingXXHash64} instance + */ + public StreamingXXHash64 newStreamingHash64(XXHash64State savedState) { + if (this != NATIVE_INSTANCE && savedState instanceof XXHash64JavaState) { + return streamingHash64Factory.newStreamingHash(savedState); + } else { + throw new UnsupportedOperationException("Resumable state is only supported by the Java implementations"); + } + } + /** * Prints the fastest instance. *