/* * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package jdk.internal.foreign; import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; import java.lang.foreign.SegmentAllocator; import java.lang.foreign.StructLayout; 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.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jdk.internal.access.SharedSecrets; import jdk.internal.foreign.abi.SharedUtils; import jdk.internal.vm.annotation.ForceInline; import sun.invoke.util.Wrapper; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static sun.security.action.GetPropertyAction.privilegedGetProperty; /** * This class contains misc helper functions to support creation of memory segments. */ public final class Utils { public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows"); // Suppresses default constructor, ensuring non-instantiability. private Utils() {} private static final MethodHandle BYTE_TO_BOOL; private static final MethodHandle BOOL_TO_BYTE; private static final MethodHandle ADDRESS_TO_LONG; private static final MethodHandle LONG_TO_ADDRESS; public static final MethodHandle BITS_TO_BYTES; static { try { MethodHandles.Lookup lookup = MethodHandles.lookup(); BYTE_TO_BOOL = lookup.findStatic(Utils.class, "byteToBoolean", MethodType.methodType(boolean.class, byte.class)); BOOL_TO_BYTE = lookup.findStatic(Utils.class, "booleanToByte", MethodType.methodType(byte.class, boolean.class)); ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment", MethodType.methodType(long.class, MemorySegment.class)); LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress", MethodType.methodType(MemorySegment.class, long.class, long.class, long.class)); BITS_TO_BYTES = lookup.findStatic(Utils.class, "bitsToBytes", MethodType.methodType(long.class, long.class)); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } } public static long alignUp(long n, long alignment) { return (n + alignment - 1) & -alignment; } public static MemorySegment alignUp(MemorySegment ms, long alignment) { long offset = ms.address(); return ms.asSlice(alignUp(offset, alignment) - offset); } public static long bitsToBytes(long bits) { assert Utils.isAligned(bits, 8); return bits / Byte.SIZE; } public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) { final class VarHandleCache { private static final Map HANDLE_MAP = new ConcurrentHashMap<>(); static VarHandle put(ValueLayout layout, VarHandle handle) { VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle); return prev != null ? prev : handle; } } Class baseCarrier = layout.carrier(); if (layout.carrier() == MemorySegment.class) { baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { case Long.BYTES -> long.class; case Integer.BYTES -> int.class; default -> throw new UnsupportedOperationException("Unsupported address layout"); }; } else if (layout.carrier() == boolean.class) { baseCarrier = byte.class; } VarHandle handle = SharedSecrets.getJavaLangInvokeAccess().memorySegmentViewHandle(baseCarrier, layout.byteAlignment() - 1, layout.order()); if (layout.carrier() == boolean.class) { handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL); } else if (layout instanceof AddressLayout addressLayout) { handle = MethodHandles.filterValue(handle, MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)), MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1, pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)), MethodType.methodType(MemorySegment.class, baseCarrier))); } return VarHandleCache.put(layout, handle); } public static boolean byteToBoolean(byte b) { return b != 0; } private static byte booleanToByte(boolean b) { return b ? (byte)1 : (byte)0; } @ForceInline public static MemorySegment longToAddress(long addr, long size, long align) { if (!isAligned(addr, align)) { throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); } return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size); } @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); } return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope); } public static void copy(MemorySegment addr, byte[] bytes) { var heapSegment = MemorySegment.ofArray(bytes); addr.copyFrom(heapSegment); addr.set(JAVA_BYTE, bytes.length, (byte)0); } public static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) { MemorySegment addr = allocator.allocate(bytes.length + 1); copy(addr, bytes); return addr; } @ForceInline public static boolean isAligned(long offset, long align) { return (offset & (align - 1)) == 0; } @ForceInline public static void checkElementAlignment(ValueLayout layout, String msg) { // Fast-path: if both size and alignment are powers of two, we can just // check if one is greater than the other. assert isPowerOfTwo(layout.bitSize()); if (layout.byteAlignment() > layout.byteSize()) { throw new IllegalArgumentException(msg); } } @ForceInline public static void checkElementAlignment(MemoryLayout layout, String msg) { if (layout.byteSize() % layout.byteAlignment() != 0) { throw new IllegalArgumentException(msg); } } public static long pointeeByteSize(AddressLayout addressLayout) { return addressLayout.targetLayout() .map(MemoryLayout::byteSize) .orElse(0L); } public static long pointeeByteAlign(AddressLayout addressLayout) { return addressLayout.targetLayout() .map(MemoryLayout::byteAlignment) .orElse(1L); } public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) { // size should be >= 0 if (byteSize < 0) { throw new IllegalArgumentException("Invalid allocation size : " + byteSize); } // alignment should be > 0, and power of two if (byteAlignment <= 0 || ((byteAlignment & (byteAlignment - 1)) != 0L)) { throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment); } } private static long computePadding(long offset, long align) { boolean isAligned = offset == 0 || offset % align == 0; if (isAligned) { return 0; } else { long gap = offset % align; return align - gap; } } /** * {@return return a struct layout constructed from the given elements, with padding * computed automatically so that they are naturally aligned}. * * @param elements the structs' fields */ public static StructLayout computePaddedStructLayout(MemoryLayout... elements) { long offset = 0L; List layouts = new ArrayList<>(); long align = 0; for (MemoryLayout l : elements) { long padding = computePadding(offset, l.bitAlignment()); if (padding != 0) { layouts.add(MemoryLayout.paddingLayout(padding)); offset += padding; } layouts.add(l); align = Math.max(align, l.bitAlignment()); offset += l.bitSize(); } long padding = computePadding(offset, align); if (padding != 0) { layouts.add(MemoryLayout.paddingLayout(padding)); } return MemoryLayout.structLayout(layouts.toArray(MemoryLayout[]::new)); } public static int byteWidthOfPrimitive(Class primitive) { return Wrapper.forPrimitiveType(primitive).bitWidth() / 8; } public static boolean isPowerOfTwo(long value) { return (value & (value - 1)) == 0L; } }