8294432: Add provisions to calculate hash values from MemorySegments

Reviewed-by: mcimadamore
This commit is contained in:
Per Minborg 2024-11-27 13:26:36 +00:00
parent 4a22c1fefc
commit a1473ec302
3 changed files with 338 additions and 0 deletions

View file

@ -28,7 +28,9 @@ package jdk.internal.foreign;
import jdk.internal.misc.ScopedMemoryAccess;
import jdk.internal.util.Architecture;
import jdk.internal.util.ArraysSupport;
import jdk.internal.util.ByteArrayLittleEndian;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.Stable;
import java.lang.foreign.MemorySegment;
@ -156,6 +158,94 @@ public final class SegmentBulkOperations {
}
}
private static final @Stable int[] POWERS_OF_31 = new int[]{
0x0000001f, 0x000003c1, 0x0000745f, 0x000e1781,
0x01b4d89f, 0x34e63b41, 0x67e12cdf, 0x94446f01};
/**
* {@return a 32-bit hash value calculated from the content in the provided
* {@code segment} between the provided offsets}
* <p>
* The method is implemented as a 32-bit polynomial hash function equivalent to:
* {@snippet lang=java :
* final long length = toOffset - fromOffset;
* segment.checkBounds(fromOffset, length);
* int result = 1;
* for (long i = fromOffset; i < toOffset; i++) {
* result = 31 * result + segment.get(JAVA_BYTE, i);
* }
* return result;
* }
* but is potentially more performant.
*
* @param segment from which a content hash should be computed
* @param fromOffset starting offset (inclusive) in the segment
* @param toOffset ending offset (non-inclusive) in the segment
* @throws WrongThreadException if this method is called from a thread {@code T},
* such that {@code srcSegment.isAccessibleBy(T) == false}
* @throws IllegalStateException if the {@linkplain MemorySegment#scope() scope}
* associated with {@code segment} is not
* {@linkplain MemorySegment.Scope#isAlive() alive}
* @throws IndexOutOfBoundsException if either {@code fromOffset} or {@code toOffset}
* are {@code > segment.byteSize}
* @throws IndexOutOfBoundsException if either {@code fromOffset} or {@code toOffset}
* are {@code < 0}
* @throws IndexOutOfBoundsException if {@code toOffset - fromOffset} is {@code < 0}
*/
@ForceInline
public static int contentHash(AbstractMemorySegmentImpl segment, long fromOffset, long toOffset) {
final long length = toOffset - fromOffset;
segment.checkBounds(fromOffset, length);
if (length == 0) {
// The state has to be checked explicitly for zero-length segments
segment.scope.checkValidState();
return 1;
}
int result = 1;
final long longBytes = length & ((1L << 62) - 8);
final long limit = fromOffset + longBytes;
for (; fromOffset < limit; fromOffset += 8) {
long val = SCOPED_MEMORY_ACCESS.getLongUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian());
result = result * POWERS_OF_31[7]
+ ((byte) (val >>> 56)) * POWERS_OF_31[6]
+ ((byte) (val >>> 48)) * POWERS_OF_31[5]
+ ((byte) (val >>> 40)) * POWERS_OF_31[4]
+ ((byte) (val >>> 32)) * POWERS_OF_31[3]
+ ((byte) (val >>> 24)) * POWERS_OF_31[2]
+ ((byte) (val >>> 16)) * POWERS_OF_31[1]
+ ((byte) (val >>> 8)) * POWERS_OF_31[0]
+ ((byte) val);
}
int remaining = (int) (length - longBytes);
// 0...0X00
if (remaining >= 4) {
int val = SCOPED_MEMORY_ACCESS.getIntUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian());
result = result * POWERS_OF_31[3]
+ ((byte) (val >>> 24)) * POWERS_OF_31[2]
+ ((byte) (val >>> 16)) * POWERS_OF_31[1]
+ ((byte) (val >>> 8)) * POWERS_OF_31[0]
+ ((byte) val);
fromOffset += 4;
remaining -= 4;
}
// 0...00X0
if (remaining >= 2) {
short val = SCOPED_MEMORY_ACCESS.getShortUnaligned(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset, !Architecture.isLittleEndian());
result = result * POWERS_OF_31[1]
+ ((byte) (val >>> 8)) * POWERS_OF_31[0]
+ ((byte) val);
fromOffset += 2;
remaining -= 2;
}
// 0...000X
if (remaining == 1) {
byte val = SCOPED_MEMORY_ACCESS.getByte(segment.sessionImpl(), segment.unsafeGetBase(), segment.unsafeGetOffset() + fromOffset);
result = result * POWERS_OF_31[0]
+ val;
}
return result;
}
@ForceInline
public static long mismatch(AbstractMemorySegmentImpl src, long srcFromOffset, long srcToOffset,
AbstractMemorySegmentImpl dst, long dstFromOffset, long dstToOffset) {