mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8317837: Leftover FFM implementation-only changes
Co-authored-by: Maurizio Cimadamore <mcimadamore@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Reviewed-by: mcimadamore
This commit is contained in:
parent
605c976729
commit
b12c471a99
22 changed files with 1432 additions and 115 deletions
|
@ -28,6 +28,8 @@ package java.lang;
|
|||
import java.io.ObjectStreamField;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.annotation.Native;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.constant.Constable;
|
||||
import java.lang.constant.ConstantDesc;
|
||||
|
@ -1836,6 +1838,21 @@ public final class String
|
|||
return encode(Charset.defaultCharset(), coder(), value);
|
||||
}
|
||||
|
||||
boolean bytesCompatible(Charset charset) {
|
||||
if (isLatin1()) {
|
||||
if (charset == ISO_8859_1.INSTANCE) {
|
||||
return true; // ok, same encoding
|
||||
} else if (charset == UTF_8.INSTANCE || charset == US_ASCII.INSTANCE) {
|
||||
return !StringCoding.hasNegatives(value, 0, value.length); // ok, if ASCII-compatible
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void copyToSegmentRaw(MemorySegment segment, long offset) {
|
||||
MemorySegment.copy(value, 0, segment, ValueLayout.JAVA_BYTE, offset, value.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this string to the specified object. The result is {@code
|
||||
* true} if and only if the argument is not {@code null} and is a {@code
|
||||
|
|
|
@ -35,6 +35,7 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.StringConcatFactory;
|
||||
|
@ -88,7 +89,6 @@ import jdk.internal.vm.Continuation;
|
|||
import jdk.internal.vm.ContinuationScope;
|
||||
import jdk.internal.vm.StackableScope;
|
||||
import jdk.internal.vm.ThreadContainer;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.IntrinsicCandidate;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
import sun.nio.fs.DefaultFileSystemProvider;
|
||||
|
@ -2669,6 +2669,16 @@ public final class System {
|
|||
public String getLoaderNameID(ClassLoader loader) {
|
||||
return loader.nameAndId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
string.copyToSegmentRaw(segment, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bytesCompatible(String string, Charset charset) {
|
||||
return string.bytesCompatible(charset);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1076,7 +1076,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
|||
* such that {@code isAccessibleBy(T) == false}.
|
||||
*/
|
||||
default String getString(long offset) {
|
||||
return getString(offset, StandardCharsets.UTF_8);
|
||||
return getString(offset, sun.nio.cs.UTF_8.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1132,7 +1132,7 @@ public sealed interface MemorySegment permits AbstractMemorySegmentImpl {
|
|||
*/
|
||||
default void setString(long offset, String str) {
|
||||
Objects.requireNonNull(str);
|
||||
setString(offset, str, StandardCharsets.UTF_8);
|
||||
setString(offset, str, sun.nio.cs.UTF_8.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -89,7 +89,7 @@ public interface SegmentAllocator {
|
|||
@ForceInline
|
||||
default MemorySegment allocateFrom(String str) {
|
||||
Objects.requireNonNull(str);
|
||||
return allocateFrom(str, StandardCharsets.UTF_8);
|
||||
return allocateFrom(str, sun.nio.cs.UTF_8.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -124,11 +124,20 @@ public interface SegmentAllocator {
|
|||
Objects.requireNonNull(charset);
|
||||
Objects.requireNonNull(str);
|
||||
int termCharSize = StringSupport.CharsetKind.of(charset).terminatorCharSize();
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
MemorySegment segment = allocateNoInit(bytes.length + termCharSize);
|
||||
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
|
||||
MemorySegment segment;
|
||||
int length;
|
||||
if (StringSupport.bytesCompatible(str, charset)) {
|
||||
length = str.length();
|
||||
segment = allocateNoInit((long) length + termCharSize);
|
||||
StringSupport.copyToSegmentRaw(str, segment, 0);
|
||||
} else {
|
||||
byte[] bytes = str.getBytes(charset);
|
||||
length = bytes.length;
|
||||
segment = allocateNoInit((long) bytes.length + termCharSize);
|
||||
MemorySegment.copy(bytes, 0, segment, ValueLayout.JAVA_BYTE, 0, bytes.length);
|
||||
}
|
||||
for (int i = 0 ; i < termCharSize ; i++) {
|
||||
segment.set(ValueLayout.JAVA_BYTE, bytes.length + i, (byte)0);
|
||||
segment.set(ValueLayout.JAVA_BYTE, length + i, (byte)0);
|
||||
}
|
||||
return segment;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
/**
|
||||
* Base class for memory segment var handle view implementations.
|
||||
*/
|
||||
|
@ -54,7 +56,7 @@ abstract sealed class VarHandleSegmentViewBase extends VarHandle permits
|
|||
}
|
||||
|
||||
static IllegalArgumentException newIllegalArgumentExceptionForMisalignedAccess(long address) {
|
||||
return new IllegalArgumentException("Misaligned access at address: " + address);
|
||||
return new IllegalArgumentException("Misaligned access at address: " + Utils.toHexString(address));
|
||||
}
|
||||
|
||||
static UnsupportedOperationException newUnsupportedAccessModeForAlignment(long alignment) {
|
||||
|
|
|
@ -27,6 +27,7 @@ package jdk.internal.access;
|
|||
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
|
@ -574,4 +575,14 @@ public interface JavaLangAccess {
|
|||
* explicitly set otherwise <qualified-class-name> @<id>
|
||||
*/
|
||||
String getLoaderNameID(ClassLoader loader);
|
||||
|
||||
/**
|
||||
* Copy the string bytes to an existing segment, avoiding intermediate copies.
|
||||
*/
|
||||
void copyToSegmentRaw(String string, MemorySegment segment, long offset);
|
||||
|
||||
/**
|
||||
* Are the string bytes compatible with the given charset?
|
||||
*/
|
||||
boolean bytesCompatible(String string, Charset charset);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import java.lang.foreign.AddressLayout;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
|
@ -40,9 +41,13 @@ import java.lang.invoke.MethodType;
|
|||
import java.lang.invoke.VarHandle;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
/**
|
||||
* This class provide support for constructing layout paths; that is, starting from a root path (see {@link #rootPath(MemoryLayout)},
|
||||
|
@ -89,7 +94,6 @@ public class LayoutPath {
|
|||
private final long offset;
|
||||
private final LayoutPath enclosing;
|
||||
private final long[] strides;
|
||||
|
||||
private final long[] bounds;
|
||||
private final MethodHandle[] derefAdapters;
|
||||
|
||||
|
@ -105,15 +109,13 @@ public class LayoutPath {
|
|||
// Layout path selector methods
|
||||
|
||||
public LayoutPath sequenceElement() {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
SequenceLayout seq = requireSequenceLayout();
|
||||
MemoryLayout elem = seq.elementLayout();
|
||||
return LayoutPath.nestedPath(elem, offset, addStride(elem.byteSize()), addBound(seq.elementCount()), derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long start, long step) {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
SequenceLayout seq = requireSequenceLayout();
|
||||
checkSequenceBounds(seq, start);
|
||||
MemoryLayout elem = seq.elementLayout();
|
||||
long elemSize = elem.byteSize();
|
||||
|
@ -122,21 +124,19 @@ public class LayoutPath {
|
|||
start + 1;
|
||||
long maxIndex = Math.ceilDiv(nelems, Math.abs(step));
|
||||
return LayoutPath.nestedPath(elem, offset + (start * elemSize),
|
||||
addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
|
||||
addStride(elemSize * step), addBound(maxIndex), derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath sequenceElement(long index) {
|
||||
check(SequenceLayout.class, "attempting to select a sequence element from a non-sequence layout");
|
||||
SequenceLayout seq = (SequenceLayout)layout;
|
||||
SequenceLayout seq = requireSequenceLayout();
|
||||
checkSequenceBounds(seq, index);
|
||||
long elemSize = seq.elementLayout().byteSize();
|
||||
long elemOffset = elemSize * index;
|
||||
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters,this);
|
||||
return LayoutPath.nestedPath(seq.elementLayout(), offset + elemOffset, strides, bounds, derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath groupElement(String name) {
|
||||
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
|
||||
GroupLayout g = (GroupLayout)layout;
|
||||
GroupLayout g = requireGroupLayout();
|
||||
long offset = 0;
|
||||
MemoryLayout elem = null;
|
||||
for (int i = 0; i < g.memberLayouts().size(); i++) {
|
||||
|
@ -150,20 +150,21 @@ public class LayoutPath {
|
|||
}
|
||||
}
|
||||
if (elem == null) {
|
||||
throw badLayoutPath("cannot resolve '" + name + "' in layout " + layout);
|
||||
throw badLayoutPath(
|
||||
String.format("cannot resolve '%s' in layout %s", name, breadcrumbs()));
|
||||
}
|
||||
return LayoutPath.nestedPath(elem, this.offset + offset, strides, bounds, derefAdapters, this);
|
||||
}
|
||||
|
||||
public LayoutPath groupElement(long index) {
|
||||
check(GroupLayout.class, "attempting to select a group element from a non-group layout");
|
||||
GroupLayout g = (GroupLayout)layout;
|
||||
GroupLayout g = requireGroupLayout();
|
||||
long elemSize = g.memberLayouts().size();
|
||||
long offset = 0;
|
||||
MemoryLayout elem = null;
|
||||
for (int i = 0; i <= index; i++) {
|
||||
if (i == elemSize) {
|
||||
throw badLayoutPath("cannot resolve element " + index + " in layout " + layout);
|
||||
throw badLayoutPath(
|
||||
String.format("cannot resolve element %d in layout: %s", index, breadcrumbs()));
|
||||
}
|
||||
elem = g.memberLayouts().get(i);
|
||||
if (g instanceof StructLayout && i < index) {
|
||||
|
@ -176,7 +177,8 @@ public class LayoutPath {
|
|||
public LayoutPath derefElement() {
|
||||
if (!(layout instanceof AddressLayout addressLayout) ||
|
||||
addressLayout.targetLayout().isEmpty()) {
|
||||
throw badLayoutPath("Cannot dereference layout: " + layout);
|
||||
throw badLayoutPath(
|
||||
String.format("Cannot dereference layout: %s", breadcrumbs()));
|
||||
}
|
||||
MemoryLayout derefLayout = addressLayout.targetLayout().get();
|
||||
MethodHandle handle = dereferenceHandle(false).toMethodHandle(VarHandle.AccessMode.GET);
|
||||
|
@ -201,7 +203,8 @@ public class LayoutPath {
|
|||
|
||||
public VarHandle dereferenceHandle(boolean adapt) {
|
||||
if (!(layout instanceof ValueLayout valueLayout)) {
|
||||
throw new IllegalArgumentException("Path does not select a value layout");
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Path does not select a value layout: %s", breadcrumbs()));
|
||||
}
|
||||
|
||||
// If we have an enclosing layout, drop the alignment check for the accessed element,
|
||||
|
@ -288,7 +291,9 @@ public class LayoutPath {
|
|||
|
||||
private static void checkAlign(MemorySegment segment, long offset, MemoryLayout constraint) {
|
||||
if (!((AbstractMemorySegmentImpl) segment).isAlignedForElement(offset, constraint)) {
|
||||
throw new IllegalArgumentException("Target offset incompatible with alignment constraints: " + constraint.byteAlignment());
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"Target offset %d is incompatible with alignment constraint %d (of %s) for segment %s"
|
||||
, offset, constraint.byteAlignment(), constraint, segment));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,15 +319,27 @@ public class LayoutPath {
|
|||
|
||||
// Helper methods
|
||||
|
||||
private void check(Class<?> layoutClass, String msg) {
|
||||
private SequenceLayout requireSequenceLayout() {
|
||||
return requireLayoutType(SequenceLayout.class, "sequence");
|
||||
}
|
||||
|
||||
private GroupLayout requireGroupLayout() {
|
||||
return requireLayoutType(GroupLayout.class, "group");
|
||||
}
|
||||
|
||||
private <T extends MemoryLayout> T requireLayoutType(Class<T> layoutClass, String name) {
|
||||
if (!layoutClass.isAssignableFrom(layout.getClass())) {
|
||||
throw badLayoutPath(msg);
|
||||
throw badLayoutPath(
|
||||
String.format("attempting to select a %s element from a non-%s layout: %s",
|
||||
name, name, breadcrumbs()));
|
||||
}
|
||||
return layoutClass.cast(layout);
|
||||
}
|
||||
|
||||
private void checkSequenceBounds(SequenceLayout seq, long index) {
|
||||
if (index >= seq.elementCount()) {
|
||||
throw badLayoutPath(String.format("Sequence index out of bound; found: %d, size: %d", index, seq.elementCount()));
|
||||
throw badLayoutPath(String.format("sequence index out of bounds; index: %d, elementCount is %d for layout %s",
|
||||
index, seq.elementCount(), breadcrumbs()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,6 +359,13 @@ public class LayoutPath {
|
|||
return newBounds;
|
||||
}
|
||||
|
||||
private String breadcrumbs() {
|
||||
return Stream.iterate(this, Objects::nonNull, lp -> lp.enclosing)
|
||||
.map(LayoutPath::layout)
|
||||
.map(Object::toString)
|
||||
.collect(joining(", selected from: "));
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides an immutable implementation for the {@code PathElement} interface. A path element implementation
|
||||
* is simply a pointer to one of the selector methods provided by the {@code LayoutPath} class.
|
||||
|
|
|
@ -25,103 +25,250 @@
|
|||
|
||||
package jdk.internal.foreign;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
import static java.lang.foreign.ValueLayout.*;
|
||||
|
||||
/**
|
||||
* Miscellaneous functions to read and write strings, in various charsets.
|
||||
*/
|
||||
public class StringSupport {
|
||||
|
||||
static final JavaLangAccess JAVA_LANG_ACCESS = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
private StringSupport() {}
|
||||
|
||||
public static String read(MemorySegment segment, long offset, Charset charset) {
|
||||
return switch (CharsetKind.of(charset)) {
|
||||
case SINGLE_BYTE -> readFast_byte(segment, offset, charset);
|
||||
case DOUBLE_BYTE -> readFast_short(segment, offset, charset);
|
||||
case QUAD_BYTE -> readFast_int(segment, offset, charset);
|
||||
case SINGLE_BYTE -> readByte(segment, offset, charset);
|
||||
case DOUBLE_BYTE -> readShort(segment, offset, charset);
|
||||
case QUAD_BYTE -> readInt(segment, offset, charset);
|
||||
};
|
||||
}
|
||||
|
||||
public static void write(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
switch (CharsetKind.of(charset)) {
|
||||
case SINGLE_BYTE -> writeFast_byte(segment, offset, charset, string);
|
||||
case DOUBLE_BYTE -> writeFast_short(segment, offset, charset, string);
|
||||
case QUAD_BYTE -> writeFast_int(segment, offset, charset, string);
|
||||
case SINGLE_BYTE -> writeByte(segment, offset, charset, string);
|
||||
case DOUBLE_BYTE -> writeShort(segment, offset, charset, string);
|
||||
case QUAD_BYTE -> writeInt(segment, offset, charset, string);
|
||||
}
|
||||
}
|
||||
private static String readFast_byte(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = strlen_byte(segment, offset);
|
||||
|
||||
private static String readByte(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = chunkedStrlenByte(segment, offset);
|
||||
byte[] bytes = new byte[(int)len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len);
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
|
||||
private static void writeFast_byte(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
segment.set(JAVA_BYTE, offset + bytes.length, (byte)0);
|
||||
private static void writeByte(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
int bytes = copyBytes(string, segment, charset, offset);
|
||||
segment.set(JAVA_BYTE, offset + bytes, (byte)0);
|
||||
}
|
||||
|
||||
private static String readFast_short(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = strlen_short(segment, offset);
|
||||
private static String readShort(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = chunkedStrlenShort(segment, offset);
|
||||
byte[] bytes = new byte[(int)len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len);
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
|
||||
private static void writeFast_short(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
segment.set(JAVA_SHORT, offset + bytes.length, (short)0);
|
||||
private static void writeShort(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
int bytes = copyBytes(string, segment, charset, offset);
|
||||
segment.set(JAVA_SHORT, offset + bytes, (short)0);
|
||||
}
|
||||
|
||||
private static String readFast_int(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = strlen_int(segment, offset);
|
||||
private static String readInt(MemorySegment segment, long offset, Charset charset) {
|
||||
long len = strlenInt(segment, offset);
|
||||
byte[] bytes = new byte[(int)len];
|
||||
MemorySegment.copy(segment, JAVA_BYTE, offset, bytes, 0, (int)len);
|
||||
return new String(bytes, charset);
|
||||
}
|
||||
|
||||
private static void writeFast_int(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
segment.set(JAVA_INT, offset + bytes.length, 0);
|
||||
private static void writeInt(MemorySegment segment, long offset, Charset charset, String string) {
|
||||
int bytes = copyBytes(string, segment, charset, offset);
|
||||
segment.set(JAVA_INT, offset + bytes, 0);
|
||||
}
|
||||
|
||||
private static int strlen_byte(MemorySegment segment, long start) {
|
||||
// iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
|
||||
for (int offset = 0; offset >= 0; offset++) {
|
||||
/**
|
||||
* {@return the shortest distance beginning at the provided {@code start}
|
||||
* to the encountering of a zero byte in the provided {@code segment}}
|
||||
* <p>
|
||||
* The method divides the region of interest into three distinct regions:
|
||||
* <ul>
|
||||
* <li>head (access made on a byte-by-byte basis) (if any)</li>
|
||||
* <li>body (access made with eight bytes at a time at physically 64-bit-aligned memory) (if any)</li>
|
||||
* <li>tail (access made on a byte-by-byte basis) (if any)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The body is using a heuristic method to determine if a long word
|
||||
* contains a zero byte. The method might have false positives but
|
||||
* never false negatives.
|
||||
* <p>
|
||||
* This method is inspired by the `glibc/string/strlen.c` implementation
|
||||
*
|
||||
* @param segment to examine
|
||||
* @param start from where examination shall begin
|
||||
* @throws IllegalArgumentException if the examined region contains no zero bytes
|
||||
* within a length that can be accepted by a String
|
||||
*/
|
||||
public static int chunkedStrlenByte(MemorySegment segment, long start) {
|
||||
|
||||
// Handle the first unaligned "head" bytes separately
|
||||
int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES);
|
||||
|
||||
int offset = 0;
|
||||
for (; offset < headCount; offset++) {
|
||||
byte curr = segment.get(JAVA_BYTE, start + offset);
|
||||
if (curr == 0) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("String too large");
|
||||
|
||||
// We are now on a long-aligned boundary so this is the "body"
|
||||
int bodyCount = bodyCount(segment.byteSize() - start - headCount);
|
||||
|
||||
for (; offset < bodyCount; offset += Long.BYTES) {
|
||||
// We know we are `long` aligned so, we can save on alignment checking here
|
||||
long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset);
|
||||
// Is this a candidate?
|
||||
if (mightContainZeroByte(curr)) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (segment.get(JAVA_BYTE, start + offset + j) == 0) {
|
||||
return offset + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the "tail"
|
||||
return requireWithinArraySize((long) offset + strlenByte(segment, start + offset));
|
||||
}
|
||||
|
||||
private static int strlen_short(MemorySegment segment, long start) {
|
||||
// iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
|
||||
for (int offset = 0; offset >= 0; offset += 2) {
|
||||
/* Bits 63 and N * 8 (N = 1..7) of this number are zero. Call these bits
|
||||
the "holes". Note that there is a hole just to the left of
|
||||
each byte, with an extra at the end:
|
||||
|
||||
bits: 01111110 11111110 11111110 11111110 11111110 11111110 11111110 11111111
|
||||
bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE FFFFFFFF GGGGGGGG HHHHHHHH
|
||||
|
||||
The 1-bits make sure that carries propagate to the next 0-bit.
|
||||
The 0-bits provide holes for carries to fall into.
|
||||
*/
|
||||
private static final long HIMAGIC_FOR_BYTES = 0x8080_8080_8080_8080L;
|
||||
private static final long LOMAGIC_FOR_BYTES = 0x0101_0101_0101_0101L;
|
||||
|
||||
static boolean mightContainZeroByte(long l) {
|
||||
return ((l - LOMAGIC_FOR_BYTES) & (~l) & HIMAGIC_FOR_BYTES) != 0;
|
||||
}
|
||||
|
||||
private static final long HIMAGIC_FOR_SHORTS = 0x8000_8000_8000_8000L;
|
||||
private static final long LOMAGIC_FOR_SHORTS = 0x0001_0001_0001_0001L;
|
||||
|
||||
static boolean mightContainZeroShort(long l) {
|
||||
return ((l - LOMAGIC_FOR_SHORTS) & (~l) & HIMAGIC_FOR_SHORTS) != 0;
|
||||
}
|
||||
|
||||
static int requireWithinArraySize(long size) {
|
||||
if (size > ArraysSupport.SOFT_MAX_ARRAY_LENGTH) {
|
||||
throw newIaeStringTooLarge();
|
||||
}
|
||||
return (int) size;
|
||||
}
|
||||
|
||||
static int bodyCount(long remaining) {
|
||||
return (int) Math.min(
|
||||
// Make sure we do not wrap around
|
||||
Integer.MAX_VALUE - Long.BYTES,
|
||||
// Remaining bytes to consider
|
||||
remaining)
|
||||
& -Long.BYTES; // Mask 0xFFFFFFF8
|
||||
}
|
||||
|
||||
private static int strlenByte(MemorySegment segment, long start) {
|
||||
for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += 1) {
|
||||
byte curr = segment.get(JAVA_BYTE, start + offset);
|
||||
if (curr == 0) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
throw newIaeStringTooLarge();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the shortest distance beginning at the provided {@code start}
|
||||
* to the encountering of a zero short in the provided {@code segment}}
|
||||
* <p>
|
||||
* Note: The inspected region must be short aligned.
|
||||
*
|
||||
* @see #chunkedStrlenByte(MemorySegment, long) for more information
|
||||
*
|
||||
* @param segment to examine
|
||||
* @param start from where examination shall begin
|
||||
* @throws IllegalArgumentException if the examined region contains no zero shorts
|
||||
* within a length that can be accepted by a String
|
||||
*/
|
||||
public static int chunkedStrlenShort(MemorySegment segment, long start) {
|
||||
|
||||
// Handle the first unaligned "head" bytes separately
|
||||
int headCount = (int)SharedUtils.remainsToAlignment(segment.address() + start, Long.BYTES);
|
||||
|
||||
int offset = 0;
|
||||
for (; offset < headCount; offset += Short.BYTES) {
|
||||
short curr = segment.get(JAVA_SHORT, start + offset);
|
||||
if (curr == 0) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("String too large");
|
||||
|
||||
// We are now on a long-aligned boundary so this is the "body"
|
||||
int bodyCount = bodyCount(segment.byteSize() - start - headCount);
|
||||
|
||||
for (; offset < bodyCount; offset += Long.BYTES) {
|
||||
// We know we are `long` aligned so, we can save on alignment checking here
|
||||
long curr = segment.get(JAVA_LONG_UNALIGNED, start + offset);
|
||||
// Is this a candidate?
|
||||
if (mightContainZeroShort(curr)) {
|
||||
for (int j = 0; j < Long.BYTES; j += Short.BYTES) {
|
||||
if (segment.get(JAVA_SHORT_UNALIGNED, start + offset + j) == 0) {
|
||||
return offset + j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the "tail"
|
||||
return requireWithinArraySize((long) offset + strlenShort(segment, start + offset));
|
||||
}
|
||||
|
||||
private static int strlen_int(MemorySegment segment, long start) {
|
||||
// iterate until overflow (String can only hold a byte[], whose length can be expressed as an int)
|
||||
for (int offset = 0; offset >= 0; offset += 4) {
|
||||
int curr = segment.get(JAVA_INT, start + offset);
|
||||
private static int strlenShort(MemorySegment segment, long start) {
|
||||
for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Short.BYTES) {
|
||||
short curr = segment.get(JAVA_SHORT_UNALIGNED, start + offset);
|
||||
if (curr == (short)0) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
throw newIaeStringTooLarge();
|
||||
}
|
||||
|
||||
// The gain of using `long` wide operations for `int` is lower than for the two other `byte` and `short` variants
|
||||
// so, there is only one method for ints.
|
||||
public static int strlenInt(MemorySegment segment, long start) {
|
||||
for (int offset = 0; offset < ArraysSupport.SOFT_MAX_ARRAY_LENGTH; offset += Integer.BYTES) {
|
||||
// We are guaranteed to be aligned here so, we can use unaligned access.
|
||||
int curr = segment.get(JAVA_INT_UNALIGNED, start + offset);
|
||||
if (curr == 0) {
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("String too large");
|
||||
throw newIaeStringTooLarge();
|
||||
}
|
||||
|
||||
public enum CharsetKind {
|
||||
|
@ -140,15 +287,46 @@ public class StringSupport {
|
|||
}
|
||||
|
||||
public static CharsetKind of(Charset charset) {
|
||||
if (charset == StandardCharsets.UTF_8 || charset == StandardCharsets.ISO_8859_1 || charset == StandardCharsets.US_ASCII) {
|
||||
return CharsetKind.SINGLE_BYTE;
|
||||
} else if (charset == StandardCharsets.UTF_16LE || charset == StandardCharsets.UTF_16BE || charset == StandardCharsets.UTF_16) {
|
||||
return CharsetKind.DOUBLE_BYTE;
|
||||
} else if (charset == StandardCharsets.UTF_32LE || charset == StandardCharsets.UTF_32BE || charset == StandardCharsets.UTF_32) {
|
||||
return CharsetKind.QUAD_BYTE;
|
||||
// Comparing the charset to specific internal implementations avoids loading the class `StandardCharsets`
|
||||
if (charset == sun.nio.cs.UTF_8.INSTANCE ||
|
||||
charset == sun.nio.cs.ISO_8859_1.INSTANCE ||
|
||||
charset == sun.nio.cs.US_ASCII.INSTANCE) {
|
||||
return SINGLE_BYTE;
|
||||
} else if (charset instanceof sun.nio.cs.UTF_16LE ||
|
||||
charset instanceof sun.nio.cs.UTF_16BE ||
|
||||
charset instanceof sun.nio.cs.UTF_16) {
|
||||
return DOUBLE_BYTE;
|
||||
} else if (charset instanceof sun.nio.cs.UTF_32LE ||
|
||||
charset instanceof sun.nio.cs.UTF_32BE ||
|
||||
charset instanceof sun.nio.cs.UTF_32) {
|
||||
return QUAD_BYTE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported charset: " + charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean bytesCompatible(String string, Charset charset) {
|
||||
return JAVA_LANG_ACCESS.bytesCompatible(string, charset);
|
||||
}
|
||||
|
||||
public static int copyBytes(String string, MemorySegment segment, Charset charset, long offset) {
|
||||
if (bytesCompatible(string, charset)) {
|
||||
copyToSegmentRaw(string, segment, offset);
|
||||
return string.length();
|
||||
} else {
|
||||
byte[] bytes = string.getBytes(charset);
|
||||
MemorySegment.copy(bytes, 0, segment, JAVA_BYTE, offset, bytes.length);
|
||||
return bytes.length;
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyToSegmentRaw(String string, MemorySegment segment, long offset) {
|
||||
JAVA_LANG_ACCESS.copyToSegmentRaw(string, segment, offset);
|
||||
}
|
||||
|
||||
private static IllegalArgumentException newIaeStringTooLarge() {
|
||||
return new IllegalArgumentException("String too large");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -146,7 +146,7 @@ public final class Utils {
|
|||
@ForceInline
|
||||
public static MemorySegment longToAddress(long addr, long size, long align) {
|
||||
if (!isAligned(addr, align)) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
|
||||
}
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size);
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ public final class Utils {
|
|||
@ForceInline
|
||||
public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) {
|
||||
if (!isAligned(addr, align)) {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr);
|
||||
throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
|
||||
}
|
||||
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope);
|
||||
}
|
||||
|
|
|
@ -54,10 +54,8 @@ import java.lang.foreign.ValueLayout;
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.lang.ref.Reference;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
@ -129,6 +127,10 @@ public final class SharedUtils {
|
|||
return ((addr - 1) | (alignment - 1)) + 1;
|
||||
}
|
||||
|
||||
public static long remainsToAlignment(long addr, long alignment) {
|
||||
return alignUp(addr, alignment) - addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a MethodHandle that takes an input buffer as a first argument (a MemorySegment), and returns nothing,
|
||||
* and adapts it to return a MemorySegment, by allocating a MemorySegment for the input
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.lang.foreign.MemorySegment;
|
|||
import java.lang.foreign.Arena;
|
||||
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
public final class UpcallStubs {
|
||||
|
||||
|
@ -36,7 +37,7 @@ public final class UpcallStubs {
|
|||
|
||||
private static void freeUpcallStub(long stubAddress) {
|
||||
if (!freeUpcallStub0(stubAddress)) {
|
||||
throw new IllegalStateException("Not a stub address: " + stubAddress);
|
||||
throw new IllegalStateException("Not a stub address: " + Utils.toHexString(stubAddress));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -372,7 +372,7 @@ public final class ValueLayouts {
|
|||
* <li>{@link ValueLayout.OfFloat}, for {@code float.class}</li>
|
||||
* <li>{@link ValueLayout.OfLong}, for {@code long.class}</li>
|
||||
* <li>{@link ValueLayout.OfDouble}, for {@code double.class}</li>
|
||||
* <li>{@link ValueLayout.OfAddress}, for {@code MemorySegment.class}</li>
|
||||
* <li>{@link AddressLayout}, for {@code MemorySegment.class}</li>
|
||||
* </ul>
|
||||
* @param carrier the value layout carrier.
|
||||
* @param order the value layout's byte order.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue