org.apache.commons.codec.digest.XXHash32 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gwt-commons-codec Show documentation
Show all versions of gwt-commons-codec Show documentation
The Apache Commons Codec component contains encoders and decoders for
various formats such as Base16, Base32, Base64, digest, and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
This is a port for GWT, which enables program, to use Apache Commons Codec
also in the frontend compiled by the gwt compiler to java-script.
/*
* 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.commons.codec.digest;
import static java.lang.Integer.rotateLeft;
import com.google.gwt.core.shared.GwtIncompatible;
import java.util.zip.Checksum;
/**
* Implementation of the xxhash32 hash algorithm.
*
*
* Copied from Commons Compress 1.14 https://git-wip-us.apache.org/repos/asf?p=commons-compress.git;a=blob;f=src/main/java/org/apache/commons/compress/compressors/lz4/XXHash32.java;h=a406ffc197449be594d46f0d2712b2d4786a1e68;hb=HEAD
*
*
* NotThreadSafe
*
*
* @see xxHash
* @since 1.11
*/
@GwtIncompatible("incompatible class")
public class XXHash32 implements Checksum {
private static final int BUF_SIZE = 16;
private static final int ROTATE_BITS = 13;
private static final int PRIME1 = (int) 2654435761l;
private static final int PRIME2 = (int) 2246822519l;
private static final int PRIME3 = (int) 3266489917l;
private static final int PRIME4 = 668265263;
private static final int PRIME5 = 374761393;
private final byte[] oneByte = new byte[1];
private final int[] state = new int[4];
// Note: the code used to use ByteBuffer but the manual method is 50% faster
// See: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/2f56fb5c
private final byte[] buffer = new byte[BUF_SIZE];
private final int seed;
private int totalLen;
private int pos;
/** Set to true when the state array has been updated since the last reset. */
private boolean stateUpdated;
/**
* Creates an XXHash32 instance with a seed of 0.
*/
public XXHash32() {
this(0);
}
/**
* Creates an XXHash32 instance.
* @param seed the seed to use
*/
public XXHash32(final int seed) {
this.seed = seed;
initializeState();
}
@Override
public void reset() {
initializeState();
totalLen = 0;
pos = 0;
stateUpdated = false;
}
@Override
public void update(final int b) {
oneByte[0] = (byte) (b & 0xff);
update(oneByte, 0, 1);
}
@Override
public void update(final byte[] b, int off, final int len) {
if (len <= 0) {
return;
}
totalLen += len;
final int end = off + len;
// Check if the unprocessed bytes and new bytes can fill a block of 16.
// Make this overflow safe in the event that len is Integer.MAX_VALUE.
// Equivalent to: (pos + len < BUF_SIZE)
if (pos + len - BUF_SIZE < 0) {
System.arraycopy(b, off, buffer, pos, len);
pos += len;
return;
}
// Process left-over bytes with new bytes
if (pos > 0) {
final int size = BUF_SIZE - pos;
System.arraycopy(b, off, buffer, pos, size);
process(buffer, 0);
off += size;
}
final int limit = end - BUF_SIZE;
while (off <= limit) {
process(b, off);
off += BUF_SIZE;
}
// Handle left-over bytes
if (off < end) {
pos = end - off;
System.arraycopy(b, off, buffer, 0, pos);
} else {
pos = 0;
}
}
@Override
public long getValue() {
int hash;
if (stateUpdated) {
// Hash with the state
hash =
rotateLeft(state[0], 1) +
rotateLeft(state[1], 7) +
rotateLeft(state[2], 12) +
rotateLeft(state[3], 18);
} else {
// Hash using the original seed from position 2
hash = state[2] + PRIME5;
}
hash += totalLen;
int idx = 0;
final int limit = pos - 4;
for (; idx <= limit; idx += 4) {
hash = rotateLeft(hash + getInt(buffer, idx) * PRIME3, 17) * PRIME4;
}
while (idx < pos) {
hash = rotateLeft(hash + (buffer[idx++] & 0xff) * PRIME5, 11) * PRIME1;
}
hash ^= hash >>> 15;
hash *= PRIME2;
hash ^= hash >>> 13;
hash *= PRIME3;
hash ^= hash >>> 16;
return hash & 0xffffffffl;
}
/**
* Gets the little-endian int from 4 bytes starting at the specified index.
*
* @param buffer The data
* @param idx The index
* @return The little-endian int
*/
private static int getInt(final byte[] buffer, final int idx) {
return ((buffer[idx ] & 0xff) ) |
((buffer[idx + 1] & 0xff) << 8) |
((buffer[idx + 2] & 0xff) << 16) |
((buffer[idx + 3] & 0xff) << 24);
}
private void initializeState() {
state[0] = seed + PRIME1 + PRIME2;
state[1] = seed + PRIME2;
state[2] = seed;
state[3] = seed - PRIME1;
}
private void process(final byte[] b, final int offset) {
// local shadows for performance
int s0 = state[0];
int s1 = state[1];
int s2 = state[2];
int s3 = state[3];
s0 = rotateLeft(s0 + getInt(b, offset) * PRIME2, ROTATE_BITS) * PRIME1;
s1 = rotateLeft(s1 + getInt(b, offset + 4) * PRIME2, ROTATE_BITS) * PRIME1;
s2 = rotateLeft(s2 + getInt(b, offset + 8) * PRIME2, ROTATE_BITS) * PRIME1;
s3 = rotateLeft(s3 + getInt(b, offset + 12) * PRIME2, ROTATE_BITS) * PRIME1;
state[0] = s0;
state[1] = s1;
state[2] = s2;
state[3] = s3;
stateUpdated = true;
}
}