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