Skip to content

Commit f80dd9f

Browse files
authored
Demonstrate usage of foreign arrow function (#9150)
Including arrow language in the distribution by default. Added a basic example for creating an Arrow array. Making sure that memory layout agrees with Arrow specification (padding, continuous allocation of memory chunks). Related to #9118. This should unblock work on allowing serialization/deserialization to/from Parquet but I'd like to delay it to a follow up ticket as it is going to be a significant amount of specialized work.
1 parent a3bf5a0 commit f80dd9f

File tree

14 files changed

+772
-343
lines changed

14 files changed

+772
-343
lines changed

build.sbt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2216,8 +2216,7 @@ lazy val `engine-runner` = project
22162216
"com.sun.imageio",
22172217
"com.sun.jna.internal.Cleaner",
22182218
"com.sun.jna.Structure$FFIType",
2219-
"akka.http",
2220-
"org.enso.interpreter.arrow.util.MemoryUtil"
2219+
"akka.http"
22212220
)
22222221
)
22232222
.dependsOn(assembly)
@@ -3030,7 +3029,8 @@ buildEngineDistribution := {
30303029
val _ = (`engine-runner` / assembly).value
30313030
updateLibraryManifests.value
30323031
val modulesToCopy = componentModulesPaths.value.map(_.data)
3033-
val engineModules = Seq(file("runtime.jar"))
3032+
val arrow = Seq((`runtime-language-arrow` / Compile / packageBin).value)
3033+
val engineModules = Seq(file("runtime.jar")) ++ arrow
30343034
val root = engineDistributionRoot.value
30353035
val log = streams.value.log
30363036
val cacheFactory = streams.value.cacheStoreFactory

distribution/lib/Standard/Base/0.0.0-dev/src/Polyglot.enso

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Polyglot
2828
Reads the element in a given polyglot array object.
2929

3030
Arguments:
31+
- array: The array on which to perform the operation.
3132
- index: The index to get the element from.
3233
read_array_element : Any -> Integer -> Any
3334
read_array_element array index = @Builtin_Method "Polyglot.read_array_element"

engine/runtime-language-arrow/src/main/java/org/enso/interpreter/arrow/ArrowParser.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public record Result(PhysicalLayout physicalLayout, LogicalLayout logicalLayout,
1212

1313
public static Result parse(Source source) {
1414
String src = source.getCharacters().toString();
15-
Matcher m = ARRAY_PATTERN.matcher(src);
15+
Matcher m = NEW_ARRAY_CONSTR.matcher(src);
1616
if (m.find()) {
1717
try {
1818
var layout = LogicalLayout.valueOf(m.group(1));
@@ -36,8 +36,8 @@ public static Result parse(Source source) {
3636
return null;
3737
}
3838

39-
private static final Pattern ARRAY_PATTERN = Pattern.compile("new\\[(.+)\\]");
40-
private static final Pattern CAST_PATTERN = Pattern.compile("cast\\[(.+)\\]");
39+
private static final Pattern NEW_ARRAY_CONSTR = Pattern.compile("^new\\[(.+)\\]$");
40+
private static final Pattern CAST_PATTERN = Pattern.compile("^cast\\[(.+)\\]$");
4141

4242
public enum Mode {
4343
Allocate,

engine/runtime-language-arrow/src/main/java/org/enso/interpreter/arrow/runtime/ArrowCastToFixedSizeArrayFactory.java

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313
import com.oracle.truffle.api.library.CachedLibrary;
1414
import com.oracle.truffle.api.library.ExportLibrary;
1515
import com.oracle.truffle.api.library.ExportMessage;
16-
import java.nio.ByteBuffer;
17-
import java.nio.ByteOrder;
1816
import org.enso.interpreter.arrow.LogicalLayout;
19-
import org.enso.interpreter.arrow.util.MemoryUtil;
2017

2118
@ExportLibrary(InteropLibrary.class)
2219
public class ArrowCastToFixedSizeArrayFactory implements TruffleObject {
@@ -45,7 +42,7 @@ static Object doDate32(
4542
Object[] args,
4643
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
4744
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
48-
var unit = ArrowFixedArrayDate.DateUnit.Day;
45+
var unit = LogicalLayout.Date32;
4946
return new ArrowFixedArrayDate(pointer(args, iop, unit), unit);
5047
}
5148

@@ -55,7 +52,7 @@ static Object doDate64(
5552
Object[] args,
5653
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
5754
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
58-
var unit = ArrowFixedArrayDate.DateUnit.Millisecond;
55+
var unit = LogicalLayout.Date64;
5956
return new ArrowFixedArrayDate(pointer(args, iop, unit), unit);
6057
}
6158

@@ -65,7 +62,7 @@ static Object doInt8(
6562
Object[] args,
6663
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
6764
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
68-
var unit = ArrowFixedArrayInt.IntUnit.Byte1;
65+
var unit = LogicalLayout.Int8;
6966
return new ArrowFixedArrayInt(pointer(args, iop, unit), unit);
7067
}
7168

@@ -75,7 +72,7 @@ static Object doInt16(
7572
Object[] args,
7673
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
7774
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
78-
var unit = ArrowFixedArrayInt.IntUnit.Byte2;
75+
var unit = LogicalLayout.Int16;
7976
return new ArrowFixedArrayInt(pointer(args, iop, unit), unit);
8077
}
8178

@@ -85,7 +82,7 @@ static Object doInt32(
8582
Object[] args,
8683
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
8784
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
88-
var unit = ArrowFixedArrayInt.IntUnit.Byte4;
85+
var unit = LogicalLayout.Int32;
8986
return new ArrowFixedArrayInt(pointer(args, iop, unit), unit);
9087
}
9188

@@ -95,7 +92,7 @@ static Object doInt64(
9592
Object[] args,
9693
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
9794
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
98-
var unit = ArrowFixedArrayInt.IntUnit.Byte8;
95+
var unit = LogicalLayout.Int64;
9996
return new ArrowFixedArrayInt(pointer(args, iop, unit), unit);
10097
}
10198

@@ -114,20 +111,16 @@ private static ByteBufferDirect pointer(Object[] args, InteropLibrary interop, S
114111
new Object[] {args[0]}, "Size of allocated memory is invalid");
115112
}
116113

117-
var size = interop.asInt(args[1]);
118-
var targetSize = size * unit.sizeInBytes();
119-
ByteBuffer buffer = MemoryUtil.directBuffer(interop.asLong(args[0]), targetSize);
120-
buffer.order(ByteOrder.LITTLE_ENDIAN);
114+
var capacity = interop.asInt(args[1]);
121115
if (args.length == 3) {
122116
if (!interop.isNumber(args[2]) || !interop.fitsInLong(args[2])) {
123117
throw UnsupportedTypeException.create(
124118
new Object[] {args[2]}, "Address of non-null bitmap is invalid");
125119
}
126-
ByteBuffer validityMap =
127-
MemoryUtil.directBuffer(interop.asLong(args[2]), (int) Math.ceil(size / 8) + 1);
128-
return new ByteBufferDirect(buffer, validityMap);
120+
return ByteBufferDirect.fromAddress(
121+
interop.asLong(args[0]), interop.asLong(args[2]), capacity, unit);
129122
} else {
130-
return new ByteBufferDirect(buffer, size);
123+
return ByteBufferDirect.fromAddress(interop.asLong(args[0]), capacity, unit);
131124
}
132125
}
133126

Lines changed: 33 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,40 @@
11
package org.enso.interpreter.arrow.runtime;
22

33
import com.oracle.truffle.api.CompilerDirectives;
4-
import com.oracle.truffle.api.dsl.Cached;
54
import com.oracle.truffle.api.dsl.ImportStatic;
65
import com.oracle.truffle.api.dsl.Specialization;
76
import com.oracle.truffle.api.interop.InteropLibrary;
87
import com.oracle.truffle.api.interop.TruffleObject;
98
import com.oracle.truffle.api.interop.UnsupportedMessageException;
10-
import com.oracle.truffle.api.library.CachedLibrary;
119
import com.oracle.truffle.api.library.ExportLibrary;
1210
import com.oracle.truffle.api.library.ExportMessage;
1311
import java.time.Instant;
1412
import java.time.LocalDate;
1513
import java.time.LocalTime;
1614
import java.time.ZoneId;
17-
import java.time.ZoneOffset;
1815
import java.time.ZonedDateTime;
16+
import org.enso.interpreter.arrow.LogicalLayout;
1917

2018
@ExportLibrary(InteropLibrary.class)
2119
public final class ArrowFixedArrayDate implements TruffleObject {
2220
private final int size;
2321
private final ByteBufferDirect buffer;
22+
private final LogicalLayout unit;
2423

25-
private final DateUnit unit;
26-
27-
public ArrowFixedArrayDate(int size, DateUnit unit) {
24+
public ArrowFixedArrayDate(int size, LogicalLayout unit) {
2825
this.size = size;
2926
this.unit = unit;
30-
this.buffer = allocateBuffer(size * unit.sizeInBytes(), size);
27+
this.buffer = ByteBufferDirect.forSize(size, unit);
3128
}
3229

33-
public ArrowFixedArrayDate(ByteBufferDirect buffer, DateUnit unit)
30+
public ArrowFixedArrayDate(ByteBufferDirect buffer, LogicalLayout unit)
3431
throws UnsupportedMessageException {
3532
this.size = buffer.capacity() / unit.sizeInBytes();
3633
this.unit = unit;
3734
this.buffer = buffer;
3835
}
3936

40-
public DateUnit getUnit() {
37+
public LogicalLayout getUnit() {
4138
return unit;
4239
}
4340

@@ -47,86 +44,42 @@ public boolean hasArrayElements() {
4744
}
4845

4946
@ExportMessage
50-
@ImportStatic(ArrowFixedArrayDate.DateUnit.class)
47+
@ImportStatic(LogicalLayout.class)
5148
static class ReadArrayElement {
52-
@Specialization(guards = "receiver.getUnit() == Day")
49+
@Specialization(guards = "receiver.getUnit() == Date32")
5350
static Object doDay(ArrowFixedArrayDate receiver, long index)
5451
throws UnsupportedMessageException {
55-
if (receiver.buffer.isNull((int) index)) {
56-
return NullValue.get();
57-
}
58-
var at = typeAdjustedIndex(index, receiver.unit);
59-
var daysSinceEpoch = receiver.buffer.getInt(at);
60-
var localDate = localDateFromDays(daysSinceEpoch);
61-
return new ArrowDate(localDate);
52+
return readDay(receiver.buffer, index);
6253
}
6354

64-
@Specialization(guards = "receiver.getUnit() == Millisecond")
55+
@Specialization(guards = "receiver.getUnit() == Date64")
6556
static Object doMilliseconds(ArrowFixedArrayDate receiver, long index)
6657
throws UnsupportedMessageException {
67-
if (receiver.buffer.isNull((int) index)) {
68-
return NullValue.get();
69-
}
70-
var at = typeAdjustedIndex(index, receiver.unit);
71-
var secondsPlusNanoSinceEpoch = receiver.buffer.getLong(at);
72-
var seconds = Math.floorDiv(secondsPlusNanoSinceEpoch, nanoDiv);
73-
var nano = Math.floorMod(secondsPlusNanoSinceEpoch, nanoDiv);
74-
var zonedDateTime = zonedDateTimeFromSeconds(seconds, nano, utc);
75-
return new ArrowZonedDateTime(zonedDateTime);
58+
return readMilliseconds(receiver.buffer, index);
7659
}
7760
}
7861

79-
@ExportMessage
80-
@ImportStatic(ArrowFixedArrayDate.DateUnit.class)
81-
static class WriteArrayElement {
82-
@Specialization(guards = "receiver.getUnit() == Day")
83-
static void doDay(
84-
ArrowFixedArrayDate receiver,
85-
long index,
86-
Object value,
87-
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
88-
throws UnsupportedMessageException {
89-
if (!iop.isDate(value)) {
90-
throw UnsupportedMessageException.create();
91-
}
92-
var at = typeAdjustedIndex(index, receiver.unit);
93-
var time = iop.asDate(value).toEpochDay();
94-
receiver.buffer.putInt(at, Math.toIntExact(time));
95-
}
96-
97-
@Specialization(guards = {"receiver.getUnit() == Millisecond", "!iop.isNull(value)"})
98-
static void doMilliseconds(
99-
ArrowFixedArrayDate receiver,
100-
long index,
101-
Object value,
102-
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop)
103-
throws UnsupportedMessageException {
104-
if (!iop.isDate(value) || !iop.isTime(value)) {
105-
throw UnsupportedMessageException.create();
106-
}
107-
108-
var at = typeAdjustedIndex(index, receiver.unit);
109-
if (iop.isTimeZone(value)) {
110-
var zoneDateTimeInstant =
111-
instantForZone(iop.asDate(value), iop.asTime(value), iop.asTimeZone(value), utc);
112-
var secondsPlusNano =
113-
zoneDateTimeInstant.getEpochSecond() * nanoDiv + zoneDateTimeInstant.getNano();
114-
receiver.buffer.putLong(at, secondsPlusNano);
115-
} else {
116-
var dateTime = instantForOffset(iop.asDate(value), iop.asTime(value), ZoneOffset.UTC);
117-
var secondsPlusNano = dateTime.getEpochSecond() * nanoDiv + dateTime.getNano();
118-
receiver.buffer.putLong(at, secondsPlusNano);
119-
}
62+
static Object readDay(ByteBufferDirect buffer, long index) throws UnsupportedMessageException {
63+
if (buffer.isNull((int) index)) {
64+
return NullValue.get();
12065
}
66+
var at = typeAdjustedIndex(index, 4);
67+
var daysSinceEpoch = buffer.getInt(at);
68+
var localDate = localDateFromDays(daysSinceEpoch);
69+
return new ArrowDate(localDate);
70+
}
12171

122-
@Specialization(guards = "iop.isNull(value)")
123-
static void doNull(
124-
ArrowFixedArrayDate receiver,
125-
long index,
126-
Object value,
127-
@Cached.Shared("interop") @CachedLibrary(limit = "1") InteropLibrary iop) {
128-
receiver.buffer.setNull((int) index);
72+
static Object readMilliseconds(ByteBufferDirect buffer, long index)
73+
throws UnsupportedMessageException {
74+
if (buffer.isNull((int) index)) {
75+
return NullValue.get();
12976
}
77+
var at = typeAdjustedIndex(index, 8);
78+
var secondsPlusNanoSinceEpoch = buffer.getLong(at);
79+
var seconds = Math.floorDiv(secondsPlusNanoSinceEpoch, NANO_DIV);
80+
var nano = Math.floorMod(secondsPlusNanoSinceEpoch, NANO_DIV);
81+
var zonedDateTime = zonedDateTimeFromSeconds(seconds, nano, UTC);
82+
return new ArrowZonedDateTime(zonedDateTime);
13083
}
13184

13285
@ExportMessage
@@ -139,16 +92,6 @@ boolean isArrayElementReadable(long index) {
13992
return index >= 0 && index < size && !buffer.isNull((int) index);
14093
}
14194

142-
@ExportMessage
143-
boolean isArrayElementModifiable(long index) {
144-
return index >= 0 && index < size;
145-
}
146-
147-
@ExportMessage
148-
boolean isArrayElementInsertable(long index) {
149-
return index >= 0 && index < size;
150-
}
151-
15295
@ExportLibrary(InteropLibrary.class)
15396
static class ArrowDate implements TruffleObject {
15497
private LocalDate date;
@@ -207,11 +150,6 @@ public ZoneId asTimeZone() {
207150
}
208151
}
209152

210-
@CompilerDirectives.TruffleBoundary
211-
private static ByteBufferDirect allocateBuffer(int sizeInBytes, int size) {
212-
return new ByteBufferDirect(sizeInBytes, size);
213-
}
214-
215153
@CompilerDirectives.TruffleBoundary
216154
private static LocalDate localDateFromDays(int daysSinceEpoch) {
217155
return LocalDate.ofEpochDay(daysSinceEpoch);
@@ -222,37 +160,11 @@ private static ZonedDateTime zonedDateTimeFromSeconds(long seconds, long nano, Z
222160
return Instant.ofEpochSecond(seconds, nano).atZone(zone);
223161
}
224162

225-
@CompilerDirectives.TruffleBoundary
226-
private static Instant instantForZone(
227-
LocalDate date, LocalTime time, ZoneId zone, ZoneId target) {
228-
return date.atTime(time).atZone(zone).withZoneSameLocal(target).toInstant();
229-
}
230-
231-
@CompilerDirectives.TruffleBoundary
232-
private static Instant instantForOffset(LocalDate date, LocalTime time, ZoneOffset offset) {
233-
return date.atTime(time).toInstant(offset);
234-
}
235-
236-
public enum DateUnit implements SizeInBytes {
237-
Day(4),
238-
Millisecond(8);
239-
240-
private final int bytes;
241-
242-
DateUnit(int bytes) {
243-
this.bytes = bytes;
244-
}
245-
246-
public int sizeInBytes() {
247-
return bytes;
248-
}
249-
}
250-
251-
private static final long nanoDiv = 1000000000L;
163+
static final long NANO_DIV = 1000000000L;
252164

253-
private static final ZoneId utc = ZoneId.of("UTC");
165+
static final ZoneId UTC = ZoneId.of("UTC");
254166

255-
private static int typeAdjustedIndex(long index, SizeInBytes unit) {
256-
return Math.toIntExact(index * unit.sizeInBytes());
167+
static int typeAdjustedIndex(long index, int daySizeInBytes) {
168+
return Math.toIntExact(index * daySizeInBytes);
257169
}
258170
}

0 commit comments

Comments
 (0)