1 /*
  2  * Copyright (c) 2024, 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 hat.ifacemapper;
 26 
 27 import hat.Accelerator;
 28 import hat.buffer.Buffer;
 29 import hat.ifacemapper.accessor.AccessorInfo;
 30 import hat.ifacemapper.accessor.ValueType;
 31 
 32 import java.lang.foreign.MemorySegment;
 33 import java.lang.foreign.ValueLayout;
 34 import java.util.ArrayList;
 35 import java.util.Arrays;
 36 import java.util.List;
 37 import java.util.Optional;
 38 import java.util.concurrent.ThreadLocalRandom;
 39 import java.util.function.Consumer;
 40 
 41 public class Schema<T extends Buffer> {
 42     final public IfaceType rootIfaceType;
 43     public Class<T> iface;
 44 
 45     public static abstract class SchemaNode {
 46         public static final class Padding extends FieldNode {
 47             int len;
 48 
 49             Padding(IfaceType parent, int len) {
 50                 super(parent, AccessorInfo.Key.NONE, "pad" + len);
 51                 this.len = len;
 52             }
 53 
 54             /**
 55              * Generates a set of n-random characters from a set of legal characters in C99.
 56              * @param numCharsToBuild
 57              * @return {@link String}
 58              */
 59             private String generateRandomString(final int numCharsToBuild) {
 60                 StringBuilder sb = new StringBuilder();
 61                 String LEGAL_CHARS = "_$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
 62                 ThreadLocalRandom.current() //
 63                         .ints(numCharsToBuild, 0, LEGAL_CHARS.length()) //
 64                         .mapToObj(LEGAL_CHARS::charAt) //
 65                         .forEach(sb::append);
 66                 return sb.toString();
 67             }
 68 
 69             /**
 70              * Returns a string in C99 to represent the padding. It generates
 71              * the <code>pad$</code> name with a set of 5 characters in order to avoid
 72              * collision of names in the same iFace schema.
 73              * @return {@link String}
 74              */
 75             public String toC99() {
 76                 String randomPostfix = generateRandomString(5);
 77                 return "char pad$" + randomPostfix + "[" + len + "]";
 78             }
 79 
 80             @Override
 81             public void toText(String indent, Consumer<String> stringConsumer) {
 82                 stringConsumer.accept(indent + "padding " + len + " bytes");
 83             }
 84         }
 85     }
 86 
 87     Schema(Class<T> iface, IfaceType rootIfaceType) {
 88         this.iface = iface;
 89         this.rootIfaceType = rootIfaceType;
 90     }
 91 
 92     public T allocate(Accelerator accelerator, int... boundLengths) {
 93         BoundSchema<?> boundSchema = new BoundSchema<>(this, boundLengths);
 94         T instance = (T) boundSchema.allocate(accelerator.lookup, accelerator);
 95         MemorySegment memorySegment = Buffer.getMemorySegment(instance);
 96         int[] count = new int[]{0};
 97 
 98         boundSchema.boundArrayFields().forEach(boundArrayFieldLayout -> {
 99             boundArrayFieldLayout.dimFields.forEach(dimLayout -> {
100                 long dimOffset = dimLayout.offset();
101                 int dim = boundLengths[count[0]++];
102                 if (dimLayout.field instanceof FieldNode.ArrayLen arrayLen) {
103                     if (arrayLen.key.accessorType.equals(AccessorInfo.AccessorType.GETTER_AND_SETTER)) {
104                         throw new IllegalStateException("You have a bound array dim field " + dimLayout.field.name + " controlling size of " + boundArrayFieldLayout.field.name + "[] which has a setter ");
105                     }
106                     if (arrayLen.type == Long.TYPE) {
107                         memorySegment.set(ValueLayout.JAVA_LONG, dimOffset, dim);
108                     } else if (arrayLen.type == Integer.TYPE) {
109                         memorySegment.set(ValueLayout.JAVA_INT, dimOffset, dim);
110                     } else {
111                         throw new IllegalArgumentException("Unsupported array length type: " + arrayLen.type);
112                     }
113                 }
114             });
115         });
116 
117 
118         return instance;
119     }
120 
121     public static <T extends Buffer> Schema<T> of(Class<T> iface, Consumer<IfaceType> parentFieldConsumer) {
122         var struct = new IfaceType.Struct(null, (Class<MappableIface>) (Object) iface); // why the need for this?
123         parentFieldConsumer.accept(struct);
124         return new Schema<>(iface, struct);
125     }
126 
127     public void toText(Consumer<String> stringConsumer) {
128         rootIfaceType.toText("", stringConsumer);
129     }
130 
131     public static abstract sealed class IfaceType
132             permits IfaceType.Union, IfaceType.Struct {
133         public final IfaceType parent;
134         public List<FieldNode> fields = new ArrayList<>();
135         public List<IfaceType> ifaceTypes = new ArrayList<>();
136         public Class<MappableIface> iface;
137 
138         <T extends FieldNode> T addField(T child) {
139             fields.add(child);
140             return child;
141         }
142 
143         <T extends IfaceType> T addIfaceTypeNode(T child) {
144             ifaceTypes.add(child);
145             return child;
146         }
147 
148         IfaceType(IfaceType parent, Class<MappableIface> iface) {
149             this.parent = parent;
150             this.iface = iface;
151         }
152 
153         IfaceType getChild(Class<?> iface) {
154             Optional<IfaceType> ifaceTypeNodeOptional = ifaceTypes
155                     .stream()
156                     .filter(n -> n.iface.equals(iface))
157                     .findFirst();
158             if (ifaceTypeNodeOptional.isPresent()) {
159                 return ifaceTypeNodeOptional.get();
160             } else {
161                 throw new IllegalStateException("no supported iface type");
162             }
163         }
164 
165         public void visitTypes(int depth, Consumer<IfaceType> ifaceTypeNodeConsumer) {
166             ifaceTypes.forEach(t -> t.visitTypes(depth + 1, ifaceTypeNodeConsumer));
167             ifaceTypeNodeConsumer.accept(this);
168         }
169 
170 
171         public IfaceType struct(String name, Consumer<IfaceType> parentSchemaNodeConsumer) {
172             parentSchemaNodeConsumer.accept(addIfaceTypeNode(new Struct(this, (Class<MappableIface>) MapperUtil.typeOf(iface, name))));
173             return this;
174         }
175 
176         public IfaceType union(String name, Consumer<IfaceType> parentSchemaNodeConsumer) {
177             parentSchemaNodeConsumer.accept(addIfaceTypeNode(new Union(this, (Class<MappableIface>) MapperUtil.typeOf(iface, name))));
178             return this;
179         }
180 
181         public IfaceType field(String name) {
182             var key = AccessorInfo.Key.of(iface, name);
183             var typeOf = MapperUtil.typeOf(iface, name);
184             addField(MapperUtil.isMemorySegment(typeOf)
185                     ? new FieldNode.AddressField(this, key, (Class<MemorySegment>) typeOf, name)
186                     : MapperUtil.isMappableIface(typeOf)
187                     ? new FieldNode.IfaceField(this, key, this.getChild(typeOf), name)
188                     : new FieldNode.PrimitiveField(this, key, typeOf, name));
189             return this;
190         }
191 
192         public IfaceType atomic(String name) {
193             addField(new FieldNode.AtomicField(this, AccessorInfo.Key.of(iface, name), MapperUtil.typeOf(iface, name), name));
194             return this;
195         }
196 
197         public IfaceType pad(int len) {
198             addField(new SchemaNode.Padding(this, len));
199             return this;
200         }
201 
202         public IfaceType field(String name, Consumer<IfaceType> parentSchemaNodeConsumer) {
203             AccessorInfo.Key fieldKey = AccessorInfo.Key.of(iface, name);
204             Class<MappableIface> fieldType = (Class<MappableIface>) MapperUtil.typeOf(iface, name);
205             IfaceType structOrUnion = MapperUtil.isStruct(fieldType) ? new Struct(this, fieldType) : new Union(this, fieldType);
206             addIfaceTypeNode(structOrUnion);
207             addField(new FieldNode.IfaceField(this, fieldKey, structOrUnion, name));
208             parentSchemaNodeConsumer.accept(structOrUnion);
209             return this;
210         }
211 
212         public IfaceType fields(String name1, String name2, Consumer<IfaceType> parentSchemaNodeConsumer) {
213             AccessorInfo.Key fieldKey1 = AccessorInfo.Key.of(iface, name1);
214             AccessorInfo.Key fieldKey2 = AccessorInfo.Key.of(iface, name2);
215             if (!fieldKey1.equals(fieldKey2)) {
216                 throw new IllegalStateException("fields " + name1 + " and " + name2 + " have different keys");
217             }
218             Class<MappableIface> structOrUnionType = (Class<MappableIface>) MapperUtil.typeOf(iface, name1);
219             Class<?> fieldTypeCheck = MapperUtil.typeOf(iface, name2);
220             if (!structOrUnionType.equals(fieldTypeCheck)) {
221                 throw new IllegalStateException("fields " + name1 + " and " + name2 + " have different types");
222             }
223             IfaceType ifaceType = MapperUtil.isStruct(iface)
224                     ? new Struct(this, structOrUnionType)
225                     : new Union(this, structOrUnionType);
226             addIfaceTypeNode(ifaceType);
227             addField(new FieldNode.IfaceField(this, fieldKey1, ifaceType, name1));
228             addField(new FieldNode.IfaceField(this, fieldKey2, ifaceType, name2));
229 
230             parentSchemaNodeConsumer.accept(ifaceType);
231             return this;
232         }
233 
234         public IfaceType fields(String... names) {
235             for (var name : names) {
236                 field(name);
237             }
238             return this;
239         }
240 
241         public IfaceType array(String name, int len) {
242             AccessorInfo.Key arrayKey = AccessorInfo.Key.of(iface, name);
243             var typeof = MapperUtil.typeOf(iface, name);
244             addField(arrayKey.valueType().equals(ValueType.INTERFACE)
245                     ? new FieldNode.IfaceFixedArray(this, arrayKey, this.getChild(typeof), name, len)
246                     : new FieldNode.PrimitiveFixedArray(this, arrayKey, typeof, name, len));
247             return this;
248         }
249 
250         public IfaceType array(String name, int len, Consumer<IfaceType> parentFieldConsumer) {
251             AccessorInfo.Key arrayKey = AccessorInfo.Key.of(iface, name);
252             Class<MappableIface> structOrUnionType = (Class<MappableIface>) MapperUtil.typeOf(iface, name);
253             IfaceType ifaceType = MapperUtil.isStruct(iface)
254                     ? new Struct(this, structOrUnionType)
255                     : new Union(this, structOrUnionType);
256             parentFieldConsumer.accept(ifaceType);
257             addIfaceTypeNode(ifaceType);
258             addField(new FieldNode.IfaceFixedArray(this, arrayKey, ifaceType, name, len));
259             return this;
260         }
261 
262         private IfaceType fieldControlledArray(String name, List<FieldNode.ArrayLen> arrayLenFields, int stride) {
263             AccessorInfo.Key arrayKey = AccessorInfo.Key.of(iface, name);
264             var typeOf = MapperUtil.typeOf(iface, name);
265             addField(arrayKey.valueType().equals(ValueType.INTERFACE)
266                     ? new FieldNode.IfaceFieldControlledArray(this, arrayKey, this.getChild(typeOf), name, arrayLenFields, stride)
267                     : new FieldNode.PrimitiveFieldControlledArray(this, arrayKey, typeOf, name, arrayLenFields, stride));
268             return this;
269         }
270 
271         public static class ArrayBuildState {
272             IfaceType ifaceType;
273             List<FieldNode.ArrayLen> arrayLenFields;
274             int padding = 0;
275             int stride = 1;
276 
277             public IfaceType array(String name) {
278                 return ifaceType.fieldControlledArray(name, arrayLenFields, stride);
279             }
280 
281             public ArrayBuildState stride(int stride) {
282                 this.stride = stride;
283                 return this;
284             }
285 
286             public ArrayBuildState pad(int padding) {
287                 this.padding = padding;
288                 var paddingField = new SchemaNode.Padding(ifaceType, padding);
289                 ifaceType.addField(paddingField);
290                 return this;
291             }
292 
293             public IfaceType array(String name, Consumer<IfaceType> parentFieldConsumer) {
294                 Class<MappableIface> arrayType = (Class<MappableIface>) MapperUtil.typeOf(this.ifaceType.iface, name);
295                 IfaceType ifaceType = MapperUtil.isStruct(arrayType)
296                         ? new Struct(this.ifaceType, arrayType)
297                         : new Union(this.ifaceType, arrayType);
298                 parentFieldConsumer.accept(ifaceType);
299                 this.ifaceType.addIfaceTypeNode(ifaceType);
300                 this.ifaceType.fieldControlledArray(name, arrayLenFields, stride);
301 
302 
303                 return this.ifaceType;
304             }
305 
306             ArrayBuildState(IfaceType ifaceType, List<FieldNode.ArrayLen> arrayLenFields) {
307                 this.ifaceType = ifaceType;
308                 this.arrayLenFields = arrayLenFields;
309             }
310         }
311 
312         public ArrayBuildState buildArray() {
313             return new ArrayBuildState(this, null);
314         }
315 
316         public ArrayBuildState arrayLen(String... arrayLenFieldNames) {
317             List<FieldNode.ArrayLen> arrayLenFields = new ArrayList<>();
318             Arrays.stream(arrayLenFieldNames).forEach(arrayLenFieldName -> {
319                 var arrayLenField = new FieldNode.ArrayLen(this, AccessorInfo.Key.of(iface, arrayLenFieldName), MapperUtil.typeOf(iface, arrayLenFieldName), arrayLenFieldName);
320                 addField(arrayLenField);
321                 arrayLenFields.add(arrayLenField);
322             });
323             return new ArrayBuildState(this, arrayLenFields);
324         }
325 
326 
327         public void toText(String indent, Consumer<String> stringConsumer) {
328             stringConsumer.accept(indent);
329             if (MapperUtil.isUnion(iface)) {
330                 stringConsumer.accept("union");
331             } else if (MapperUtil.isStructOrBuffer(iface)) {
332                 stringConsumer.accept("struct");
333             } else {
334                 throw new IllegalStateException("Oh my ");
335             }
336             stringConsumer.accept(" " + iface + "{");
337             stringConsumer.accept("\n");
338             ifaceTypes.forEach(ifaceType -> {
339                 ifaceType.toText(indent + " TYPE: ", stringConsumer);
340                 stringConsumer.accept("\n");
341             });
342             fields.forEach(field -> {
343                 field.toText(indent + " FIELD: ", stringConsumer);
344                 stringConsumer.accept("\n");
345             });
346 
347             stringConsumer.accept(indent);
348             stringConsumer.accept("}");
349         }
350 
351         public static final class Struct extends IfaceType {
352             Struct(IfaceType parent, Class<MappableIface> type) {
353                 super(parent, type);
354             }
355         }
356 
357         public static final class Union extends IfaceType {
358             Union(IfaceType parent, Class<MappableIface> type) {
359                 super(parent, type);
360             }
361         }
362     }
363 
364     public static abstract sealed class FieldNode
365             permits FieldNode.AddressField, FieldNode.AbstractIfaceField, SchemaNode.Padding, FieldNode.AbstractPrimitiveField {
366         public IfaceType parent;
367         public final AccessorInfo.Key key;
368         public final String name;
369 
370         FieldNode(IfaceType parent, AccessorInfo.Key key, String name) {
371             this.parent = parent;
372             this.key = key;
373             this.name = name;
374         }
375 
376         public abstract void toText(String indent, Consumer<String> stringConsumer);
377 
378 
379         public static final class AddressField extends FieldNode {
380             Class<MemorySegment> type;
381 
382             AddressField(IfaceType parent, AccessorInfo.Key key, Class<MemorySegment> type, String name) {
383                 super(parent, key, name);
384                 this.type = type;
385             }
386 
387             @Override
388             public void toText(String indent, Consumer<String> stringConsumer) {
389                 stringConsumer.accept(indent + "address " + key + ":" + type);
390             }
391         }
392 
393         public static abstract sealed class AbstractPrimitiveField extends FieldNode
394                 permits ArrayLen, AtomicField, PrimitiveArray, PrimitiveField {
395             public Class<?> type;
396 
397             AbstractPrimitiveField(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name) {
398                 super(parent, key, name);
399                 this.type = type;
400             }
401         }
402 
403         public static final class ArrayLen extends AbstractPrimitiveField {
404             ArrayLen(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name) {
405                 super(parent, key, type, name);
406             }
407 
408             @Override
409             public void toText(String indent, Consumer<String> stringConsumer) {
410                 stringConsumer.accept(indent + "arrayLen " + key + ":" + type);
411             }
412         }
413 
414         public static final class AtomicField extends AbstractPrimitiveField {
415             AtomicField(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name) {
416                 super(parent, key, type, name);
417             }
418 
419             @Override
420             public void toText(String indent, Consumer<String> stringConsumer) {
421                 stringConsumer.accept(indent + "atomic " + key + ":" + type);
422             }
423         }
424 
425 
426         public static final class PrimitiveField extends AbstractPrimitiveField {
427             PrimitiveField(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name) {
428                 super(parent, key, type, name);
429 
430             }
431 
432             @Override
433             public void toText(String indent, Consumer<String> stringConsumer) {
434                 stringConsumer.accept(indent + "primitive field " + key + ":" + type);
435             }
436         }
437 
438 
439         public abstract static sealed class PrimitiveArray extends AbstractPrimitiveField permits PrimitiveFieldControlledArray, PrimitiveFixedArray {
440             PrimitiveArray(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name) {
441                 super(parent, key, type, name);
442             }
443         }
444 
445 
446         public static final class PrimitiveFixedArray extends PrimitiveArray {
447             public int len;
448 
449             PrimitiveFixedArray(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name, int len) {
450                 super(parent, key, type, name);
451                 this.len = len;
452             }
453 
454             @Override
455             public void toText(String indent, Consumer<String> stringConsumer) {
456                 stringConsumer.accept(indent + "primitive array [" + len + "]");
457             }
458         }
459 
460         public static final class PrimitiveFieldControlledArray extends PrimitiveArray {
461             List<ArrayLen> arrayLenFields;
462             int stride;
463             int contributingDims;
464 
465             PrimitiveFieldControlledArray(IfaceType parent, AccessorInfo.Key key, Class<?> type, String name, List<ArrayLen> arrayLenFields, int stride) {
466                 super(parent, key, type, name);
467                 this.arrayLenFields = arrayLenFields;
468                 this.stride = stride;
469                 this.contributingDims = arrayLenFields.size();
470             }
471 
472             @Override
473             public void toText(String indent, Consumer<String> stringConsumer) {
474                 stringConsumer.accept(indent + name + "[" + key + ":" + type + "] where len defined by " + arrayLenFields);
475             }
476         }
477 
478         public static abstract sealed class AbstractIfaceField extends FieldNode
479                 permits FieldNode.IfaceArray, FieldNode.IfaceField {
480             public IfaceType ifaceType;
481 
482             AbstractIfaceField(IfaceType parent, AccessorInfo.Key key, IfaceType ifaceType, String name) {
483                 super(parent, key, name);
484                 this.ifaceType = ifaceType;
485             }
486         }
487 
488 
489         public static final class IfaceField extends AbstractIfaceField {
490 
491             IfaceField(IfaceType parent, AccessorInfo.Key key, IfaceType ifaceType, String name) {
492                 super(parent, key, ifaceType, name);
493 
494             }
495 
496             @Override
497             public void toText(String indent, Consumer<String> stringConsumer) {
498                 stringConsumer.accept(indent + "mappable field " + key + ":" + ifaceType.iface);
499             }
500         }
501 
502 
503         public abstract static sealed class IfaceArray extends AbstractIfaceField permits IfaceFieldControlledArray, IfaceFixedArray {
504             IfaceArray(IfaceType parent, AccessorInfo.Key key, IfaceType ifaceType, String name) {
505                 super(parent, key, ifaceType, name);
506             }
507         }
508 
509         public static final class IfaceFixedArray extends IfaceArray {
510             public int len;
511 
512             IfaceFixedArray(IfaceType parent, AccessorInfo.Key key, IfaceType ifaceType, String name, int len) {
513                 super(parent, key, ifaceType, name);
514                 this.len = len;
515             }
516 
517             @Override
518             public void toText(String indent, Consumer<String> stringConsumer) {
519                 stringConsumer.accept(indent + "array [" + len + "]");
520             }
521         }
522 
523         public static final class IfaceFieldControlledArray extends IfaceArray {
524             List<ArrayLen> arrayLenFields;
525             int stride;
526             int contributingDims;
527 
528             IfaceFieldControlledArray(IfaceType parent, AccessorInfo.Key key, IfaceType ifaceType, String name, List<ArrayLen> arrayLenFields, int stride) {
529                 super(parent, key, ifaceType, name);
530                 this.arrayLenFields = arrayLenFields;
531                 this.stride = stride;
532                 this.contributingDims = arrayLenFields.size();
533             }
534 
535             @Override
536             public void toText(String indent, Consumer<String> stringConsumer) {
537                 stringConsumer.accept(indent + name + "[" + key + ":" + ifaceType.iface + "] where len defined by " + arrayLenFields);
538             }
539         }
540 
541 
542     }
543 }