1 /* 2 * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 * 25 */ 26 27 package jdk.internal.foreign; 28 29 import java.lang.foreign.AddressLayout; 30 import java.lang.foreign.MemoryLayout; 31 import java.lang.foreign.MemorySegment; 32 import java.lang.foreign.SegmentAllocator; 33 import java.lang.foreign.StructLayout; 34 import java.lang.foreign.ValueLayout; 35 import java.lang.invoke.MethodHandle; 36 import java.lang.invoke.MethodHandles; 37 import java.lang.invoke.MethodType; 38 import java.lang.invoke.VarHandle; 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.concurrent.ConcurrentHashMap; 43 import java.util.function.Supplier; 44 45 import jdk.internal.access.SharedSecrets; 46 import jdk.internal.foreign.abi.SharedUtils; 47 import jdk.internal.vm.annotation.ForceInline; 48 import sun.invoke.util.Wrapper; 49 50 import static java.lang.foreign.ValueLayout.JAVA_BYTE; 51 import static sun.security.action.GetPropertyAction.privilegedGetProperty; 52 53 /** 54 * This class contains misc helper functions to support creation of memory segments. 55 */ 56 public final class Utils { 57 58 public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows"); 59 60 // Suppresses default constructor, ensuring non-instantiability. 61 private Utils() {} 62 63 private static final MethodHandle BYTE_TO_BOOL; 64 private static final MethodHandle BOOL_TO_BYTE; 65 private static final MethodHandle ADDRESS_TO_LONG; 66 private static final MethodHandle LONG_TO_ADDRESS; 67 68 static { 69 try { 70 MethodHandles.Lookup lookup = MethodHandles.lookup(); 71 BYTE_TO_BOOL = lookup.findStatic(Utils.class, "byteToBoolean", 72 MethodType.methodType(boolean.class, byte.class)); 73 BOOL_TO_BYTE = lookup.findStatic(Utils.class, "booleanToByte", 74 MethodType.methodType(byte.class, boolean.class)); 75 ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment", 76 MethodType.methodType(long.class, MemorySegment.class)); 77 LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress", 78 MethodType.methodType(MemorySegment.class, long.class, long.class, long.class)); 79 } catch (Throwable ex) { 80 throw new ExceptionInInitializerError(ex); 81 } 82 } 83 84 public static long alignUp(long n, long alignment) { 85 return (n + alignment - 1) & -alignment; 86 } 87 88 public static MemorySegment alignUp(MemorySegment ms, long alignment) { 89 long offset = ms.address(); 90 return ms.asSlice(alignUp(offset, alignment) - offset); 91 } 92 93 public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) { 94 final class VarHandleCache { 95 private static final Map<ValueLayout, VarHandle> HANDLE_MAP = new ConcurrentHashMap<>(); 96 97 static VarHandle put(ValueLayout layout, VarHandle handle) { 98 VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle); 99 return prev != null ? prev : handle; 100 } 101 } 102 Class<?> baseCarrier = layout.carrier(); 103 if (layout.carrier() == MemorySegment.class) { 104 baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) { 105 case Long.BYTES -> long.class; 106 case Integer.BYTES -> int.class; 107 default -> throw new UnsupportedOperationException("Unsupported address layout"); 108 }; 109 } else if (layout.carrier() == boolean.class) { 110 baseCarrier = byte.class; 111 } 112 113 VarHandle handle = SharedSecrets.getJavaLangInvokeAccess().memorySegmentViewHandle(baseCarrier, 114 layout.byteAlignment() - 1, layout.order()); 115 116 if (layout.carrier() == boolean.class) { 117 handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL); 118 } else if (layout instanceof AddressLayout addressLayout) { 119 handle = MethodHandles.filterValue(handle, 120 MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)), 121 MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1, 122 pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)), 123 MethodType.methodType(MemorySegment.class, baseCarrier))); 124 } 125 return VarHandleCache.put(layout, handle); 126 } 127 128 public static boolean byteToBoolean(byte b) { 129 return b != 0; 130 } 131 132 private static byte booleanToByte(boolean b) { 133 return b ? (byte)1 : (byte)0; 134 } 135 136 @ForceInline 137 public static MemorySegment longToAddress(long addr, long size, long align) { 138 if (!isAligned(addr, align)) { 139 throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); 140 } 141 return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size); 142 } 143 144 @ForceInline 145 public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) { 146 if (!isAligned(addr, align)) { 147 throw new IllegalArgumentException("Invalid alignment constraint for address: " + addr); 148 } 149 return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope); 150 } 151 152 public static void copy(MemorySegment addr, byte[] bytes) { 153 var heapSegment = MemorySegment.ofArray(bytes); 154 addr.copyFrom(heapSegment); 155 addr.set(JAVA_BYTE, bytes.length, (byte)0); 156 } 157 158 public static MemorySegment toCString(byte[] bytes, SegmentAllocator allocator) { 159 MemorySegment addr = allocator.allocate(bytes.length + 1); 160 copy(addr, bytes); 161 return addr; 162 } 163 164 @ForceInline 165 public static boolean isAligned(long offset, long align) { 166 return (offset & (align - 1)) == 0; 167 } 168 169 @ForceInline 170 public static boolean isElementAligned(ValueLayout layout) { 171 // Fast-path: if both size and alignment are powers of two, we can just 172 // check if one is greater than the other. 173 assert isPowerOfTwo(layout.byteSize()); 174 return layout.byteAlignment() <= layout.byteSize(); 175 } 176 177 @ForceInline 178 public static void checkElementAlignment(ValueLayout layout, String msg) { 179 if (!isElementAligned(layout)) { 180 throw new IllegalArgumentException(msg); 181 } 182 } 183 184 @ForceInline 185 public static void checkElementAlignment(MemoryLayout layout, String msg) { 186 if (layout.byteSize() % layout.byteAlignment() != 0) { 187 throw new IllegalArgumentException(msg); 188 } 189 } 190 191 public static long pointeeByteSize(AddressLayout addressLayout) { 192 return addressLayout.targetLayout() 193 .map(MemoryLayout::byteSize) 194 .orElse(0L); 195 } 196 197 public static long pointeeByteAlign(AddressLayout addressLayout) { 198 return addressLayout.targetLayout() 199 .map(MemoryLayout::byteAlignment) 200 .orElse(1L); 201 } 202 203 public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) { 204 // size should be >= 0 205 if (byteSize < 0) { 206 throw new IllegalArgumentException("Invalid allocation size : " + byteSize); 207 } 208 209 checkAlign(byteAlignment); 210 } 211 212 public static void checkAlign(long byteAlignment) { 213 // alignment should be > 0, and power of two 214 if (byteAlignment <= 0 || 215 ((byteAlignment & (byteAlignment - 1)) != 0L)) { 216 throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment); 217 } 218 } 219 220 private static long computePadding(long offset, long align) { 221 boolean isAligned = offset == 0 || offset % align == 0; 222 if (isAligned) { 223 return 0; 224 } else { 225 long gap = offset % align; 226 return align - gap; 227 } 228 } 229 230 /** 231 * {@return return a struct layout constructed from the given elements, with padding 232 * computed automatically so that they are naturally aligned}. 233 * 234 * @param elements the structs' fields 235 */ 236 public static StructLayout computePaddedStructLayout(MemoryLayout... elements) { 237 long offset = 0L; 238 List<MemoryLayout> layouts = new ArrayList<>(); 239 long align = 0; 240 for (MemoryLayout l : elements) { 241 long padding = computePadding(offset, l.byteAlignment()); 242 if (padding != 0) { 243 layouts.add(MemoryLayout.paddingLayout(padding)); 244 offset += padding; 245 } 246 layouts.add(l); 247 align = Math.max(align, l.byteAlignment()); 248 offset += l.byteSize(); 249 } 250 long padding = computePadding(offset, align); 251 if (padding != 0) { 252 layouts.add(MemoryLayout.paddingLayout(padding)); 253 } 254 return MemoryLayout.structLayout(layouts.toArray(MemoryLayout[]::new)); 255 } 256 257 public static int byteWidthOfPrimitive(Class<?> primitive) { 258 return Wrapper.forPrimitiveType(primitive).bitWidth() / 8; 259 } 260 261 public static boolean isPowerOfTwo(long value) { 262 return (value & (value - 1)) == 0L; 263 } 264 265 public static <L extends MemoryLayout> L wrapOverflow(Supplier<L> layoutSupplier) { 266 try { 267 return layoutSupplier.get(); 268 } catch (ArithmeticException ex) { 269 throw new IllegalArgumentException("Layout size exceeds Long.MAX_VALUE"); 270 } 271 } 272 273 public static boolean containsNullChars(String s) { 274 return s.indexOf('\u0000') >= 0; 275 } 276 277 public static String toHexString(long value) { 278 return "0x" + Long.toHexString(value); 279 } 280 281 }