From a3a41bbc2b7be5416405620ef4d588c8fbcc2ae1 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 22 Oct 2018 14:31:40 +0200 Subject: [PATCH 1/2] Improve compression of TastyString Improves #3877 `'{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } }` was reduced from 206 bytes to 172 bytes `'{ ~x * ~powerCode(n - 1, x) }` was reduced from 143 bytes to 128 bytes --- .../tools/dotc/core/tasty/TastyString.scala | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala index fe9f8cc0883d..5bb6a8456a1a 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala @@ -2,34 +2,34 @@ package dotty.tools.dotc.core.tasty import scala.runtime.quoted.Unpickler.Pickled +import java.io._ +import java.util.Base64 +import java.nio.charset.StandardCharsets.UTF_8 + /** Utils for String representation of TASTY */ object TastyString { - // Conservative encoding, each byte is encoded in a char - // TODO improve encoding compression - private final val maxStringSize = 65535 / 2 + // Max size of a string literal in the bytecode + private final val maxStringSize = 65535 /** Encode TASTY bytes into an Seq of String */ - def pickle(bytes: Array[Byte]): Pickled = - bytes.sliding(maxStringSize, maxStringSize).map(bytesToString).toList - - /** Decode the TASTY String into TASTY bytes */ - def unpickle(strings: Pickled): Array[Byte] = { - val bytes = new Array[Byte](strings.map(_.length).sum) - var i = 0 - for (str <- strings; j <- str.indices) { - bytes(i) = str.charAt(j).toByte - i += 1 + def pickle(bytes: Array[Byte]): Pickled = { + val str = new String(Base64.getEncoder().encode(bytes), UTF_8) + def split(sliceEnd: Int, acc: List[String]): List[String] = { + if (sliceEnd == 0) acc + else { + val sliceStart = (sliceEnd - maxStringSize) max 0 + split(sliceStart, str.substring(sliceStart, sliceEnd) :: acc) + } } - bytes + split(str.length, Nil) } - /** Encode bytes into a String */ - private def bytesToString(bytes: Array[Byte]): String = { - assert(bytes.length <= maxStringSize) - val chars = new Array[Char](bytes.length) - for (i <- bytes.indices) chars(i) = (bytes(i) & 0xff).toChar - new String(chars) + /** Decode the TASTY String into TASTY bytes */ + def unpickle(strings: Pickled): Array[Byte] = { + val string = new StringBuilder + strings.foreach(string.append) + Base64.getDecoder().decode(string.result().getBytes(UTF_8)) } } From 2f669956cdca6ac188dd77ad09dfcca77bf46988 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 23 Oct 2018 11:46:08 +0200 Subject: [PATCH 2/2] Use sliding to simplify the code --- .../src/dotty/tools/dotc/core/tasty/TastyString.scala | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala index 5bb6a8456a1a..8450ee1f1a4c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala @@ -15,14 +15,7 @@ object TastyString { /** Encode TASTY bytes into an Seq of String */ def pickle(bytes: Array[Byte]): Pickled = { val str = new String(Base64.getEncoder().encode(bytes), UTF_8) - def split(sliceEnd: Int, acc: List[String]): List[String] = { - if (sliceEnd == 0) acc - else { - val sliceStart = (sliceEnd - maxStringSize) max 0 - split(sliceStart, str.substring(sliceStart, sliceEnd) :: acc) - } - } - split(str.length, Nil) + str.sliding(maxStringSize, maxStringSize).toList } /** Decode the TASTY String into TASTY bytes */