Pattern matching and defaults for structs
[panamaz] / src / template / Memory.java
1 /*
2  * Copyright (C) 2020 Michael Zucchi
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package api;
19
20 import java.lang.invoke.*;
21 import java.lang.ref.Cleaner;
22 import jdk.incubator.foreign.*;
23 import static jdk.incubator.foreign.ValueLayout.*;
24
25 import java.util.AbstractList;
26 import java.util.function.Function;
27 import java.util.function.BiFunction;
28
29 /**
30  * A utility for memory operations including a stack allocator.
31  * <p>
32  * The stack allocator works like this
33  * <pre>
34  * try (Frame f = Memory.createFrame()) {
35  *              MemorySegment a = f.allocate(size);
36  * }
37  * </pre>
38  * Any memory allocated is freed when the frame is closed.
39  * <p>
40  * This is MUCH faster than using MemorySegment.allocateNative().
41  */
42 public class Memory {
43
44         // probably should be INT8 INT16, etc
45         public  static final OfByte BYTE = JAVA_BYTE;
46         public static final OfShort SHORT = JAVA_SHORT.withBitAlignment(16);
47         public static final OfInt INT = JAVA_INT.withBitAlignment(32);
48         public static final OfLong LONG = JAVA_LONG.withBitAlignment(64);
49         public static final OfFloat FLOAT = JAVA_FLOAT.withBitAlignment(32);
50         public static final OfDouble DOUBLE = JAVA_DOUBLE.withBitAlignment(64);
51         public static final OfAddress POINTER = ADDRESS.withBitAlignment(64);
52
53         static final ResourceScope sharedScope = ResourceScope.newSharedScope(); // cleaner?
54         static final MemorySegment NULL = MemorySegment.ofAddress(MemoryAddress.NULL, 1, ResourceScope.globalScope());
55
56         public static ResourceScope sharedScope() {
57                 return sharedScope;
58         }
59
60         public static MethodHandle downcall(String name, FunctionDescriptor desc) {
61                 return SymbolLookup.loaderLookup().lookup(name)
62                         .map(sym -> CLinker.systemCLinker().downcallHandle(sym, desc))
63                         .orElse(null);
64         }
65
66         public static MethodHandle downcall(NativeSymbol sym, FunctionDescriptor desc) {
67                 return CLinker.systemCLinker().downcallHandle(sym, desc);
68         }
69
70         public static MethodHandle downcall(String name, MemoryAddress sym, FunctionDescriptor desc, ResourceScope scope) {
71                 return sym != MemoryAddress.NULL
72                         ? CLinker.systemCLinker().downcallHandle(NativeSymbol.ofAddress(name, sym, scope), desc)
73                         : null;
74         }
75
76         static final MethodHandles.Lookup lookup = MethodHandles.lookup();
77
78         public static NativeSymbol upcall(Object instance, FunctionDescriptor desc, ResourceScope scope) {
79                 try {
80                         java.lang.reflect.Method m = instance.getClass().getMethods()[0];
81                         MethodHandle handle = lookup.findVirtual(instance.getClass(), "call", MethodType.methodType(m.getReturnType(), m.getParameterTypes()))
82                                               .bindTo(instance);
83                         return CLinker.systemCLinker().upcallStub(handle, desc, scope);
84                 } catch (Throwable t) {
85                         throw new AssertionError(t);
86                 }
87         }
88
89
90         public static NativeSymbol upcall(Object instance, String method, String signature, FunctionDescriptor desc, ResourceScope scope) {
91                 try {
92                         MethodHandle handle = lookup.findVirtual(instance.getClass(), method, MethodType.fromMethodDescriptorString(signature, Memory.class.getClassLoader()))
93                                               .bindTo(instance);
94                         return CLinker.systemCLinker().upcallStub(handle, desc, scope);
95                 } catch (Throwable t) {
96                         throw new AssertionError(t);
97                 }
98         }
99
100         static final ResourceScope scope = ResourceScope.newSharedScope(Cleaner.create());
101         private static final ThreadLocal<Stack> stacks = ThreadLocal.withInitial(() -> new Stack(scope));
102
103         public static Frame createFrame() {
104                 return stacks.get().createFrame();
105         }
106
107         static class Stack {
108
109                 private final MemorySegment stack;
110                 private long sp;
111                 private Thread thread = Thread.currentThread();
112
113                 Stack(ResourceScope scope) {
114                         stack = MemorySegment.allocateNative(4096, 4096, scope);
115                         sp = 4096;
116                 }
117
118                 Frame createFrame() {
119
120                         return new Frame() {
121                                 private final long tos = sp;
122                                 private Thread self = thread;
123                                 private ResourceScope scope;
124
125                                 @Override
126                                 public MemorySegment allocate(long size, long alignment) {
127                                         if (self != Thread.currentThread())
128                                                 throw new IllegalStateException();
129                                         if (alignment != Long.highestOneBit(alignment))
130                                                 throw new IllegalArgumentException();
131                                         if (sp >= size) {
132                                                 sp = (sp - size) & ~(alignment - 1);
133                                                 return stack.asSlice(sp, size).fill((byte)0);
134                                         } else {
135                                                 if (scope == null)
136                                                         scope = ResourceScope.newConfinedScope();
137                                                 return MemorySegment.allocateNative(size, alignment, scope);
138                                         }
139                                 }
140
141                                 @Override
142                                 public void close() {
143                                         sp = tos;
144                                         self = null;
145                                         if (scope != null) {
146                                                 scope.close();
147                                                 scope = null;
148                                         }
149                                 }
150                         };
151                 }
152         }
153
154         public interface Addressable {
155                 MemoryAddress address();
156                 ResourceScope scope();
157         }
158
159         public record FunctionPointer<T>(NativeSymbol symbol, T function) {
160         }
161
162         public static MemoryAddress address(jdk.incubator.foreign.Addressable v) {
163                 return v != null ? v.address() : MemoryAddress.NULL;
164         }
165
166         public static MemoryAddress address(Memory.Addressable v) {
167                 return v != null ? v.address() : MemoryAddress.NULL;
168         }
169
170         public static <T> MemoryAddress address(FunctionPointer<T> v) {
171                 return v != null ? v.symbol().address() : MemoryAddress.NULL;
172         }
173
174         // hmm do i want this or not?
175         // -> added 'type safety'
176         // -> load of crap to be written
177         public static class ByteArray extends AbstractList<Byte> implements Memory.Addressable {
178                 final MemorySegment segment;
179
180                 private ByteArray(MemorySegment segment) {
181                         this.segment = segment;
182                 }
183
184                 public static ByteArray create(MemorySegment segment) {
185                         return new ByteArray(segment);
186                 }
187
188                 public static ByteArray createArray(MemoryAddress address, long length, ResourceScope scope) {
189                         return create(MemorySegment.ofAddress(address, length, scope));
190                 }
191
192                 public static ByteArray createArray(long length, SegmentAllocator alloc) {
193                         return create(alloc.allocateArray(Memory.BYTE, length));
194                 }
195
196                 public static ByteArray create(String value, SegmentAllocator alloc) {
197                         return create(alloc.allocateUtf8String(value));
198                 }
199
200                 public static ByteArray create(String value, ResourceScope scope) {
201                         return create(SegmentAllocator.nativeAllocator(scope).allocateUtf8String(value));
202                 }
203
204                 public final MemoryAddress address() {
205                         return segment.address();
206                 }
207
208                 public final ResourceScope scope() {
209                         return segment.scope();
210                 }
211
212                 @Override
213                 public int size() {
214                         return (int)length();
215                 }
216
217                 @Override
218                 public Byte get(int index) {
219                         return getAtIndex(index);
220                 }
221
222                 @Override
223                 public Byte set(int index, Byte value) {
224                         byte old = getAtIndex(index);
225                         setAtIndex(index, value);
226                         return old;
227                 }
228
229                 public long length() {
230                         return segment.byteSize() / Memory.BYTE.byteSize();
231                 }
232
233                 public byte getAtIndex(long index) {
234                         return (byte)segment.get(Memory.BYTE, index);
235                 }
236
237                 public void setAtIndex(long index, byte value) {
238                         segment.set(Memory.BYTE, index, value);
239                 }
240         }
241
242         public static class ShortArray extends AbstractList<Short> implements Memory.Addressable {
243                 final MemorySegment segment;
244
245                 private ShortArray(MemorySegment segment) {
246                         this.segment = segment;
247                 }
248
249                 public static ShortArray create(MemorySegment segment) {
250                         return new ShortArray(segment);
251                 }
252
253                 public static ShortArray createArray(MemoryAddress address, long length, ResourceScope scope) {
254                         return create(MemorySegment.ofAddress(address, length, scope));
255                 }
256
257                 public static ShortArray createArray(long length, SegmentAllocator alloc) {
258                         return create(alloc.allocateArray(Memory.SHORT, length));
259                 }
260
261                 public static ShortArray create(SegmentAllocator alloc, short... values) {
262                         return create(alloc.allocateArray(Memory.SHORT, values));
263                 }
264
265                 public final MemoryAddress address() {
266                         return segment.address();
267                 }
268
269                 public final ResourceScope scope() {
270                         return segment.scope();
271                 }
272
273                 @Override
274                 public int size() {
275                         return (int)length();
276                 }
277
278                 @Override
279                 public Short get(int index) {
280                         return getAtIndex(index);
281                 }
282
283                 @Override
284                 public Short set(int index, Short value) {
285                         short old = getAtIndex(index);
286                         setAtIndex(index, value);
287                         return old;
288                 }
289
290                 public long length() {
291                         return segment.byteSize() / Memory.SHORT.byteSize();
292                 }
293
294                 public short getAtIndex(long index) {
295                         return segment.getAtIndex(Memory.SHORT, index);
296                 }
297
298                 public void setAtIndex(long index, short value) {
299                         segment.setAtIndex(Memory.SHORT, index, value);
300                 }
301         }
302
303         public static class IntArray extends AbstractList<Integer> implements Memory.Addressable {
304                 final MemorySegment segment;
305
306                 private IntArray(MemorySegment segment) {
307                         this.segment = segment;
308                 }
309
310                 public static IntArray create(MemorySegment segment) {
311                         return new IntArray(segment);
312                 }
313
314                 public static IntArray createArray(MemoryAddress address, long length, ResourceScope scope) {
315                         return create(MemorySegment.ofAddress(address, length, scope));
316                 }
317
318                 public static IntArray createArray(long length, SegmentAllocator alloc) {
319                         return create(alloc.allocateArray(Memory.INT, length));
320                 }
321
322                 public static IntArray create(SegmentAllocator alloc, int... values) {
323                         return create(alloc.allocateArray(Memory.INT, values));
324                 }
325
326                 public final MemoryAddress address() {
327                         return segment.address();
328                 }
329
330                 public final ResourceScope scope() {
331                         return segment.scope();
332                 }
333
334                 @Override
335                 public int size() {
336                         return (int)length();
337                 }
338
339                 @Override
340                 public Integer get(int index) {
341                         return getAtIndex(index);
342                 }
343
344                 @Override
345                 public Integer set(int index, Integer value) {
346                         int old = getAtIndex(index);
347                         setAtIndex(index, value);
348                         return old;
349                 }
350
351                 public long length() {
352                         return segment.byteSize() / Memory.INT.byteSize();
353                 }
354
355                 public int getAtIndex(long index) {
356                         return segment.getAtIndex(Memory.INT, index);
357                 }
358
359                 public void setAtIndex(long index, int value) {
360                         segment.setAtIndex(Memory.INT, index, value);
361                 }
362         }
363
364         public static class LongArray extends AbstractList<Long> implements Memory.Addressable {
365                 final MemorySegment segment;
366
367                 public LongArray(MemorySegment segment) {
368                         this.segment = segment;
369                 }
370
371                 public static LongArray create(MemorySegment segment) {
372                         return new LongArray(segment);
373                 }
374
375                 public static LongArray createArray(MemoryAddress address, long length, ResourceScope scope) {
376                         return create(MemorySegment.ofAddress(address, length, scope));
377                 }
378
379                 public static LongArray createArray(long length, SegmentAllocator alloc) {
380                         return create(alloc.allocateArray(Memory.LONG, length));
381                 }
382
383                 public static LongArray create(SegmentAllocator alloc, long... values) {
384                         return create(alloc.allocateArray(Memory.LONG, values));
385                 }
386
387                 public final MemoryAddress address() {
388                         return segment.address();
389                 }
390
391                 public final ResourceScope scope() {
392                         return segment.scope();
393                 }
394
395                 @Override
396                 public int size() {
397                         return (int)length();
398                 }
399
400                 @Override
401                 public Long get(int index) {
402                         return getAtIndex(index);
403                 }
404
405                 @Override
406                 public Long set(int index, Long value) {
407                         long old = getAtIndex(index);
408                         setAtIndex(index, value);
409                         return old;
410                 }
411
412                 public long length() {
413                         return segment.byteSize() / Memory.LONG.byteSize();
414                 }
415
416                 public long getAtIndex(long index) {
417                         return segment.getAtIndex(Memory.LONG, index);
418                 }
419
420                 public void setAtIndex(long index, long value) {
421                         segment.setAtIndex(Memory.LONG, index, value);
422                 }
423         }
424
425         public static class FloatArray extends AbstractList<Float> implements Memory.Addressable {
426                 final MemorySegment segment;
427
428                 private FloatArray(MemorySegment segment) {
429                         this.segment = segment;
430                 }
431
432                 public static FloatArray create(MemorySegment segment) {
433                         return new FloatArray(segment);
434                 }
435
436                 public static FloatArray createArray(MemoryAddress address, long length, ResourceScope scope) {
437                         return create(MemorySegment.ofAddress(address, length, scope));
438                 }
439
440                 public static FloatArray createArray(long length, SegmentAllocator alloc) {
441                         return create(alloc.allocateArray(Memory.FLOAT, length));
442                 }
443
444                 public static FloatArray create(SegmentAllocator alloc, float... values) {
445                         return create(alloc.allocateArray(Memory.FLOAT, values));
446                 }
447
448                 public final MemoryAddress address() {
449                         return segment.address();
450                 }
451
452                 public final ResourceScope scope() {
453                         return segment.scope();
454                 }
455
456                 @Override
457                 public int size() {
458                         return (int)length();
459                 }
460
461                 @Override
462                 public Float get(int index) {
463                         return getAtIndex(index);
464                 }
465
466                 @Override
467                 public Float set(int index, Float value) {
468                         float old = getAtIndex(index);
469                         setAtIndex(index, value);
470                         return old;
471                 }
472
473                 public long length() {
474                         return segment.byteSize() / Memory.FLOAT.byteSize();
475                 }
476
477                 public float getAtIndex(long index) {
478                         return segment.getAtIndex(Memory.FLOAT, index);
479                 }
480
481                 public void setAtIndex(long index, float value) {
482                         segment.setAtIndex(Memory.FLOAT, index, value);
483                 }
484         }
485
486         public static class DoubleArray extends AbstractList<Double> implements Memory.Addressable {
487                 final MemorySegment segment;
488
489                 private DoubleArray(MemorySegment segment) {
490                         this.segment = segment;
491                 }
492
493                 public static DoubleArray create(MemorySegment segment) {
494                         return new DoubleArray(segment);
495                 }
496
497                 public static DoubleArray createArray(MemoryAddress address, long length, ResourceScope scope) {
498                         return create(MemorySegment.ofAddress(address, length, scope));
499                 }
500
501                 public static DoubleArray createArray(long length, SegmentAllocator alloc) {
502                         return create(alloc.allocateArray(Memory.DOUBLE, length));
503                 }
504
505                 public static DoubleArray create(SegmentAllocator alloc, double... values) {
506                         return create(alloc.allocateArray(Memory.DOUBLE, values));
507                 }
508
509                 public final MemoryAddress address() {
510                         return segment.address();
511                 }
512
513                 public final ResourceScope scope() {
514                         return segment.scope();
515                 }
516
517                 @Override
518                 public int size() {
519                         return (int)length();
520                 }
521
522                 @Override
523                 public Double get(int index) {
524                         return getAtIndex(index);
525                 }
526
527                 @Override
528                 public Double set(int index, Double value) {
529                         double old = getAtIndex(index);
530                         setAtIndex(index, value);
531                         return old;
532                 }
533
534                 public long length() {
535                         return segment.byteSize() / Memory.DOUBLE.byteSize();
536                 }
537
538                 public double getAtIndex(long index) {
539                         return segment.getAtIndex(Memory.DOUBLE, index);
540                 }
541
542                 public void setAtIndex(long index, double value) {
543                         segment.setAtIndex(Memory.DOUBLE, index, value);
544                 }
545         }
546
547         public static class PointerArray extends AbstractList<MemoryAddress> implements Memory.Addressable {
548                 final MemorySegment segment;
549
550                 private PointerArray(MemorySegment segment) {
551                         this.segment = segment;
552                 }
553
554                 public static PointerArray create(MemorySegment segment) {
555                         return new PointerArray(segment);
556                 }
557
558                 public static PointerArray createArray(MemoryAddress address, long length, ResourceScope scope) {
559                         return create(MemorySegment.ofAddress(address, length, scope));
560                 }
561
562                 public static PointerArray createArray(long length, SegmentAllocator alloc) {
563                         return create(alloc.allocateArray(Memory.POINTER, length));
564                 }
565
566                 public static PointerArray create(Frame alloc, MemoryAddress... values) {
567                         return create(alloc.allocateArray(Memory.POINTER, values));
568                 }
569
570                 public final MemoryAddress address() {
571                         return segment.address();
572                 }
573
574                 public final ResourceScope scope() {
575                         return segment.scope();
576                 }
577
578                 @Override
579                 public int size() {
580                         return (int)length();
581                 }
582
583                 @Override
584                 public MemoryAddress get(int index) {
585                         return getAtIndex(index);
586                 }
587
588                 @Override
589                 public MemoryAddress set(int index, MemoryAddress value) {
590                         MemoryAddress old = getAtIndex(index);
591                         setAtIndex(index, value);
592                         return old;
593                 }
594
595                 public long length() {
596                         return segment.byteSize() / Memory.POINTER.byteSize();
597                 }
598
599                 public MemoryAddress getAtIndex(long index) {
600                         return segment.getAtIndex(Memory.POINTER, index);
601                 }
602
603                 public void setAtIndex(long index, MemoryAddress value) {
604                         segment.setAtIndex(Memory.POINTER, index, value);
605                 }
606         }
607
608         // This needs a separate scope from the array itself
609         public static class HandleArray<T extends Memory.Addressable> extends AbstractList<T> implements Memory.Addressable {
610                 final MemorySegment segment;
611                 final ResourceScope scope;
612                 BiFunction<MemoryAddress,ResourceScope,T> create;
613
614                 private HandleArray(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
615                         this.segment = segment;
616                         this.create = create;
617                         this.scope = scope;
618                 }
619
620                 public static <T extends Memory.Addressable> HandleArray<T> create(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create) {
621                         return new HandleArray<>(segment, create, segment.scope());
622                 }
623
624                 public static <T extends Memory.Addressable> HandleArray<T> create(MemorySegment segment, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
625                         return new HandleArray<>(segment, create, scope);
626                 }
627
628                 public static <T extends Memory.Addressable> HandleArray<T> createArray(long size, SegmentAllocator alloc, BiFunction<MemoryAddress,ResourceScope,T> create) {
629                         return create(alloc.allocateArray(Memory.POINTER, size), create);
630                 }
631
632                 public static <T extends Memory.Addressable> HandleArray<T> createArray(long size, SegmentAllocator alloc, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
633                         return create(alloc.allocateArray(Memory.POINTER, size), create, scope);
634                 }
635
636                 public static <T extends Memory.Addressable> HandleArray<T> createArray(MemoryAddress address, long size, BiFunction<MemoryAddress,ResourceScope,T> create, ResourceScope scope) {
637                         return create(MemorySegment.ofAddress(address, size * Memory.POINTER.byteSize(), scope), create);
638                 }
639
640                 @Override
641                 public final MemoryAddress address() {
642                         return segment.address();
643                 }
644
645                 public final ResourceScope scope() {
646                         return segment.scope();
647                 }
648
649                 @Override
650                 public int size() {
651                         return (int)length();
652                 }
653
654                 @Override
655                 public T get(int index) {
656                         return getAtIndex(index);
657                 }
658
659                 @Override
660                 public T set(int index, T value) {
661                         T old = getAtIndex(index);
662                         setAtIndex(index, value);
663                         return old;
664                 }
665
666                 public long length() {
667                         return segment.byteSize() / Memory.POINTER.byteSize();
668                 }
669
670                 public T getAtIndex(long index) {
671                         MemoryAddress ptr = segment.getAtIndex(Memory.POINTER, index);
672                         return ptr != null ? create.apply(ptr, scope) : null;
673                 }
674
675                 public void setAtIndex(long index, T value) {
676                         segment.setAtIndex(Memory.POINTER, index, value != null ? value.address() : MemoryAddress.NULL);
677                 }
678         }
679
680 }