diff --git a/sjsonnet/src/sjsonnet/Evaluator.scala b/sjsonnet/src/sjsonnet/Evaluator.scala index 2e52c2cd..63030229 100644 --- a/sjsonnet/src/sjsonnet/Evaluator.scala +++ b/sjsonnet/src/sjsonnet/Evaluator.scala @@ -1103,7 +1103,7 @@ class Evaluator( } def visitObjComp(e: ObjBody.ObjComp, sup: Val.Obj)(implicit scope: ValScope): Val.Obj = { - val binds = e.preLocals ++ e.postLocals + val binds = e.allLocals val compScope: ValScope = scope // .clearSuper val builder = new java.util.LinkedHashMap[String, Val.Obj.Member] val compScopes = visitComp(e.first :: e.rest, Array(compScope)) diff --git a/sjsonnet/src/sjsonnet/Expr.scala b/sjsonnet/src/sjsonnet/Expr.scala index 16d5da0d..0fb4307a 100644 --- a/sjsonnet/src/sjsonnet/Expr.scala +++ b/sjsonnet/src/sjsonnet/Expr.scala @@ -390,6 +390,8 @@ object Expr { override def exprErrorString: String = "object comprehension" override def toString: String = s"ObjComp($pos, ${arrStr(preLocals)}, $key, $value, ${arrStr(postLocals)}, $first, $rest)" + // Cache concatenation of preLocals and postLocals to avoid repeated array allocation + lazy val allLocals: Array[Bind] = preLocals ++ postLocals } } } diff --git a/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala b/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala index 5c82f8d2..cbd314b3 100644 --- a/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala +++ b/sjsonnet/src/sjsonnet/stdlib/ArrayModule.scala @@ -487,26 +487,35 @@ object ArrayModule extends AbstractFunctionModule { if (idx == -1) { arr } else { - Val.Arr( - arr.pos, - arr.asLazyArray.slice(0, idx) ++ arr.asLazyArray.slice(idx + 1, arr.length) - ) + val src = arr.asLazyArray + val result = new Array[Eval](src.length - 1) + System.arraycopy(src, 0, result, 0, idx) + System.arraycopy(src, idx + 1, result, idx, src.length - idx - 1) + Val.Arr(arr.pos, result) } }, builtin("removeAt", "arr", "idx") { (_, _, arr: Val.Arr, idx: Int) => if (!(0 <= idx && idx < arr.length)) { Error.fail("index out of bounds: 0 <= " + idx + " < " + arr.length) } - Val.Arr( - arr.pos, - arr.asLazyArray.slice(0, idx) ++ arr.asLazyArray.slice(idx + 1, arr.length) - ) + val src = arr.asLazyArray + val result = new Array[Eval](src.length - 1) + System.arraycopy(src, 0, result, 0, idx) + System.arraycopy(src, idx + 1, result, idx, src.length - idx - 1) + Val.Arr(arr.pos, result) }, builtin("sum", "arr") { (_, _, arr: Val.Arr) => if (!arr.forall(_.isInstanceOf[Val.Num])) { Error.fail("Argument must be an array of numbers") } - arr.asLazyArray.map(_.value.asDouble).sum + val a = arr.asLazyArray + var sum = 0.0 + var i = 0 + while (i < a.length) { + sum += a(i).value.asDouble + i += 1 + } + sum }, builtin("avg", "arr") { (_, _, arr: Val.Arr) => if (!arr.forall(_.isInstanceOf[Val.Num])) { @@ -515,7 +524,14 @@ object ArrayModule extends AbstractFunctionModule { if (arr.length == 0) { Error.fail("Cannot calculate average of an empty array") } - arr.asLazyArray.map(_.value.asDouble).sum / arr.length + val a = arr.asLazyArray + var sum = 0.0 + var i = 0 + while (i < a.length) { + sum += a(i).value.asDouble + i += 1 + } + sum / arr.length } ) }