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