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 package java.foreign.layout;
  26 
  27 import java.util.Arrays;
  28 import java.util.List;
  29 import java.util.Map;
  30 import java.util.function.ToLongFunction;
  31 import java.util.stream.Collectors;
  32 import java.util.stream.LongStream;
  33 
  34 /**
  35  * A group layout is used to combine together multiple layouts. There are two ways in which layouts can be combined,
  36  * e.g. a 'struct' (see {@link Kind#STRUCT}), where contained elements are laid out one after the other, and a 'union'
  37  * (see {@link Kind#UNION}, where contained elements are laid out 'on top' of each other.
  38  */
  39 public class Group extends AbstractDescriptor<Group> implements Layout {
  40 
  41     /**
  42      * The group kind.
  43      */
  44     public enum Kind {
  45         /**
  46          * A 'struct' kind.
  47          */
  48         STRUCT(LongStream::sum, ""),
  49         /**
  50          * A 'union' kind.
  51          */
  52         UNION(ls -> ls.max().getAsLong(), "|");
  53 
  54         final ToLongFunction<LongStream> sizeFunc;
  55         final String delimTag;
  56 
  57         Kind(ToLongFunction<LongStream> sizeFunc, String delimTag) {
  58             this.sizeFunc = sizeFunc;
  59             this.delimTag = delimTag;
  60         }
  61     }
  62 
  63     private final Kind kind;
  64     private final List<Layout> elements;
  65     private long size = -1L;
  66 
  67     Group(Kind kind, List<Layout> elements, Map<String, String> annotations) {
  68         super(annotations);
  69         this.kind = kind;
  70         this.elements = elements;
  71     }
  72 
  73     /**
  74      * Returns the kind associated to this group.
  75      * @return the group kind.
  76      */
  77     public Kind kind() {
  78         return kind;
  79     }
  80 
  81     /**
  82      * Returns the sub-elements associated with this group.
  83      * @return the element layouts.
  84      */
  85     public List<Layout> elements() {
  86         return elements;
  87     }
  88 
  89     @Override
  90     public String toString() {
  91         return wrapWithAnnotations(elements.stream()
  92                 .map(Object::toString)
  93                 .collect(Collectors.joining(kind.delimTag, "[", "]")));
  94     }
  95 
  96     @Override
  97     public boolean equals(Object other) {
  98         if (this == other) {
  99             return true;
 100         }
 101         if (!super.equals(other)) {
 102             return false;
 103         }
 104         if (!(other instanceof Group)) {
 105             return false;
 106         }
 107         Group g = (Group)other;
 108         return kind.equals(g.kind) && elements.equals(g.elements);
 109     }
 110 
 111     @Override
 112     public int hashCode() {
 113         return super.hashCode() ^ kind.hashCode() ^ elements.hashCode();
 114     }
 115 
 116     @Override
 117     public long bitsSize() {
 118         if (size == -1L) {
 119             size = kind.sizeFunc.applyAsLong(elements.stream().mapToLong(Layout::bitsSize));
 120         }
 121         return size;
 122     }
 123 
 124     @Override
 125     public boolean isPartial() {
 126         return elements.stream().anyMatch(Layout::isPartial);
 127     }
 128 
 129     /**
 130      * Create a new product group layout with given elements.
 131      * @param elements The components of the product layout.
 132      * @return the new product group layout.
 133      */
 134     public static Group struct(Layout... elements) {
 135         return new Group(Kind.STRUCT, Arrays.asList(elements), NO_ANNOS);
 136     }
 137 
 138     /**
 139      * Create a new sum group layout with given elements.
 140      * @param elements The components of the sum layout.
 141      * @return the new sum group layout.
 142      */
 143     public static Group union(Layout... elements) {
 144         return new Group(Kind.UNION, Arrays.asList(elements), NO_ANNOS);
 145     }
 146 
 147     @Override
 148     Group withAnnotations(Map<String, String> annotations) {
 149         return new Group(kind, elements, annotations);
 150     }
 151 }