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:
Jorn Vernee 2023-10-13 19:05:47 +00:00
parent 605c976729
commit b12c471a99
22 changed files with 1432 additions and 115 deletions

View file

@ -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

View file

@ -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);
}
});
}
}

View file

@ -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);
}
/**

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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.

View file

@ -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");
}
}

View file

@ -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);
}

View file

@ -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

View file

@ -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));
}
}

View file

@ -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.