Skip to content

Commit

Permalink
Optimise Starlark singleton tuples
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 509447074
Change-Id: I8089c148dc584bfed52a52a83ddeb2cbdd21eee7
  • Loading branch information
comius authored and Copybara-Service committed Feb 14, 2023
1 parent 1895585 commit 2cc9e13
Show file tree
Hide file tree
Showing 9 changed files with 340 additions and 138 deletions.
2 changes: 2 additions & 0 deletions src/main/java/net/starlark/java/eval/BUILD
Expand Up @@ -35,7 +35,9 @@ java_library(
"ParamDescriptor.java",
"Printer.java",
"RangeList.java",
"RegularTuple.java",
"Sequence.java",
"SingletonTuple.java",
"Starlark.java",
"StarlarkCallable.java",
"StarlarkFloat.java",
Expand Down
155 changes: 155 additions & 0 deletions src/main/java/net/starlark/java/eval/RegularTuple.java
@@ -0,0 +1,155 @@
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package net.starlark.java.eval;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;

/**
* An implementation of non-singleton (empty or more than 1 element) Tuple using an {@code Object}
* array.
*/
final class RegularTuple extends Tuple {
final Object[] elems;

RegularTuple(Object[] elems) {
Preconditions.checkArgument(elems.length != 1);
this.elems = elems;
}

@Override
public boolean isImmutable() {
for (Object x : elems) {
if (!Starlark.isImmutable(x)) {
return false;
}
}
return true;
}

@Override
public void checkHashable() throws EvalException {
for (Object x : elems) {
Starlark.checkHashable(x);
}
}

@Override
public int hashCode() {
return 9857 + 8167 * Arrays.hashCode(elems);
}

@Override
public Object get(int i) {
return elems[i];
}

@Override
public int size() {
return elems.length;
}

@Override
public boolean contains(Object o) {
// Tuple contains only valid Starlark objects (which are non-null)
if (o == null) {
return false;
}
for (Object elem : elems) {
if (o.equals(elem)) {
return true;
}
}
return false;
}

@Override
public Tuple subList(int from, int to) {
Preconditions.checkPositionIndexes(from, to, elems.length);
return wrap(Arrays.copyOfRange(elems, from, to));
}

/** Returns a new array of class Object[] containing the tuple elements. */
@Override
public Object[] toArray() {
return elems.length != 0 ? Arrays.copyOf(elems, elems.length, Object[].class) : elems;
}

@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a) {
if (a.length < elems.length) {
return (T[]) Arrays.copyOf(elems, elems.length, a.getClass());
} else {
System.arraycopy(elems, 0, a, 0, elems.length);
Arrays.fill(a, elems.length, a.length, null);
return a;
}
}

@Override
public void repr(Printer printer) {
// Remark: singletons, are represented as {@code '(x,)'}; whereas this implementation would
// return
// {@code '(x)'}
printer.append('(');
String sep = "";
for (Object elem : elems) {
printer.append(sep);
sep = ", ";
printer.repr(elem);
}
printer.append(')');
}

@Override
public ImmutableList<Object> getImmutableList() {
// Share the array with this (immutable) Tuple.
return wrapImmutable(elems);
}

@Override
public Tuple getSlice(Mutability mu, int start, int stop, int step) throws EvalException {
RangeList indices = new RangeList(start, stop, step);
int n = indices.size();
if (step == 1) { // common case
return subList(indices.at(0), indices.at(n));
}
Object[] res = new Object[n];
for (int i = 0; i < n; ++i) {
res[i] = elems[indices.at(i)];
}
return wrap(res);
}

@Override
Tuple repeat(StarlarkInt n) throws EvalException {
if (n.signum() <= 0 || isEmpty()) {
return empty();
}

int ni = n.toInt("repeat");
long sz = (long) ni * elems.length;
if (sz > StarlarkList.MAX_ALLOC) {
throw Starlark.errorf("excessive repeat (%d * %d elements)", elems.length, ni);
}
Object[] res = new Object[(int) sz];
for (int i = 0; i < ni; i++) {
System.arraycopy(elems, 0, res, i * elems.length, elems.length);
}
return wrap(res);
}
}
22 changes: 22 additions & 0 deletions src/main/java/net/starlark/java/eval/Sequence.java
Expand Up @@ -94,6 +94,28 @@ static int compare(List<?> x, List<?> y) {
return Integer.compare(x.size(), y.size());
}

/**
* Compares two sequences of value for equality. Sequences compare equal if they are the same size
* and corresponding elements compare equal.
*/
static boolean sameElems(List<?> x, List<?> y) {
if (x == y) {
return true;
}
if (x.size() != y.size()) {
return false;
}
for (int i = 0; i < x.size(); i++) {
Object xelem = x.get(i);
Object yelem = y.get(i);

if (xelem != yelem && !xelem.equals(yelem)) {
return false;
}
}
return true;
}

/**
* Returns the slice of this sequence, {@code this[start:stop:step]}. <br>
* For positive strides ({@code step > 0}), {@code 0 <= start <= stop <= size()}. <br>
Expand Down
119 changes: 119 additions & 0 deletions src/main/java/net/starlark/java/eval/SingletonTuple.java
@@ -0,0 +1,119 @@
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package net.starlark.java.eval;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;

/** A specific implementation of a Tuple that has exactly 1 element. */
final class SingletonTuple extends Tuple {
final Object elem;

SingletonTuple(Object elem) {
this.elem = elem;
}

@Override
public boolean isImmutable() {
return Starlark.isImmutable(elem);
}

@Override
public void checkHashable() throws EvalException {
Starlark.checkHashable(elem);
}

@Override
public int hashCode() {
// This produces the same results as RegularTuple.hashCode(),
return 9857 + 8167 * (31 + elem.hashCode());
}

@Override
public Object get(int i) {
Preconditions.checkElementIndex(i, 1);
return elem;
}

@Override
public int size() {
return 1;
}

@Override
public boolean contains(Object o) {
// Tuple contains only valid Starlark objects (which are non-null)
if (o == null) {
return false;
}
return o.equals(elem);
}

@Override
public Tuple subList(int from, int to) {
Preconditions.checkPositionIndexes(from, to, 1);
return from <= 0 && to >= 1 ? this : empty();
}

/** Returns a new array of class Object[] containing the tuple elements. */
@Override
public Object[] toArray() {
return new Object[] {elem};
}

@SuppressWarnings("unchecked")
@Override
public <T> T[] toArray(T[] a) {
if (a.length < 1) {
return (T[]) new Object[] {elem};
} else {
a[0] = (T) elem;
Arrays.fill(a, 1, a.length, null);
return a;
}
}

@Override
public void repr(Printer printer) {
printer.append('(').repr(elem).append(",)");
}

@Override
public ImmutableList<Object> getImmutableList() {
return ImmutableList.of(elem);
}

@Override
public Tuple getSlice(Mutability mu, int start, int stop, int step) throws EvalException {
RangeList indices = new RangeList(start, stop, step);
return indices.isEmpty() ? Tuple.empty() : this;
}

@Override
Tuple repeat(StarlarkInt n) throws EvalException {
if (n.signum() <= 0) {
return empty();
}

int ni = n.toInt("repeat");
if (ni > StarlarkList.MAX_ALLOC) {
throw Starlark.errorf("excessive repeat (%d * %d elements)", 1, ni);
}
Object[] res = new Object[ni];
Arrays.fill(res, 0, ni, elem);
return wrap(res);
}
}
2 changes: 1 addition & 1 deletion src/main/java/net/starlark/java/eval/Starlark.java
Expand Up @@ -327,7 +327,7 @@ public static String classType(Class<?> c) {
// but `getStarlarkBuiltin` is quite expensive.
if (c.equals(StarlarkList.class)) {
return "list";
} else if (c.equals(Tuple.class)) {
} else if (Tuple.class.isAssignableFrom(c)) {
return "tuple";
} else if (c.equals(Dict.class)) {
return "dict";
Expand Down

0 comments on commit 2cc9e13

Please sign in to comment.