--- /dev/null 2019-12-04 18:44:18.020007537 +0000 +++ new/src/jdk.incubator.foreign/share/classes/jdk/incubator/foreign/MemoryLayout.java 2019-12-09 18:15:29.956000302 +0000 @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2019, 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.incubator.foreign; + +import jdk.internal.foreign.LayoutPath; +import jdk.internal.foreign.Utils; + +import java.lang.constant.Constable; +import java.lang.constant.DynamicConstantDesc; +import java.lang.invoke.VarHandle; +import java.nio.ByteOrder; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.OptionalLong; + +/** + * A memory layout can be used to describe the contents of a memory segment in a language neutral fashion. + * There are two leaves in the layout hierarchy, value layouts, which are used to represent values of given size and kind (see + * {@link ValueLayout}) and padding layouts which are used, as the name suggests, to represent a portion of a memory + * segment whose contents should be ignored, and which are primarily present for alignment reasons (see {@link MemoryLayout#ofPaddingBits(long)}). + * Some common value layout constants are defined in the {@link MemoryLayouts} class. + *

+ * More complex layouts can be derived from simpler ones: a sequence layout denotes a repetition of one or more + * element layout (see {@link SequenceLayout}); a group layout denotes an aggregation of (typically) heterogeneous + * member layouts (see {@link GroupLayout}). + *

+ * All implementations of this interface must be value-based; + * use of identity-sensitive operations (including reference equality ({@code ==}), identity hash code, or synchronization) on + * instances of {@code MemoryLayout} may have unpredictable results and should be avoided. The {@code equals} method should + * be used for comparisons. + *

+ * Non-platform classes should not implement {@linkplain MemoryLayout} directly. + * + *

Size, alignment and byte order

+ * + * All layouts have a size; layout size for value and padding layouts is always explicitly denoted; this means that a layout description + * always has the same size in bits, regardless of the platform in which it is used. For derived layouts, the size is computed + * as follows: + * + *

+ * Furthermore, all layouts feature a natural alignment which can be inferred as follows: + *

+ * A layout's natural alignment can be overridden if needed (see {@link MemoryLayout#withBitAlignment(long)}), which can be useful to describe + * hyper-aligned layouts. + *

+ * All value layouts have an explicit byte order (see {@link java.nio.ByteOrder}) which is set when the layout is created. + * + *

Layout paths

+ * + * A layout path originates from a root layout (typically a group or a sequence layout) and terminates + * at a layout nested within the root layout - this is the layout selected by the layout path. + * Layout paths are typically expressed as a sequence of one or more {@link PathElement} instances. + *

+ * Layout paths are useful in order to e.g. to obtain offset of leaf elements inside arbitrarily nested layouts + * (see {@link MemoryLayout#offset(PathElement...)}), or to quickly obtain a memory access handle corresponding to the selected + * layout (see {@link MemoryLayout#varHandle(Class, PathElement...)}). + *

+ * Such layout paths can be constructed programmatically using the methods in this class. + * For instance, given a layout constructed as follows: + *

{@code
+SequenceLayout seq = MemoryLayout.ofSequence(5,
+    MemoryLayout.ofStruct(
+        MemoryLayout.ofPaddingBits(32),
+        MemoryLayout.ofValueBits(32, ByteOrder.BIG_ENDIAN).withName("value")
+));
+ * }
+ * + * We can obtain the offset of the member layout named value from seq, as follows: + *
{@code
+long valueOffset = seq.offset(PathElement.sequenceElement(), PathElement.groupElement("value"));
+ * }
+ * + * Layout paths can feature one or more free dimensions. For instance, a layout path traversing + * an unspecified sequence element (that is, where one of the path component was obtained with the + * {@link PathElement#sequenceElement()} method) features an additional free dimension, which will have to be bound at runtime; + * that is, the memory access var handle associated with such a layout path expression will feature an extra {@code long} + * access coordinate. The layout path constructed in the above example features exactly one free dimension. + * + * @apiNote In the future, if the Java language permits, {@link MemoryLayout} + * may become a {@code sealed} interface, which would prohibit subclassing except by + * explicitly permitted types. + * + * @implSpec + * Implementations of this class are immutable and thread-safe. + */ +public interface MemoryLayout extends Constable { + + /** + * Returns an {@link Optional} containing the nominal descriptor for this + * layout, if one can be constructed, or an empty {@link Optional} + * if one cannot be constructed. + * + * @return An {@link Optional} containing the resulting nominal descriptor, + * or an empty {@link Optional} if one cannot be constructed. + */ + @Override + Optional> describeConstable(); + + /** + * Computes the layout size, in bits. + * + * @return the layout size, in bits. + * @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}). + */ + long bitSize(); + + /** + * Computes the layout size, in bytes. + * + * @return the layout size, in bytes. + * @throws UnsupportedOperationException if the layout is, or contains, a sequence layout with unspecified size (see {@link SequenceLayout}), + * or if {@code bitSize()} is not a multiple of 8. + */ + default long byteSize() { + return Utils.bitsToBytesOrThrow(bitSize(), + () -> new UnsupportedOperationException("Cannot compute byte size; bit size is not a multiple of 8")); + } + + /** + * Return the name (if any) associated with this layout. + * + * @return the layout name (if any). + * @see MemoryLayout#withName(String) + */ + Optional name(); + + /** + * Attach a name to this layout. + * + * @param name the layout name. + * @return a new layout which is the same as this layout, except for the name associated to it. + * @see MemoryLayout#name() + */ + MemoryLayout withName(String name); + + /** + * Returns the alignment constraint associated with this layout, expressed in bits. Layout alignment defines a power + * of two {@code A} which is the bit-wise alignment of the layout. If {@code A <= 8} then {@code A/8} is the number of + * bytes that must be aligned for any pointer that correctly points to this layout. Thus: + * + * + * + * @return the layout alignment constraint, in bits. + */ + long bitAlignment(); + + /** + * Returns the alignment constraint associated with this layout, expressed in bytes. Layout alignment defines a power + * of two {@code A} which is the byte-wise alignment of the layout, where {@code A} is the number of bytes that must be aligned + * for any pointer that correctly points to this layout. Thus: + * + * + * + * @return the layout alignment constraint, in bytes. + * @throws UnsupportedOperationException if {@code bitAlignment()} is not a multiple of 8. + */ + default long byteAlignment() { + return Utils.bitsToBytesOrThrow(bitAlignment(), + () -> new UnsupportedOperationException("Cannot compute byte alignment; bit alignment is not a multiple of 8")); + } + + /** + * Creates a new layout which features the desired alignment constraint. + * + * @param bitAlignment the layout alignment constraint, expressed in bits. + * @return a new layout which is the same as this layout, except for the alignment constraint associated to it. + * @throws IllegalArgumentException if {@code bitAlignment} is not a power of two, or if it's less than than 8. + */ + MemoryLayout withBitAlignment(long bitAlignment); + + /** + * Computes the offset of the layout selected by a given layout path, where the path is considered rooted in this + * layout. + * + * @apiNote if the layout path has one (or more) free dimensions, + * the offset is computed as if all the indices corresponding to such dimensions were set to {@code 0}. + * + * @param elements the layout path elements. + * @return The offset of layout selected by a the layout path obtained by concatenating the path elements in {@code elements}. + * @throws IllegalArgumentException if the layout path obtained by concatenating the path elements in {@code elements} + * does not select a valid layout element. + */ + default long offset(PathElement... elements) { + LayoutPath path = LayoutPath.rootPath(this); + for (PathElement e : elements) { + path = ((LayoutPath.PathElementImpl)e).apply(path); + } + return path.offset(); + } + + /** + * Creates a memory access var handle that can be used to dereference memory at the layout selected by a given layout path, + * where the path is considered rooted in this layout. + * + * @apiNote the resulting var handle will feature an additional {@code long} access coordinate for every + * unspecified sequence access component contained in this layout path. Moreover, the resulting var handle + * features certain access mode restrictions, which are common to all memory access var handles. + * + * @param carrier the var handle carrier type. + * @param elements the layout path elements. + * @return a var handle which can be used to dereference memory at the layout denoted by given layout path. + * @throws UnsupportedOperationException if the layout path has one or more elements with incompatible alignment constraints. + * @throws IllegalArgumentException if the carrier does not represent a primitive type, if the carrier is {@code void}, + * {@code boolean}, or if the layout path obtained by concatenating the path elements in {@code elements} + * does not select a value layout (see {@link ValueLayout}), or if the selected value layout has a size that + * that does not match that of the specified carrier type. + */ + default VarHandle varHandle(Class carrier, PathElement... elements) { + LayoutPath path = LayoutPath.rootPath(this); + for (PathElement e : elements) { + path = ((LayoutPath.PathElementImpl)e).apply(path); + } + return path.dereferenceHandle(carrier); + } + + /** + * Instances of this class are used to form layout paths. There + * are two kinds of path elements: group path elements and sequence path elements. Group + * path elements are used to select a given named member layout within a {@link GroupLayout}. Sequence + * path elements are used to select a sequence element layout within a {@link SequenceLayout}; selection + * of sequence element layout can be explicit (see {@link PathElement#sequenceElement(long)}) or + * implicit (see {@link PathElement#sequenceElement()}). When a path uses one or more implicit + * sequence path elements, it acquires additional free dimensions. + *

+ * Non-platform classes should not implement {@linkplain PathElement} directly. + * + * @apiNote In the future, if the Java language permits, {@link PathElement} + * may become a {@code sealed} interface, which would prohibit subclassing except by + * explicitly permitted types. + * + * @implSpec + * Implementations of this interface are immutable and thread-safe. + */ + interface PathElement { + + /** + * Returns a path element which selects a member layout with given name from a given group layout. + * The path element returned by this method does not alter the number of free dimensions of any path + * that is combined with such element. + * + * @implSpec in case multiple group elements with a matching name exist, the path element returned by this + * method will select the first one; that is, the group element with lowest offset from current path is selected. + * + * @param name the name of the group element to be selected. + * @return a path element which selects the group element with given name. + * @throws NullPointerException if the specified group element name is {@code null}. + */ + static PathElement groupElement(String name) { + Objects.requireNonNull(name); + return new LayoutPath.PathElementImpl(path -> path.groupElement(name)); + } + + /** + * Returns a path element which selects the element layout at the specified position in a given the sequence layout. + * The path element returned by this method does not alter the number of free dimensions of any path + * that is combined with such element. + * + * @param index the index of the sequence element to be selected. + * @return a path element which selects the sequence element layout with given index. + * @throws IllegalArgumentException if {@code index < 0}. + */ + static PathElement sequenceElement(long index) { + if (index < 0) { + throw new IllegalArgumentException("Index must be positive: " + index); + } + return new LayoutPath.PathElementImpl(path -> path.sequenceElement(index)); + } + + /** + * Returns a path element which selects the element layout in a range of positions in a given the sequence layout, + * where the range is expressed as a pair of starting index (inclusive) {@code S} and step factor (which can also be negative) + * {@code F}. + * If a path with free dimensions {@code n} is combined with the path element returned by this method, + * the number of free dimensions of the resulting path will be {@code 1 + n}. If the free dimension associated + * with this path is bound by an index {@code I}, the resulting accessed offset can be obtained with the following + * formula: + *

{@code
+E * (S + I * F)
+         * }
+ * where {@code E} is the size (in bytes) of the sequence element layout. + * + * @param start the index of the first sequence element to be selected. + * @param step the step factor at which subsequence sequence elements are to be selected. + * @return a path element which selects the sequence element layout with given index. + * @throws IllegalArgumentException if {@code start < 0}, or {@code step == 0}. + */ + static PathElement sequenceElement(long start, long step) { + if (start < 0) { + throw new IllegalArgumentException("Start index must be positive: " + start); + } + if (step == 0) { + throw new IllegalArgumentException("Step must be != 0: " + step); + } + return new LayoutPath.PathElementImpl(path -> path.sequenceElement(start, step)); + } + + /** + * Returns a path element which selects an unspecified element layout from a given sequence layout. + * If a path with free dimensions {@code n} is combined with the path element returned by this method, + * the number of free dimensions of the resulting path will be {@code 1 + n}. + * + * @return a path element which selects an unspecified sequence element layout. + */ + static PathElement sequenceElement() { + return new LayoutPath.PathElementImpl(LayoutPath::sequenceElement); + } + } + + /** + * Compares the specified object with this layout for equality. Returns {@code true} if and only if the specified + * object is also a layout, and it is equal to this layout. + * + * @param that the object to be compared for equality with this layout. + * @return {@code true} if the specified object is equal to this layout. + */ + boolean equals(Object that); + + /** + * Returns the hash code value for this layout. + * + * @return the hash code value for this layout. + */ + int hashCode(); + + /** + * Returns a string representation of this layout. + * + * @return a string representation of this layout. + */ + @Override + String toString(); + + /** + * Create a new padding layout with given size. + * + * @param size the padding size in bits. + * @return the new selector layout. + * @throws IllegalArgumentException if {@code size <= 0}. + */ + static MemoryLayout ofPaddingBits(long size) { + AbstractLayout.checkSize(size); + return new PaddingLayout(size); + } + + /** + * Create a value layout of given byte order and size. + * + * @param size the value layout size. + * @param order the value layout's byte order. + * @return a new value layout. + * @throws IllegalArgumentException if {@code size <= 0}. + */ + static ValueLayout ofValueBits(long size, ByteOrder order) { + AbstractLayout.checkSize(size); + return new ValueLayout(order, size); + } + + /** + * Create a new sequence layout with given element layout and element count. + * + * @param elementCount the sequence element count. + * @param elementLayout the sequence element layout. + * @return the new sequence layout with given element layout and size. + * @throws IllegalArgumentException if {@code elementCount < 0}. + */ + static SequenceLayout ofSequence(long elementCount, MemoryLayout elementLayout) { + AbstractLayout.checkSize(elementCount, true); + OptionalLong size = OptionalLong.of(elementCount); + return new SequenceLayout(size, elementLayout); + } + + /** + * Create a new sequence layout, with unbounded element count and given element layout. + * + * @param elementLayout the element layout of the sequence layout. + * @return the new sequence layout with given element layout. + */ + static SequenceLayout ofSequence(MemoryLayout elementLayout) { + return new SequenceLayout(OptionalLong.empty(), elementLayout); + } + + /** + * Create a new struct group layout with given member layouts. + * + * @param elements The member layouts of the struct group layout. + * @return a new struct group layout with given member layouts. + */ + static GroupLayout ofStruct(MemoryLayout... elements) { + return new GroupLayout(GroupLayout.Kind.STRUCT, List.of(elements)); + } + + /** + * Create a new union group layout with given member layouts. + * + * @param elements The member layouts of the union layout. + * @return a new union group layout with given member layouts. + */ + static GroupLayout ofUnion(MemoryLayout... elements) { + return new GroupLayout(GroupLayout.Kind.UNION, List.of(elements)); + } +}