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.StructLayout;
 33 import java.lang.foreign.ValueLayout;
 34 import java.lang.invoke.MethodHandle;
 35 import java.lang.invoke.MethodHandles;
 36 import java.lang.invoke.MethodType;
 37 import java.lang.invoke.VarHandle;
 38 import java.util.ArrayList;
 39 import java.util.List;
 40 import java.util.Map;
 41 import java.util.concurrent.ConcurrentHashMap;
 42 import java.util.function.Supplier;
 43 
 44 import jdk.internal.access.SharedSecrets;
 45 import jdk.internal.foreign.abi.SharedUtils;
 46 import jdk.internal.misc.Unsafe;
 47 import jdk.internal.vm.annotation.ForceInline;
 48 import sun.invoke.util.Wrapper;
 49 
 50 import static sun.security.action.GetPropertyAction.privilegedGetProperty;
 51 
 52 /**
 53  * This class contains misc helper functions to support creation of memory segments.
 54  */
 55 public final class Utils {
 56 
 57     public static final boolean IS_WINDOWS = privilegedGetProperty("os.name").startsWith("Windows");
 58 
 59     // Suppresses default constructor, ensuring non-instantiability.
 60     private Utils() {}
 61 
 62     private static final MethodHandle BYTE_TO_BOOL;
 63     private static final MethodHandle BOOL_TO_BYTE;
 64     private static final MethodHandle ADDRESS_TO_LONG;
 65     private static final MethodHandle LONG_TO_ADDRESS;
 66 
 67     static {
 68         try {
 69             MethodHandles.Lookup lookup = MethodHandles.lookup();
 70             BYTE_TO_BOOL = lookup.findStatic(Utils.class, "byteToBoolean",
 71                     MethodType.methodType(boolean.class, byte.class));
 72             BOOL_TO_BYTE = lookup.findStatic(Utils.class, "booleanToByte",
 73                     MethodType.methodType(byte.class, boolean.class));
 74             ADDRESS_TO_LONG = lookup.findStatic(SharedUtils.class, "unboxSegment",
 75                     MethodType.methodType(long.class, MemorySegment.class));
 76             LONG_TO_ADDRESS = lookup.findStatic(Utils.class, "longToAddress",
 77                     MethodType.methodType(MemorySegment.class, long.class, long.class, long.class));
 78         } catch (Throwable ex) {
 79             throw new ExceptionInInitializerError(ex);
 80         }
 81     }
 82 
 83     public static long alignUp(long n, long alignment) {
 84         return (n + alignment - 1) & -alignment;
 85     }
 86 
 87     public static MemorySegment alignUp(MemorySegment ms, long alignment) {
 88         long offset = ms.address();
 89         return ms.asSlice(alignUp(offset, alignment) - offset);
 90     }
 91 
 92     public static VarHandle makeSegmentViewVarHandle(ValueLayout layout) {
 93         final class VarHandleCache {
 94             private static final Map<ValueLayout, VarHandle> HANDLE_MAP = new ConcurrentHashMap<>();
 95 
 96             static VarHandle put(ValueLayout layout, VarHandle handle) {
 97                 VarHandle prev = HANDLE_MAP.putIfAbsent(layout, handle);
 98                 return prev != null ? prev : handle;
 99             }
100 
101             static VarHandle get(ValueLayout layout) {
102                 return HANDLE_MAP.get(layout);
103             }
104         }
105         layout = layout.withoutName(); // name doesn't matter
106         // keep the addressee layout as it's used below
107 
108         VarHandle handle = VarHandleCache.get(layout);
109         if (handle != null) {
110             return handle;
111         }
112 
113         Class<?> baseCarrier = layout.carrier();
114         if (layout.carrier() == MemorySegment.class) {
115             baseCarrier = switch ((int) ValueLayout.ADDRESS.byteSize()) {
116                 case Long.BYTES -> long.class;
117                 case Integer.BYTES -> int.class;
118                 default -> throw new UnsupportedOperationException("Unsupported address layout");
119             };
120         } else if (layout.carrier() == boolean.class) {
121             baseCarrier = byte.class;
122         }
123 
124         handle = SharedSecrets.getJavaLangInvokeAccess().memorySegmentViewHandle(baseCarrier,
125                 layout.byteAlignment() - 1, layout.order());
126 
127         if (layout.carrier() == boolean.class) {
128             handle = MethodHandles.filterValue(handle, BOOL_TO_BYTE, BYTE_TO_BOOL);
129         } else if (layout instanceof AddressLayout addressLayout) {
130             handle = MethodHandles.filterValue(handle,
131                     MethodHandles.explicitCastArguments(ADDRESS_TO_LONG, MethodType.methodType(baseCarrier, MemorySegment.class)),
132                     MethodHandles.explicitCastArguments(MethodHandles.insertArguments(LONG_TO_ADDRESS, 1,
133                             pointeeByteSize(addressLayout), pointeeByteAlign(addressLayout)),
134                             MethodType.methodType(MemorySegment.class, baseCarrier)));
135         }
136         return VarHandleCache.put(layout, handle);
137     }
138 
139     public static boolean byteToBoolean(byte b) {
140         return b != 0;
141     }
142 
143     private static byte booleanToByte(boolean b) {
144         return b ? (byte)1 : (byte)0;
145     }
146 
147     @ForceInline
148     public static MemorySegment longToAddress(long addr, long size, long align) {
149         if (!isAligned(addr, align)) {
150             throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
151         }
152         return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size);
153     }
154 
155     @ForceInline
156     public static MemorySegment longToAddress(long addr, long size, long align, MemorySessionImpl scope) {
157         if (!isAligned(addr, align)) {
158             throw new IllegalArgumentException("Invalid alignment constraint for address: " + toHexString(addr));
159         }
160         return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(addr, size, scope);
161     }
162 
163     @ForceInline
164     public static boolean isAligned(long offset, long align) {
165         return (offset & (align - 1)) == 0;
166     }
167 
168     @ForceInline
169     public static boolean isElementAligned(ValueLayout layout) {
170         // Fast-path: if both size and alignment are powers of two, we can just
171         // check if one is greater than the other.
172         assert isPowerOfTwo(layout.byteSize());
173         return layout.byteAlignment() <= layout.byteSize();
174     }
175 
176     @ForceInline
177     public static void checkElementAlignment(ValueLayout layout, String msg) {
178         if (!isElementAligned(layout)) {
179             throw new IllegalArgumentException(msg);
180         }
181     }
182 
183     @ForceInline
184     public static void checkElementAlignment(MemoryLayout layout, String msg) {
185         if (layout.byteSize() % layout.byteAlignment() != 0) {
186             throw new IllegalArgumentException(msg);
187         }
188     }
189 
190     public static long pointeeByteSize(AddressLayout addressLayout) {
191         return addressLayout.targetLayout()
192                 .map(MemoryLayout::byteSize)
193                 .orElse(0L);
194     }
195 
196     public static long pointeeByteAlign(AddressLayout addressLayout) {
197         return addressLayout.targetLayout()
198                 .map(MemoryLayout::byteAlignment)
199                 .orElse(1L);
200     }
201 
202     public static void checkAllocationSizeAndAlign(long byteSize, long byteAlignment) {
203         // size should be >= 0
204         if (byteSize < 0) {
205             throw new IllegalArgumentException("Invalid allocation size : " + byteSize);
206         }
207 
208         checkAlign(byteAlignment);
209     }
210 
211     public static void checkAlign(long byteAlignment) {
212         // alignment should be > 0, and power of two
213         if (byteAlignment <= 0 ||
214                 ((byteAlignment & (byteAlignment - 1)) != 0L)) {
215             throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment);
216         }
217     }
218 
219     private static long computePadding(long offset, long align) {
220         boolean isAligned = offset == 0 || offset % align == 0;
221         if (isAligned) {
222             return 0;
223         } else {
224             long gap = offset % align;
225             return align - gap;
226         }
227     }
228 
229     /**
230      * {@return return a struct layout constructed from the given elements, with padding
231      * computed automatically so that they are naturally aligned}.
232      *
233      * @param elements the structs' fields
234      */
235     public static StructLayout computePaddedStructLayout(MemoryLayout... elements) {
236         long offset = 0L;
237         List<MemoryLayout> layouts = new ArrayList<>();
238         long align = 0;
239         for (MemoryLayout l : elements) {
240             long padding = computePadding(offset, l.byteAlignment());
241             if (padding != 0) {
242                 layouts.add(MemoryLayout.paddingLayout(padding));
243                 offset += padding;
244             }
245             layouts.add(l);
246             align = Math.max(align, l.byteAlignment());
247             offset += l.byteSize();
248         }
249         long padding = computePadding(offset, align);
250         if (padding != 0) {
251             layouts.add(MemoryLayout.paddingLayout(padding));
252         }
253         return MemoryLayout.structLayout(layouts.toArray(MemoryLayout[]::new));
254     }
255 
256     public static int byteWidthOfPrimitive(Class<?> primitive) {
257         return Wrapper.forPrimitiveType(primitive).bitWidth() / 8;
258     }
259 
260     public static boolean isPowerOfTwo(long value) {
261         return (value & (value - 1)) == 0L;
262     }
263 
264     public static <L extends MemoryLayout> L wrapOverflow(Supplier<L> layoutSupplier) {
265         try {
266             return layoutSupplier.get();
267         } catch (ArithmeticException ex) {
268             throw new IllegalArgumentException("Layout size exceeds Long.MAX_VALUE");
269         }
270     }
271 
272     public static boolean containsNullChars(String s) {
273         return s.indexOf('\u0000') >= 0;
274     }
275 
276     public static String toHexString(long value) {
277         return "0x" + Long.toHexString(value);
278     }
279 
280     public record BaseAndScale(int base, long scale) {
281 
282         public static final BaseAndScale BYTE =
283                 new BaseAndScale(Unsafe.ARRAY_BYTE_BASE_OFFSET, Unsafe.ARRAY_BYTE_INDEX_SCALE);
284         public static final BaseAndScale CHAR =
285                 new BaseAndScale(Unsafe.ARRAY_CHAR_BASE_OFFSET, Unsafe.ARRAY_CHAR_INDEX_SCALE);
286         public static final BaseAndScale SHORT =
287                 new BaseAndScale(Unsafe.ARRAY_SHORT_BASE_OFFSET, Unsafe.ARRAY_SHORT_INDEX_SCALE);
288         public static final BaseAndScale INT =
289                 new BaseAndScale(Unsafe.ARRAY_INT_BASE_OFFSET, Unsafe.ARRAY_INT_INDEX_SCALE);
290         public static final BaseAndScale FLOAT =
291                 new BaseAndScale(Unsafe.ARRAY_FLOAT_BASE_OFFSET, Unsafe.ARRAY_FLOAT_INDEX_SCALE);
292         public static final BaseAndScale LONG =
293                 new BaseAndScale(Unsafe.ARRAY_LONG_BASE_OFFSET, Unsafe.ARRAY_LONG_INDEX_SCALE);
294         public static final BaseAndScale DOUBLE =
295                 new BaseAndScale(Unsafe.ARRAY_DOUBLE_BASE_OFFSET, Unsafe.ARRAY_DOUBLE_INDEX_SCALE);
296 
297         public static BaseAndScale of(Object array) {
298             return switch (array) {
299                 case byte[]   __ -> BaseAndScale.BYTE;
300                 case char[]   __ -> BaseAndScale.CHAR;
301                 case short[]  __ -> BaseAndScale.SHORT;
302                 case int[]    __ -> BaseAndScale.INT;
303                 case float[]  __ -> BaseAndScale.FLOAT;
304                 case long[]   __ -> BaseAndScale.LONG;
305                 case double[] __ -> BaseAndScale.DOUBLE;
306                 default -> throw new IllegalArgumentException("Not a supported array class: " + array.getClass().getSimpleName());
307             };
308         }
309 
310     }
311 
312 }