diff --git a/src/main/scala/scorex/util/encode/Base16.scala b/src/main/scala/scorex/util/encode/Base16.scala index 6ce1c74..42de7c9 100644 --- a/src/main/scala/scorex/util/encode/Base16.scala +++ b/src/main/scala/scorex/util/encode/Base16.scala @@ -21,6 +21,7 @@ object Base16 extends BytesEncoder { } def encode(input: Array[Byte]): String = { + if (input.length == 0) return "" // avoid allocation of empty array and new String instance val buf = new Array[Char](input.length * 2) var j = 0 while (j < input.length) { diff --git a/src/main/scala/scorex/util/encode/Base58.scala b/src/main/scala/scorex/util/encode/Base58.scala index 66ec18d..c4299d2 100644 --- a/src/main/scala/scorex/util/encode/Base58.scala +++ b/src/main/scala/scorex/util/encode/Base58.scala @@ -35,7 +35,7 @@ object Base58 extends BytesEncoder { override def decode(input: String): Try[Array[Byte]] = Try { val decoded = decodeToBigInteger(input) - val bytes: Array[Byte] = if (decoded == BigInt(0)) Array.empty else decoded.toByteArray + val bytes: Array[Byte] = if (decoded == BigInt(0)) Array.emptyByteArray else decoded.toByteArray // We may have got one more byte than we wanted, if the high bit of the next-to-last byte was not zero. // This is because BigIntegers are represented with twos-compliment notation, // thus if the high bit of the last byte happens to be 1 another 8 zero bits will be added to diff --git a/src/main/scala/scorex/util/serialization/Reader.scala b/src/main/scala/scorex/util/serialization/Reader.scala index 53d3faa..c44dd2a 100644 --- a/src/main/scala/scorex/util/serialization/Reader.scala +++ b/src/main/scala/scorex/util/serialization/Reader.scala @@ -1,7 +1,7 @@ package scorex.util.serialization -trait Reader { +abstract class Reader { /** * Type of encoded data diff --git a/src/main/scala/scorex/util/serialization/Serializer.scala b/src/main/scala/scorex/util/serialization/Serializer.scala index 3755d07..5b772ac 100644 --- a/src/main/scala/scorex/util/serialization/Serializer.scala +++ b/src/main/scala/scorex/util/serialization/Serializer.scala @@ -2,7 +2,7 @@ package scorex.util.serialization import scala.util.Try -trait Serializer[TFamily, T <: TFamily, R <: Reader, W <: Writer] { +abstract class Serializer[TFamily, T <: TFamily, R <: Reader, W <: Writer] { def serialize(obj: T, w: W): Unit diff --git a/src/main/scala/scorex/util/serialization/VLQByteBufferReader.scala b/src/main/scala/scorex/util/serialization/VLQByteBufferReader.scala index 2ebe435..33aaf64 100644 --- a/src/main/scala/scorex/util/serialization/VLQByteBufferReader.scala +++ b/src/main/scala/scorex/util/serialization/VLQByteBufferReader.scala @@ -22,6 +22,7 @@ class VLQByteBufferReader(buf: ByteBuffer) extends VLQReader { @inline override def getByte(): Byte = buf.get @inline override def getBytes(size: Int): Array[Byte] = { + require(size <= remaining, s"Not enough bytes in the buffer: $size") val res = new Array[Byte](size) buf.get(res) res diff --git a/src/main/scala/scorex/util/serialization/VLQReader.scala b/src/main/scala/scorex/util/serialization/VLQReader.scala index b3dec9c..84bd942 100644 --- a/src/main/scala/scorex/util/serialization/VLQReader.scala +++ b/src/main/scala/scorex/util/serialization/VLQReader.scala @@ -89,8 +89,8 @@ trait VLQReader extends Reader { } @inline override def getBits(size: Int): Array[Boolean] = { - if (size == 0) return Array[Boolean]() - val bitSet = util.BitSet.valueOf(getBytes((size + 7) / 8)) + if (size == 0) return Array.emptyBooleanArray + val bitSet = util.BitSet.valueOf(getBytes((size + 7) >> 3)) val boolArray = new Array[Boolean](size) var i = 0 while (i < size) { diff --git a/src/main/scala/scorex/util/serialization/VLQWriter.scala b/src/main/scala/scorex/util/serialization/VLQWriter.scala index e2c2e9d..a7f1fbf 100644 --- a/src/main/scala/scorex/util/serialization/VLQWriter.scala +++ b/src/main/scala/scorex/util/serialization/VLQWriter.scala @@ -131,7 +131,7 @@ trait VLQWriter extends Writer { } // 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) + val bytes = util.Arrays.copyOf(bitSet.toByteArray, (xs.length + 7) >> 3) putBytes(bytes) this } diff --git a/src/main/scala/scorex/util/serialization/Writer.scala b/src/main/scala/scorex/util/serialization/Writer.scala index 84496a5..8dc9fa3 100644 --- a/src/main/scala/scorex/util/serialization/Writer.scala +++ b/src/main/scala/scorex/util/serialization/Writer.scala @@ -1,6 +1,6 @@ package scorex.util.serialization -trait Writer { +abstract class Writer { /** * Type of encoded data diff --git a/src/test/scala/scorex/util/encode/Base16Specification.scala b/src/test/scala/scorex/util/encode/Base16Specification.scala index a9ffb10..ac583ab 100644 --- a/src/test/scala/scorex/util/encode/Base16Specification.scala +++ b/src/test/scala/scorex/util/encode/Base16Specification.scala @@ -2,4 +2,10 @@ package scorex.util.encode class Base16Specification extends BytesEncoderSpecification { override val encoder: BytesEncoder = Base16 + + property("test vectors") { + val bytes = Array[Byte](1, 2, 3) + Base16.encode(bytes) shouldBe "010203" + Base16.encode(Array.emptyByteArray) shouldBe "" + } } diff --git a/src/test/scala/scorex/util/serialization/VLQReaderWriterSpecification.scala b/src/test/scala/scorex/util/serialization/VLQReaderWriterSpecification.scala index 49c0eeb..b033436 100644 --- a/src/test/scala/scorex/util/serialization/VLQReaderWriterSpecification.scala +++ b/src/test/scala/scorex/util/serialization/VLQReaderWriterSpecification.scala @@ -260,6 +260,36 @@ trait VLQReaderWriterSpecification extends AnyPropSpec checkFail(Int.MaxValue) } + property("getBytes size check") { + val bytes = Array[Byte](1, 2, 3) + + { // successful case + val r = byteBufReader(bytes) + r.getBytes(3) shouldBe bytes + } + + { // successful case 2 + val r = byteBufReader(bytes) + r.position = 2 + r.getBytes(1) shouldBe bytes.slice(2, 3) + } + + { // failure case + val r = byteBufReader(bytes) + an[IllegalArgumentException] should be thrownBy { + r.getBytes(4) + } + } + + { // failure case 2 + val r = byteBufReader(bytes) + r.position = 2 + an[IllegalArgumentException] should be thrownBy { + r.getBytes(2) + } + } + } + property("getUInt range check assertion") { def check(in: Long): Unit = byteBufReader(byteArrayWriter().putULong(in).toBytes).getUInt() shouldBe in diff --git a/src/test/scala/scorex/util/serialization/VQLByteBufferReaderWriterSpecification.scala b/src/test/scala/scorex/util/serialization/VQLByteBufferReaderWriterSpecification.scala index 72ed303..49fdd82 100644 --- a/src/test/scala/scorex/util/serialization/VQLByteBufferReaderWriterSpecification.scala +++ b/src/test/scala/scorex/util/serialization/VQLByteBufferReaderWriterSpecification.scala @@ -4,7 +4,7 @@ import java.nio.ByteBuffer import scorex.util.ByteArrayBuilder -class VQLByteBufferReaderWriterSpecification extends VLQReaderWriterSpecification { +class VLQByteBufferReaderWriterSpecification extends VLQReaderWriterSpecification { override def byteBufReader(bytes: Array[Byte]): VLQReader = { val buf = ByteBuffer.wrap(bytes)