1 /*
  2  * Copyright (c) 2021, 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 package jdk.incubator.foreign;
 27 
 28 import jdk.internal.foreign.AbstractMemorySegmentImpl;
 29 import jdk.internal.foreign.ArenaAllocator;
 30 import jdk.internal.foreign.ResourceScopeImpl;
 31 import jdk.internal.foreign.Utils;
 32 
 33 import java.lang.invoke.VarHandle;
 34 import java.lang.reflect.Array;
 35 import java.nio.ByteOrder;
 36 import java.nio.charset.StandardCharsets;
 37 import java.util.Objects;
 38 import java.util.function.Function;
 39 
 40 /**
 41  * This interface models a memory allocator. Clients implementing this interface
 42  * must implement the {@link #allocate(long, long)} method. This interface defines several default methods
 43  * which can be useful to create segments from several kinds of Java values such as primitives and arrays.
 44  * This interface can be seen as a thin wrapper around the basic capabilities for
 45  * {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) creating} native segments;
 46  * since {@link SegmentAllocator} is a <em>functional interface</em>,
 47  * clients can easily obtain a native allocator by using either a lambda expression or a method reference.
 48  * <p>
 49  * This interface also defines factories for commonly used allocators:
 50  * <ul>
 51  *     <li>{@link #nativeAllocator(ResourceScope)} creates an allocator which
 52  *     {@linkplain MemorySegment#allocateNative(long, long, ResourceScope) allocates} native segments, backed by a given scope;</li>
 53  *     <li>{@link #newNativeArena(ResourceScope)} creates a more efficient arena-style native allocator, where memory
 54  *     is allocated in bigger blocks, which are then sliced accordingly to fit allocation requests;</li>
 55  *     <li>{@link #prefixAllocator(MemorySegment)} creates an allocator which wraps a segment (either on-heap or off-heap)
 56  *     and recycles its content upon each new allocation request.</li>
 57  * </ul>
 58  * <p>
 59  * Passing a segment allocator to an API can be especially useful in circumstances where a client wants to communicate <em>where</em>
 60  * the results of a certain operation (performed by the API) should be stored, as a memory segment. For instance,
 61  * {@linkplain CLinker#downcallHandle(FunctionDescriptor) downcall method handles} can accept an additional
 62  * {@link SegmentAllocator} parameter if the underlying native function is known to return a struct by-value. Effectively,
 63  * the allocator parameter tells the linker runtime where to store the return value of the native function.
 64  */
 65 @FunctionalInterface
 66 public interface SegmentAllocator {
 67 
 68     /**
 69      * Converts a Java string into a UTF-8 encoded, null-terminated C string,
 70      * storing the result into a memory segment.
 71      * <p>
 72      * This method always replaces malformed-input and unmappable-character
 73      * sequences with this charset's default replacement byte array.  The
 74      * {@link java.nio.charset.CharsetEncoder} class should be used when more
 75      * control over the encoding process is required.
 76      *
 77      * @param str the Java string to be converted into a C string.
 78      * @return a new native memory segment containing the converted C string.
 79      */
 80     default MemorySegment allocateUtf8String(String str) {
 81         Objects.requireNonNull(str);
 82         return Utils.toCString(str.getBytes(StandardCharsets.UTF_8), this);
 83     }
 84 
 85     /**
 86      * Allocate a memory segment with given layout and initialize it with given byte value.
 87      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
 88      * @param layout the layout of the block of memory to be allocated.
 89      * @param value the value to be set on the newly allocated memory block.
 90      * @return a segment for the newly allocated memory block.
 91      */
 92     default MemorySegment allocate(ValueLayout.OfByte layout, byte value) {
 93         Objects.requireNonNull(layout);
 94         VarHandle handle = layout.varHandle();
 95         MemorySegment addr = allocate(layout);
 96         handle.set(addr, value);
 97         return addr;
 98     }
 99 
100     /**
101      * Allocate a memory segment with given layout and initialize it with given char value.
102      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
103      * @param layout the layout of the block of memory to be allocated.
104      * @param value the value to be set on the newly allocated memory block.
105      * @return a segment for the newly allocated memory block.
106      */
107     default MemorySegment allocate(ValueLayout.OfChar layout, char value) {
108         Objects.requireNonNull(layout);
109         VarHandle handle = layout.varHandle();
110         MemorySegment addr = allocate(layout);
111         handle.set(addr, value);
112         return addr;
113     }
114 
115     /**
116      * Allocate a memory segment with given layout and initialize it with given short value.
117      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
118      * @param layout the layout of the block of memory to be allocated.
119      * @param value the value to be set on the newly allocated memory block.
120      * @return a segment for the newly allocated memory block.
121      */
122     default MemorySegment allocate(ValueLayout.OfShort layout, short value) {
123         Objects.requireNonNull(layout);
124         VarHandle handle = layout.varHandle();
125         MemorySegment addr = allocate(layout);
126         handle.set(addr, value);
127         return addr;
128     }
129 
130     /**
131      * Allocate a memory segment with given layout and initialize it with given int value.
132      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
133      * @param layout the layout of the block of memory to be allocated.
134      * @param value the value to be set on the newly allocated memory block.
135      * @return a segment for the newly allocated memory block.
136      */
137     default MemorySegment allocate(ValueLayout.OfInt layout, int value) {
138         Objects.requireNonNull(layout);
139         VarHandle handle = layout.varHandle();
140         MemorySegment addr = allocate(layout);
141         handle.set(addr, value);
142         return addr;
143     }
144 
145     /**
146      * Allocate a memory segment with given layout and initialize it with given float value.
147      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
148      * @param layout the layout of the block of memory to be allocated.
149      * @param value the value to be set on the newly allocated memory block.
150      * @return a segment for the newly allocated memory block.
151      */
152     default MemorySegment allocate(ValueLayout.OfFloat layout, float value) {
153         Objects.requireNonNull(layout);
154         VarHandle handle = layout.varHandle();
155         MemorySegment addr = allocate(layout);
156         handle.set(addr, value);
157         return addr;
158     }
159 
160     /**
161      * Allocate a memory segment with given layout and initialize it with given long value.
162      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
163      * @param layout the layout of the block of memory to be allocated.
164      * @param value the value to be set on the newly allocated memory block.
165      * @return a segment for the newly allocated memory block.
166      */
167     default MemorySegment allocate(ValueLayout.OfLong layout, long value) {
168         Objects.requireNonNull(layout);
169         VarHandle handle = layout.varHandle();
170         MemorySegment addr = allocate(layout);
171         handle.set(addr, value);
172         return addr;
173     }
174 
175     /**
176      * Allocate a memory segment with given layout and initialize it with given double value.
177      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
178      * @param layout the layout of the block of memory to be allocated.
179      * @param value the value to be set on the newly allocated memory block.
180      * @return a segment for the newly allocated memory block.
181      */
182     default MemorySegment allocate(ValueLayout.OfDouble layout, double value) {
183         Objects.requireNonNull(layout);
184         VarHandle handle = layout.varHandle();
185         MemorySegment addr = allocate(layout);
186         handle.set(addr, value);
187         return addr;
188     }
189 
190     /**
191      * Allocate a memory segment with given layout and initialize it with given address value
192      * (expressed as an {@link Addressable} instance).
193      * The address value might be narrowed according to the platform address size (see {@link ValueLayout#ADDRESS}).
194      * @implSpec the default implementation for this method calls {@code this.allocate(layout)}.
195      * @param layout the layout of the block of memory to be allocated.
196      * @param value the value to be set on the newly allocated memory block.
197      * @return a segment for the newly allocated memory block.
198      */
199     default MemorySegment allocate(ValueLayout.OfAddress layout, Addressable value) {
200         Objects.requireNonNull(value);
201         Objects.requireNonNull(layout);
202         MemorySegment segment = allocate(layout);
203         layout.varHandle().set(segment, value.address());
204         return segment;
205     }
206 
207     /**
208      * Allocate a memory segment with given layout and initialize it with given byte array.
209      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
210      * @param elementLayout the element layout of the array to be allocated.
211      * @param array the array to be copied on the newly allocated memory block.
212      * @return a segment for the newly allocated memory block.
213      */
214     default MemorySegment allocateArray(ValueLayout.OfByte elementLayout, byte[] array) {
215         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
216     }
217 
218     /**
219      * Allocate a memory segment with given layout and initialize it with given short array.
220      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
221      * @param elementLayout the element layout of the array to be allocated.
222      * @param array the array to be copied on the newly allocated memory block.
223      * @return a segment for the newly allocated memory block.
224      */
225     default MemorySegment allocateArray(ValueLayout.OfShort elementLayout, short[] array) {
226         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
227     }
228 
229     /**
230      * Allocate a memory segment with given layout and initialize it with given char array.
231      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
232      * @param elementLayout the element layout of the array to be allocated.
233      * @param array the array to be copied on the newly allocated memory block.
234      * @return a segment for the newly allocated memory block.
235      */
236     default MemorySegment allocateArray(ValueLayout.OfChar elementLayout, char[] array) {
237         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
238     }
239 
240     /**
241      * Allocate a memory segment with given layout and initialize it with given int array.
242      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
243      * @param elementLayout the element layout of the array to be allocated.
244      * @param array the array to be copied on the newly allocated memory block.
245      * @return a segment for the newly allocated memory block.
246      */
247     default MemorySegment allocateArray(ValueLayout.OfInt elementLayout, int[] array) {
248         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
249     }
250 
251     /**
252      * Allocate a memory segment with given layout and initialize it with given float array.
253      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
254      * @param elementLayout the element layout of the array to be allocated.
255      * @param array the array to be copied on the newly allocated memory block.
256      * @return a segment for the newly allocated memory block.
257      */
258     default MemorySegment allocateArray(ValueLayout.OfFloat elementLayout, float[] array) {
259         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
260     }
261 
262     /**
263      * Allocate a memory segment with given layout and initialize it with given long array.
264      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
265      * @param elementLayout the element layout of the array to be allocated.
266      * @param array the array to be copied on the newly allocated memory block.
267      * @return a segment for the newly allocated memory block.
268      */
269     default MemorySegment allocateArray(ValueLayout.OfLong elementLayout, long[] array) {
270         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
271     }
272 
273     /**
274      * Allocate a memory segment with given layout and initialize it with given double array.
275      * @implSpec the default implementation for this method calls {@code this.allocateArray(layout, array.length)}.
276      * @param elementLayout the element layout of the array to be allocated.
277      * @param array the array to be copied on the newly allocated memory block.
278      * @return a segment for the newly allocated memory block.
279      */
280     default MemorySegment allocateArray(ValueLayout.OfDouble elementLayout, double[] array) {
281         return copyArrayWithSwapIfNeeded(array, elementLayout, MemorySegment::ofArray);
282     }
283 
284     private <Z> MemorySegment copyArrayWithSwapIfNeeded(Z array, ValueLayout elementLayout,
285                                                         Function<Z, MemorySegment> heapSegmentFactory) {
286         Objects.requireNonNull(array);
287         Objects.requireNonNull(elementLayout);
288         int size = Array.getLength(array);
289         MemorySegment addr = allocate(MemoryLayout.sequenceLayout(size, elementLayout));
290         MemorySegment.copy(heapSegmentFactory.apply(array), elementLayout, 0,
291                 addr, elementLayout.withOrder(ByteOrder.nativeOrder()), 0, size);
292         return addr;
293     }
294 
295     /**
296      * Allocate a memory segment with given layout.
297      * @implSpec the default implementation for this method calls {@code this.allocate(layout.byteSize(), layout.byteAlignment())}.
298      * @param layout the layout of the block of memory to be allocated.
299      * @return a segment for the newly allocated memory block.
300      */
301     default MemorySegment allocate(MemoryLayout layout) {
302         Objects.requireNonNull(layout);
303         return allocate(layout.byteSize(), layout.byteAlignment());
304     }
305 
306     /**
307      * Allocate a memory segment with given element layout and size.
308      * @implSpec the default implementation for this method calls {@code this.allocate(MemoryLayout.sequenceLayout(count, elementLayout))}.
309      * @param elementLayout the array element layout.
310      * @param count the array element count.
311      * @return a segment for the newly allocated memory block.
312      */
313     default MemorySegment allocateArray(MemoryLayout elementLayout, long count) {
314         Objects.requireNonNull(elementLayout);
315         return allocate(MemoryLayout.sequenceLayout(count, elementLayout));
316     }
317 
318     /**
319      * Allocate a memory segment with given size
320      * and default alignment constraints (1-byte aligned).
321      * @implSpec the default implementation for this method calls {@code this.allocate(bytesSize, 1)}.
322      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
323      * @return a segment for the newly allocated memory block.
324      */
325     default MemorySegment allocate(long bytesSize) {
326         return allocate(bytesSize, 1);
327     }
328 
329     /**
330      * Allocate a memory segment with given size and alignment constraints.
331      * @param bytesSize the size (in bytes) of the block of memory to be allocated.
332      * @param bytesAlignment the alignment (in bytes) of the block of memory to be allocated.
333      * @return a segment for the newly allocated memory block.
334      */
335     MemorySegment allocate(long bytesSize, long bytesAlignment);
336 
337     /**
338      * Returns a native unbounded arena-based allocator, with predefined block size and maximum arena size,
339      * associated with the provided scope. Equivalent to the following code:
340      * <blockquote><pre>{@code
341     SegmentAllocator.newNativeArena(Long.MAX_VALUE, predefinedBlockSize, scope);
342      * }</pre></blockquote>
343      *
344      * @param scope the scope associated with the segments returned by the arena-based allocator.
345      * @return a new unbounded arena-based allocator
346      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
347      * than the thread owning {@code scope}.
348      */
349     static SegmentAllocator newNativeArena(ResourceScope scope) {
350         return newNativeArena(Long.MAX_VALUE, ArenaAllocator.DEFAULT_BLOCK_SIZE, scope);
351     }
352 
353     /**
354      * Returns a native unbounded arena-based allocator, with block size set to the specified arena size, associated with
355      * the provided scope, with given arena size. Equivalent to the following code:
356      * <blockquote><pre>{@code
357     SegmentAllocator.newNativeArena(arenaSize, arenaSize, scope);
358      * }</pre></blockquote>
359      *
360      * @param arenaSize the size (in bytes) of the allocation arena.
361      * @param scope the scope associated with the segments returned by the arena-based allocator.
362      * @return a new unbounded arena-based allocator
363      * @throws IllegalArgumentException if {@code arenaSize <= 0}.
364      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
365      * than the thread owning {@code scope}.
366      */
367     static SegmentAllocator newNativeArena(long arenaSize, ResourceScope scope) {
368         return newNativeArena(arenaSize, arenaSize, scope);
369     }
370 
371     /**
372      * Returns a native arena-based allocator, associated with the provided scope, with given arena size and block size.
373      * <p>
374      * The returned allocator {@linkplain MemorySegment#allocateNative(long, ResourceScope) allocates} a memory segment
375      * {@code S} of the specified block size and then responds to allocation requests in one of the following ways:
376      * <ul>
377      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has a <em>free</em>
378      *     slice {@code S'} which fits that allocation request, return that {@code S'}.
379      *     <li>if the size of the allocation requests is smaller than the size of {@code S}, and {@code S} has no <em>free</em>
380      *     slices which fits that allocation request, allocate a new segment {@code S'}, which has same size as {@code S}
381      *     and set {@code S = S'}; the allocator then tries to respond to the same allocation request again.
382      *     <li>if the size of the allocation requests is bigger than the size of {@code S}, allocate a new segment {@code S'},
383      *     which has a sufficient size to satisfy the allocation request, and return {@code S'}.
384      * </ul>
385      * <p>
386      * This segment allocator can be useful when clients want to perform multiple allocation requests while avoiding the
387      * cost associated with allocating a new off-heap memory region upon each allocation request.
388      * <p>
389      * An allocator associated with a <em>shared</em> resource scope is thread-safe and allocation requests may be
390      * performed concurrently; conversely, if the arena allocator is associated with a <em>confined</em> resource scope,
391      * allocation requests can only occur from the thread owning the allocator's resource scope.
392      * <p>
393      * The returned allocator might throw an {@link OutOfMemoryError} if the total memory allocated with this allocator
394      * exceeds the arena size, or the system capacity. Furthermore, the returned allocator is not thread safe, and all
395      * allocation requests should occur within a single thread (regardless of the scope associated with the native arena).
396      *
397      * @param arenaSize the size (in bytes) of the allocation arena.
398      * @param blockSize the block size associated with the arena-based allocator.
399      * @param scope the scope associated with the segments returned by the arena-based allocator.
400      * @return a new unbounded arena-based allocator
401      * @throws IllegalArgumentException if {@code blockSize <= 0}, if {@code arenaSize <= 0} or if {@code arenaSize < blockSize}.
402      * @throws IllegalStateException if {@code scope} has been already closed, or if access occurs from a thread other
403      * than the thread owning {@code scope}.
404      */
405     static SegmentAllocator newNativeArena(long arenaSize, long blockSize, ResourceScope scope) {
406         Objects.requireNonNull(scope);
407         if (blockSize <= 0) {
408             throw new IllegalArgumentException("Invalid block size: " + blockSize);
409         }
410         if (arenaSize <= 0 || arenaSize < blockSize) {
411             throw new IllegalArgumentException("Invalid arena size: " + arenaSize);
412         }
413         return new ArenaAllocator(blockSize, arenaSize, scope);
414     }
415 
416     /**
417      * Returns a segment allocator which responds to allocation requests by recycling a single segment; that is,
418      * each new allocation request will return a new slice starting at the segment offset {@code 0} (alignment
419      * constraints are ignored by this allocator), hence the name <em>prefix allocator</em>.
420      * Equivalent to (but likely more efficient than) the following code:
421      * <blockquote><pre>{@code
422     MemorySegment segment = ...
423     SegmentAllocator prefixAllocator = (size, align) -> segment.asSlice(0, size);
424      * }</pre></blockquote>
425      * <p>
426      * This allocator can be useful to limit allocation requests in case a client
427      * knows that they have fully processed the contents of the allocated segment before the subsequent allocation request
428      * takes place.
429      * <p>
430      * While the allocator returned by this method is <em>thread-safe</em>, concurrent access on the same recycling
431      * allocator might cause a thread to overwrite contents written to the underlying segment by a different thread.
432      *
433      * @param segment the memory segment to be recycled by the returned allocator.
434      * @return an allocator which recycles an existing segment upon each new allocation request.
435      */
436     static SegmentAllocator prefixAllocator(MemorySegment segment) {
437         Objects.requireNonNull(segment);
438         return (AbstractMemorySegmentImpl)segment;
439     }
440 
441     /**
442      * Returns a native allocator, associated with the provided scope. Equivalent to (but likely more efficient than)
443      * the following code:
444      * <blockquote><pre>{@code
445     ResourceScope scope = ...
446     SegmentAllocator nativeAllocator = (size, align) -> MemorySegment.allocateNative(size, align, scope);
447      * }</pre></blockquote>
448      *
449      * @param scope the scope associated with the returned allocator.
450      * @return a native allocator, associated with the provided scope.
451      */
452     static SegmentAllocator nativeAllocator(ResourceScope scope) {
453         Objects.requireNonNull(scope);
454         return (ResourceScopeImpl)scope;
455     }
456 
457     /**
458      * Returns a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
459      * Equivalent to (but likely more efficient than) the following code:
460      * <blockquote><pre>{@code
461     ResourceScope scope = ...
462     SegmentAllocator implicitAllocator = (size, align) -> MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
463      * }</pre></blockquote>
464      *
465      * @return a native allocator which allocates segments in independent {@linkplain ResourceScope#newImplicitScope() implicit scopes}.
466      */
467     static SegmentAllocator implicitAllocator() {
468         class Holder {
469             static final SegmentAllocator IMPLICIT_ALLOCATOR = (size, align) ->
470                     MemorySegment.allocateNative(size, align, ResourceScope.newImplicitScope());
471         }
472         return Holder.IMPLICIT_ALLOCATOR;
473     }
474 }