diff --git a/src/main/scala/scorex/util/encode/Base16.scala b/src/main/scala/scorex/util/encode/Base16.scala index 8ce97ec..6ce1c74 100644 --- a/src/main/scala/scorex/util/encode/Base16.scala +++ b/src/main/scala/scorex/util/encode/Base16.scala @@ -33,10 +33,11 @@ object Base16 extends BytesEncoder { } def decode(input: String): Try[Array[Byte]] = { - var (isError, errorMsg) = if (input.length % 2 == 0) { - (false, "") - } else { - (true, s"invalid length ${input.length} of Hex data") + var isError = false + var errorMsg = "" + if (input.length % 2 != 0) { + isError = true + errorMsg = s"invalid length ${input.length} of Hex data" } val out = Array.ofDim[Byte](input.length / 2) diff --git a/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala b/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala index afb097d..9e9f8d3 100644 --- a/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQByteBufferWriter.scala @@ -33,6 +33,11 @@ class VLQByteBufferWriter(b: ByteArrayBuilder) extends Writer with VLQWriter { this } + @inline override def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type = { + b.append(xs, offset, length) + this + } + @inline override def length(): Int = b.length() @inline override def result(): ByteArrayBuilder = b diff --git a/src/main/scala/scorex/util/serialization/VLQWriter.scala b/src/main/scala/scorex/util/serialization/VLQWriter.scala index 2f891f4..e2c2e9d 100644 --- a/src/main/scala/scorex/util/serialization/VLQWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQWriter.scala @@ -104,7 +104,7 @@ trait VLQWriter extends Writer { if ((value & ~0x7FL) == 0) { buffer(position) = value.asInstanceOf[Byte] position += 1 - putBytes(util.Arrays.copyOf(buffer, position)) + putBytes(buffer, 0, position) return this } else { buffer(position) = ((value.asInstanceOf[Int] & 0x7F) | 0x80).toByte @@ -116,10 +116,19 @@ trait VLQWriter extends Writer { // see https://rosettacode.org/wiki/Variable-length_quantity for implementations in other languages } + // TODO optimize: it is possible to further significantly optimize this method + // by directly packing bits into bytes and putting bytes into the writer. + // this can be done without any additional memory garbage (BitSet, toByteArray, copyOf). @inline override def putBits(xs: Array[Boolean]): this.type = { if (xs.isEmpty) return this - val bitSet = new util.BitSet(xs.length) - xs.zipWithIndex.foreach { case (bool, i) => bitSet.set(i, bool)} + val len = xs.length + val bitSet = new util.BitSet(len) + var i = 0 + while (i < len) { + val bool = xs(i) + bitSet.set(i, bool) + i += 1 + } // pad the byte array to fix the "no bit was set" behaviour // see https://stackoverflow.com/questions/11209600/how-do-i-convert-a-bitset-initialized-with-false-in-a-byte-containing-0-in-java val bytes = util.Arrays.copyOf(bitSet.toByteArray, (xs.length + 7) / 8) diff --git a/src/main/scala/scorex/util/serialization/Writer.scala b/src/main/scala/scorex/util/serialization/Writer.scala index b98f5fe..84496a5 100644 --- a/src/main/scala/scorex/util/serialization/Writer.scala +++ b/src/main/scala/scorex/util/serialization/Writer.scala @@ -117,6 +117,14 @@ trait Writer { */ def putBytes(xs: Array[Byte]): this.type + /** + * Encode a slice of array of bytes. + * @param xs an array to take bytes from + * @param offset first byte of the slice + * @param length of the slice (number of bytes) + */ + def putBytes(xs: Array[Byte], offset: Int, length: Int): this.type + /** * Encode an array of boolean values as a bit array *