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 }