From ae89884204e4353534981689d826be99a44e38c6 Mon Sep 17 00:00:00 2001 From: Jaroslav Tulach Date: Mon, 14 Aug 2023 14:20:32 +0200 Subject: [PATCH] Turning ArrayBuilder into a builtin --- .../Base/0.0.0-dev/src/Data/Vector.enso | 3 +- .../src/Internal/Array_Like_Helpers.enso | 3 + .../src/System/File/Write_Extensions.enso | 4 +- .../runtime/data/vector/ArrayBuilder.java | 212 ++++++++++++++++++ .../runtime/data/vector/ArrayLikeHelpers.java | 141 +----------- .../java/org/enso/base/Array_Builder.java | 134 ----------- .../main/java/org/enso/base/Array_Utils.java | 16 ++ 7 files changed, 242 insertions(+), 271 deletions(-) create mode 100644 engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java delete mode 100644 std-bits/base/src/main/java/org/enso/base/Array_Builder.java create mode 100644 std-bits/base/src/main/java/org/enso/base/Array_Utils.java diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso index e908f79391201..2b5012ff94fb2 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Vector.enso @@ -32,7 +32,6 @@ from project.Data.Ordering import all from project.Data.Range.Extensions import all polyglot java import java.lang.IndexOutOfBoundsException -polyglot java import org.enso.base.Array_Builder ## The basic, immutable, vector type. A vector allows to store an arbitrary number of elements, in linear memory. @@ -1047,7 +1046,7 @@ type Builder Vector.new_builder new : Integer -> Builder - new (capacity=10) = Builder.Value (Array_Builder.newBuilder capacity) + new (capacity=10) = Builder.Value (Array_Like_Helpers.new_vector_builder capacity) ## Checks if this builder is empty. is_empty : Boolean diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso index fd4107c8c5874..13e972c3be6a5 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Internal/Array_Like_Helpers.enso @@ -11,6 +11,9 @@ import project.Data.Numbers.Integer new_array_proxy_builtin : Integer -> (Integer -> Any) -> Array new_array_proxy_builtin length at = @Builtin_Method "Array_Like_Helpers.new_array_proxy_builtin" +new_vector_builder : Integer -> Any +new_vector_builder capacity = @Builtin_Method "Array_Like_Helpers.new_vector_builder" + length array_like = @Builtin_Method "Array_Like_Helpers.length" at array_like index = @Builtin_Method "Array_Like_Helpers.at" diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Write_Extensions.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Write_Extensions.enso index c4090be90ee56..5ad207099980a 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Write_Extensions.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File/Write_Extensions.enso @@ -16,7 +16,7 @@ import project.Warning.Warning from project.Data.Boolean import Boolean, False, True from project.Data.Text.Extensions import all -polyglot java import org.enso.base.Array_Builder +polyglot java import org.enso.base.Array_Utils ## Writes (or appends) the text to the specified file using the supplied encoding. The behavior specified in the `existing_file` parameter will be @@ -98,7 +98,7 @@ Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> File ! Illegal_Arg Vector.write_bytes self path on_existing_file=Existing_File_Behavior.Backup = Panic.catch Unsupported_Argument_Types handler=(_ -> Error.throw (Illegal_Argument.Error "Only Vectors consisting of bytes (integers in the range from -128 to 127) are supported by the `write_bytes` method.")) <| ## Convert to a byte array before writing - and fail early if there is any problem. - byte_array = Array_Builder.ensureByteArray self + byte_array = Array_Utils.ensureByteArray self file = File.new path r = on_existing_file.write file stream-> diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java new file mode 100644 index 0000000000000..ecbf1e8400c70 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayBuilder.java @@ -0,0 +1,212 @@ +package org.enso.interpreter.runtime.data.vector; + +import java.util.Arrays; +import java.util.List; + +import org.enso.interpreter.runtime.data.EnsoObject; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; + +@ExportLibrary(InteropLibrary.class) +final class ArrayBuilder implements EnsoObject { + private static final String[] MEMBERS = new String[] { "isEmpty", "add", "get", "getSize", "toArray" }; + private static final Object[] EMPTY_ARRAY = new Object[0]; + private final int initialCapacity; + private int size; + private Object primitiveArray; + private Object[] objectArray; + + private ArrayBuilder(int initialCapacity) { + this.initialCapacity = Math.max(1, initialCapacity); + } + + /** + * Creates new builder + */ + static ArrayBuilder newBuilder(int capacity) { + return new ArrayBuilder(capacity); + } + + /** + * Is the builder empty? + */ + boolean isEmpty() { + return size == 0; + } + + /** + * Adds an element to the builder + * + * @param e the element to add + */ + void add(Object e) { + if (objectArray != null) { + if (size == objectArray.length) { + CompilerDirectives.transferToInterpreter(); + objectArray = Arrays.copyOf(objectArray, size * 2); + } + objectArray[size++] = e; + } else if (primitiveArray instanceof long[] longArray) { + if (e instanceof Long l) { + if (size == longArray.length) { + CompilerDirectives.transferToInterpreter(); + primitiveArray = longArray = Arrays.copyOf(longArray, size * 2); + } + longArray[size++] = l; + } else { + CompilerDirectives.transferToInterpreter(); + objectArray = new Object[longArray.length]; + for (int i = 0; i < size; i++) { + objectArray[i] = longArray[i]; + } + primitiveArray = null; + add(e); + } + } else if (primitiveArray instanceof double[] doubleArray) { + if (e instanceof Double d) { + if (size == doubleArray.length) { + CompilerDirectives.transferToInterpreter(); + primitiveArray = doubleArray = Arrays.copyOf(doubleArray, size * 2); + } + doubleArray[size++] = d; + } else { + CompilerDirectives.transferToInterpreter(); + objectArray = new Object[doubleArray.length]; + for (int i = 0; i < size; i++) { + objectArray[i] = doubleArray[i]; + } + primitiveArray = null; + add(e); + } + } else { + assert objectArray == null; + assert primitiveArray == null; + assert size == 0; + if (e instanceof Long l) { + var arr = new long[initialCapacity]; + arr[0] = l; + primitiveArray = arr; + } else if (e instanceof Double d) { + var arr = new double[initialCapacity]; + arr[0] = d; + primitiveArray = arr; + } else { + var arr = new Object[initialCapacity]; + arr[0] = e; + objectArray = arr; + } + size = 1; + } + } + + /** + * Obtains an element from the builder + */ + Object get(int index) { + if (objectArray != null) { + return objectArray[index]; + } else if (primitiveArray instanceof long[] longArray) { + return longArray[index]; + } else if (primitiveArray instanceof double[] doubleArray) { + return doubleArray[index]; + } else { + throw new ArrayIndexOutOfBoundsException(); + } + } + + /** + * A temporary workaround to be able to efficiently append an array to + * `ArrayList`. + */ + void appendTo(List list) { + for (var obj : list) { + add(obj); + } + } + + /** + * Returns the current array of the builder. + */ + Object toArray() { + if (objectArray != null) { + return objectArray.length == size ? objectArray : Arrays.copyOf(objectArray, size); + } else if (primitiveArray instanceof long[] longArray) { + return longArray.length == size ? longArray : Arrays.copyOf(longArray, size); + } else if (primitiveArray instanceof double[] doubleArray) { + return doubleArray.length == size ? doubleArray : Arrays.copyOf(doubleArray, size); + } else { + return EMPTY_ARRAY; + } + } + + int getSize() { + return size; + } + + @ExportMessage + Object invokeMember( + String name, Object[] args, + @CachedLibrary(limit="3") InteropLibrary iop + ) throws UnknownIdentifierException, UnsupportedTypeException, UnsupportedMessageException { + return switch (name) { + case "isEmpty" -> isEmpty(); + case "add" -> { + add(args[0]); + yield this; + } + case "get" -> { + if (!iop.isNumber(args[0])) { + throw UnsupportedTypeException.create(args); + } + if (!iop.fitsInInt(args[0])) { + throw UnsupportedTypeException.create(args); + } + var index = iop.asInt(args[0]); + yield get(index); + } + case "getSize" -> getSize(); + case "toArray" -> { + var arr = toArray(); + if (arr instanceof long[] longs) { + yield Vector.fromLongArray(longs); + } + if (arr instanceof double[] doubles) { + yield Vector.fromDoubleArray(doubles); + } + yield Vector.fromInteropArray(new Array((Object[])arr)); + } + default -> throw UnknownIdentifierException.create(name); + }; + } + + @ExportMessage + boolean hasMembers() { + return true; + } + @ExportMessage + boolean isMemberInvocable(String member) { + for (var m : MEMBERS) { + if (m.equals(member)) { + return true; + } + } + return false; + } + + @ExportMessage + EnsoObject getMembers(boolean includeInternal) { + return ArrayLikeHelpers.wrapStrings(members); + } + + @ExportMessage + String toDisplayString(boolean ignore) { + return "Array_Builder"; + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java index 4daee269f9edf..c89405cf6e8d8 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/vector/ArrayLikeHelpers.java @@ -82,7 +82,7 @@ public static Object vectorFromFunction( @CachedLibrary(limit="3") WarningsLibrary warnings ) { var len = Math.toIntExact(length); - var target = new Array_Builder(len); + var target = ArrayBuilder.newBuilder(len); boolean nonTrivialEnsoValue = false; for (int i = 0; i < len; i++) { var value = invokeFunctionNode.execute(fun, frame, state, new Long[] {(long) i}); @@ -124,6 +124,13 @@ public static Object vectorToArray(Object obj) { } } + @Builtin.Method( + name = "new_vector_builder", + description = "Returns new vector builder.") + public static Object newVectorBuilder(long capacity) { + return ArrayBuilder.newBuilder((int)Math.min(Math.abs(capacity),Integer.MAX_VALUE)); + } + public static EnsoObject wrapBuffer(ByteBuffer buffer) { return ArrayOverBuffer.wrapBuffer(buffer); } @@ -151,136 +158,4 @@ public static EnsoObject asVectorWithCheckAt(Object... arr) { public static EnsoObject asVectorFromArray(Object storage) { return Vector.fromInteropArray(storage); } - - private static final class Array_Builder { - private static final Object[] EMPTY_ARRAY = new Object[0]; - private final int initialCapacity; - private int size; - private Object primitiveArray; - private Object[] objectArray; - - private Array_Builder(int initialCapacity) { - this.initialCapacity = Math.max(1, initialCapacity); - } - - /** - * This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for - * asserting that it is a valid `byte[]`. - * - * @param input the converted array. - * @return the `input` unchanged. - */ - public static byte[] ensureByteArray(byte[] input) { - return input; - } - - /** Creates new builder */ - public static Array_Builder newBuilder(int capacity) { - return new Array_Builder<>(capacity); - } - - /** Is the builder empty? */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Adds an element to the builder - * - * @param e the element to add - */ - @CompilerDirectives.TruffleBoundary - public void add(T e) { - if (objectArray != null) { - if (size == objectArray.length) { - objectArray = Arrays.copyOf(objectArray, size * 2); - } - objectArray[size++] = e; - } else if (primitiveArray instanceof long[] longArray) { - if (e instanceof Long l) { - if (size == longArray.length) { - primitiveArray = longArray = Arrays.copyOf(longArray, size * 2); - } - longArray[size++] = l; - } else { - objectArray = new Object[longArray.length]; - for (int i = 0; i < size; i++) { - objectArray[i] = longArray[i]; - } - primitiveArray = null; - add(e); - } - } else if (primitiveArray instanceof double[] doubleArray) { - if (e instanceof Double d) { - if (size == doubleArray.length) { - primitiveArray = doubleArray = Arrays.copyOf(doubleArray, size * 2); - } - doubleArray[size++] = d; - } else { - objectArray = new Object[doubleArray.length]; - for (int i = 0; i < size; i++) { - objectArray[i] = doubleArray[i]; - } - primitiveArray = null; - add(e); - } - } else { - assert objectArray == null; - assert primitiveArray == null; - assert size == 0; - if (e instanceof Long l) { - var arr = new long[initialCapacity]; - arr[0] = l; - primitiveArray = arr; - } else if (e instanceof Double d) { - var arr = new double[initialCapacity]; - arr[0] = d; - primitiveArray = arr; - } else { - var arr = new Object[initialCapacity]; - arr[0] = e; - objectArray = arr; - } - size = 1; - } - } - - /** Obtains an element from the builder */ - public Object get(int index) { - if (objectArray != null) { - return objectArray[index]; - } else if (primitiveArray instanceof long[] longArray) { - return longArray[index]; - } else if (primitiveArray instanceof double[] doubleArray) { - return doubleArray[index]; - } else { - throw new ArrayIndexOutOfBoundsException(); - } - } - - /** A temporary workaround to be able to efficiently append an array to `ArrayList`. */ - public void appendTo(List list) { - for (T obj : list) { - add(obj); - } - } - - /** Returns the current array of the builder. */ - public Object toArray() { - if (objectArray != null) { - return objectArray.length == size ? objectArray : Arrays.copyOf(objectArray, size); - } else if (primitiveArray instanceof long[] longArray) { - return longArray.length == size ? longArray : Arrays.copyOf(longArray, size); - } else if (primitiveArray instanceof double[] doubleArray) { - return doubleArray.length == size ? doubleArray : Arrays.copyOf(doubleArray, size); - } else { - return EMPTY_ARRAY; - } - } - - public int getSize() { - return size; - } - } - } diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Builder.java b/std-bits/base/src/main/java/org/enso/base/Array_Builder.java deleted file mode 100644 index cae356680345b..0000000000000 --- a/std-bits/base/src/main/java/org/enso/base/Array_Builder.java +++ /dev/null @@ -1,134 +0,0 @@ -package org.enso.base; - -import java.util.Arrays; -import java.util.List; - -public class Array_Builder { - private static final Object[] EMPTY_ARRAY = new Object[0]; - private final int initialCapacity; - private int size; - private Object primitiveArray; - private Object[] objectArray; - - private Array_Builder(int initialCapacity) { - this.initialCapacity = Math.max(1, initialCapacity); - } - - /** - * This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for - * asserting that it is a valid `byte[]`. - * - * @param input the converted array. - * @return the `input` unchanged. - */ - public static byte[] ensureByteArray(byte[] input) { - return input; - } - - /** Creates new builder */ - public static Array_Builder newBuilder(int capacity) { - return new Array_Builder<>(capacity); - } - - /** Is the builder empty? */ - public boolean isEmpty() { - return size == 0; - } - - /** - * Adds an element to the builder - * - * @param e the element to add - */ - public void add(T e) { - if (objectArray != null) { - if (size == objectArray.length) { - objectArray = Arrays.copyOf(objectArray, size * 2); - } - objectArray[size++] = e; - } else if (primitiveArray instanceof long[] longArray) { - if (e instanceof Long l) { - if (size == longArray.length) { - primitiveArray = longArray = Arrays.copyOf(longArray, size * 2); - } - longArray[size++] = l; - } else { - objectArray = new Object[longArray.length]; - for (int i = 0; i < size; i++) { - objectArray[i] = longArray[i]; - } - primitiveArray = null; - add(e); - } - } else if (primitiveArray instanceof double[] doubleArray) { - if (e instanceof Double d) { - if (size == doubleArray.length) { - primitiveArray = doubleArray = Arrays.copyOf(doubleArray, size * 2); - } - doubleArray[size++] = d; - } else { - objectArray = new Object[doubleArray.length]; - for (int i = 0; i < size; i++) { - objectArray[i] = doubleArray[i]; - } - primitiveArray = null; - add(e); - } - } else { - assert objectArray == null; - assert primitiveArray == null; - assert size == 0; - if (e instanceof Long l) { - var arr = new long[initialCapacity]; - arr[0] = l; - primitiveArray = arr; - } else if (e instanceof Double d) { - var arr = new double[initialCapacity]; - arr[0] = d; - primitiveArray = arr; - } else { - var arr = new Object[initialCapacity]; - arr[0] = e; - objectArray = arr; - } - size = 1; - } - } - - /** Obtains an element from the builder */ - public Object get(int index) { - if (objectArray != null) { - return objectArray[index]; - } else if (primitiveArray instanceof long[] longArray) { - return longArray[index]; - } else if (primitiveArray instanceof double[] doubleArray) { - return doubleArray[index]; - } else { - throw new ArrayIndexOutOfBoundsException(); - } - } - - /** A temporary workaround to be able to efficiently append an array to `ArrayList`. */ - public void appendTo(List list) { - for (T obj : list) { - add(obj); - } - } - - /** Returns the current array of the builder. */ - public Object toArray() { - if (objectArray != null) { - return objectArray.length == size ? objectArray : Arrays.copyOf(objectArray, size); - } else if (primitiveArray instanceof long[] longArray) { - return longArray.length == size ? longArray : Arrays.copyOf(longArray, size); - } else if (primitiveArray instanceof double[] doubleArray) { - return doubleArray.length == size ? doubleArray : Arrays.copyOf(doubleArray, size); - } else { - return EMPTY_ARRAY; - } - } - - public int getSize() { - return size; - } -} diff --git a/std-bits/base/src/main/java/org/enso/base/Array_Utils.java b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java new file mode 100644 index 0000000000000..3d1737625480e --- /dev/null +++ b/std-bits/base/src/main/java/org/enso/base/Array_Utils.java @@ -0,0 +1,16 @@ +package org.enso.base; + +public final class Array_Utils { + private Array_Utils() {} + + /** + * This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for + * asserting that it is a valid `byte[]`. + * + * @param input the converted array. + * @return the `input` unchanged. + */ + public static byte[] ensureByteArray(byte[] input) { + return input; + } +}