1 /*
  2  *  Copyright (c) 2019, 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 java.lang.constant.ConstantDesc;
 29 import java.lang.constant.ConstantDescs;
 30 import java.lang.constant.DynamicConstantDesc;
 31 import java.lang.constant.MethodHandleDesc;
 32 import java.util.Collections;
 33 import java.util.List;
 34 import java.util.Objects;
 35 import java.util.Optional;
 36 import java.util.OptionalLong;
 37 import java.util.function.LongBinaryOperator;
 38 import java.util.stream.Collectors;
 39 
 40 /**
 41  * A group layout is used to combine multiple <em>member layouts</em>. There are two ways in which member layouts
 42  * can be combined: if member layouts are laid out one after the other, the resulting group layout is said to be a <em>struct</em>
 43  * (see {@link MemoryLayout#structLayout(MemoryLayout...)}); conversely, if all member layouts are laid out at the same starting offset,
 44  * the resulting group layout is said to be a <em>union</em> (see {@link MemoryLayout#unionLayout(MemoryLayout...)}).
 45  * <p>
 46  * This is a <a href="{@docRoot}/java.base/java/lang/doc-files/ValueBased.html">value-based</a>
 47  * class; programmers should treat instances that are
 48  * {@linkplain #equals(Object) equal} as interchangeable and should not
 49  * use instances for synchronization, or unpredictable behavior may
 50  * occur. For example, in a future release, synchronization may fail.
 51  * The {@code equals} method should be used for comparisons.
 52  *
 53  * <p> Unless otherwise specified, passing a {@code null} argument, or an array argument containing one or more {@code null}
 54  * elements to a method in this class causes a {@link NullPointerException NullPointerException} to be thrown. </p>
 55  *
 56  * @implSpec
 57  * This class is immutable and thread-safe.
 58  */
 59 public final class GroupLayout extends AbstractLayout implements MemoryLayout {
 60 
 61     /**
 62      * The group kind.
 63      */
 64     enum Kind {
 65         /**
 66          * A 'struct' kind.
 67          */
 68         STRUCT("", MH_STRUCT, Long::sum),
 69         /**
 70          * A 'union' kind.
 71          */
 72         UNION("|", MH_UNION, Math::max);
 73 
 74         final String delimTag;
 75         final MethodHandleDesc mhDesc;
 76         final LongBinaryOperator sizeOp;
 77 
 78         Kind(String delimTag, MethodHandleDesc mhDesc, LongBinaryOperator sizeOp) {
 79             this.delimTag = delimTag;
 80             this.mhDesc = mhDesc;
 81             this.sizeOp = sizeOp;
 82         }
 83 
 84         OptionalLong sizeof(List<MemoryLayout> elems) {
 85             long size = 0;
 86             for (MemoryLayout elem : elems) {
 87                 if (AbstractLayout.optSize(elem).isPresent()) {
 88                     size = sizeOp.applyAsLong(size, elem.bitSize());
 89                 } else {
 90                     return OptionalLong.empty();
 91                 }
 92             }
 93             return OptionalLong.of(size);
 94         }
 95 
 96         long alignof(List<MemoryLayout> elems) {
 97             return elems.stream().mapToLong(MemoryLayout::bitAlignment).max() // max alignment in case we have member layouts
 98                     .orElse(1); // or minimal alignment if no member layout is given
 99         }
100     }
101 
102     private final Kind kind;
103     private final List<MemoryLayout> elements;
104 
105     GroupLayout(Kind kind, List<MemoryLayout> elements) {
106         this(kind, elements, kind.alignof(elements), Optional.empty());
107     }
108 
109     GroupLayout(Kind kind, List<MemoryLayout> elements, long alignment, Optional<String> name) {
110         super(kind.sizeof(elements), alignment, name);
111         this.kind = kind;
112         this.elements = elements;
113     }
114 
115     /**
116      * Returns the member layouts associated with this group.
117      *
118      * @apiNote the order in which member layouts are returned is the same order in which member layouts have
119      * been passed to one of the group layout factory methods (see {@link MemoryLayout#structLayout(MemoryLayout...)},
120      * {@link MemoryLayout#unionLayout(MemoryLayout...)}).
121      *
122      * @return the member layouts associated with this group.
123      */
124     public List<MemoryLayout> memberLayouts() {
125         return Collections.unmodifiableList(elements);
126     }
127 
128     @Override
129     public String toString() {
130         return decorateLayoutString(elements.stream()
131                 .map(Object::toString)
132                 .collect(Collectors.joining(kind.delimTag, "[", "]")));
133     }
134 
135     /**
136      * Is this group layout a <em>struct</em>?
137      *
138      * @return true, if this group layout is a <em>struct</em>.
139      */
140     public boolean isStruct() {
141         return kind == Kind.STRUCT;
142     }
143 
144     /**
145      * Is this group layout a <em>union</em>?
146      *
147      * @return true, if this group layout is a <em>union</em>.
148      */
149     public boolean isUnion() {
150         return kind == Kind.UNION;
151     }
152 
153     @Override
154     public boolean equals(Object other) {
155         if (this == other) {
156             return true;
157         }
158         if (!super.equals(other)) {
159             return false;
160         }
161         if (!(other instanceof GroupLayout g)) {
162             return false;
163         }
164         return kind.equals(g.kind) && elements.equals(g.elements);
165     }
166 
167     @Override
168     public int hashCode() {
169         return Objects.hash(super.hashCode(), kind, elements);
170     }
171 
172     @Override
173     GroupLayout dup(long alignment, Optional<String> name) {
174         return new GroupLayout(kind, elements, alignment, name);
175     }
176 
177     @Override
178     boolean hasNaturalAlignment() {
179         return alignment == kind.alignof(elements);
180     }
181 
182     @Override
183     public Optional<DynamicConstantDesc<GroupLayout>> describeConstable() {
184         ConstantDesc[] constants = new ConstantDesc[1 + elements.size()];
185         constants[0] = kind.mhDesc;
186         for (int i = 0 ; i < elements.size() ; i++) {
187             constants[i + 1] = elements.get(i).describeConstable().get();
188         }
189         return Optional.of(decorateLayoutConstant(DynamicConstantDesc.ofNamed(
190                     ConstantDescs.BSM_INVOKE, kind.name().toLowerCase(),
191                 CD_GROUP_LAYOUT, constants)));
192     }
193 
194     //hack: the declarations below are to make javadoc happy; we could have used generics in AbstractLayout
195     //but that causes issues with javadoc, see JDK-8224052
196 
197     /**
198      * {@inheritDoc}
199      */
200     @Override
201     public GroupLayout withName(String name) {
202         return (GroupLayout)super.withName(name);
203     }
204 
205     /**
206      * {@inheritDoc}
207      */
208     @Override
209     public GroupLayout withBitAlignment(long alignmentBits) {
210         return (GroupLayout)super.withBitAlignment(alignmentBits);
211     }
212 }