Skip to content

Commit

Permalink
Turning ArrayBuilder into a builtin
Browse files Browse the repository at this point in the history
  • Loading branch information
JaroslavTulach committed Aug 14, 2023
1 parent 3c439ae commit ae89884
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 271 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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->
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
}
}
Loading

0 comments on commit ae89884

Please sign in to comment.