Skip to content

Commit

Permalink
add offset & exception in case of invalid input bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
fluency03 committed Nov 19, 2018
1 parent f1430db commit c6e9715
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 16 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# varint

[![Latest release](https://img.shields.io/github/release/fluency03/varint.svg)](https://github.com/fluency03/varint/releases/latest)
[![Maven Central](https://img.shields.io/maven-central/v/com.github.fluency03/varint_2.12.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.fluency03%22%20AND%20a:%22varint_2.12%22)
[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)

> Scala Implementation of [Varint](https://github.com/multiformats/unsigned-varint).
Expand All @@ -15,7 +17,20 @@

## Install

#### Maven

```xml
<dependency>
<groupId>com.github.fluency03</groupId>
<artifactId>varint_2.12</artifactId>
<version>{latestVersion}</version>
</dependency>
```

#### sbt

```scala
libraryDependencies += "com.github.fluency03" % "varint_2.12" % {latestVersion}
```

## Usage
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name := "varint"

version := "0.0.1"
version := "0.0.2"

organization := "com.github.fluency03"
organizationName := "fluency03"
Expand Down
33 changes: 23 additions & 10 deletions src/main/scala/com/github/fluency03/varint/Varint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ object Varint {
val INT_REST_BITES = 0xFFFFFF80
val LONG_REST_BITES = 0xFFFFFFFFFFFFFF80L

// TODO (fluency03): set max size

def encodeInt(num: Int): Array[Byte] = {
@tailrec
def rec(value: Int, acc: Array[Byte]): Array[Byte] =
Expand All @@ -29,23 +27,38 @@ object Varint {
rec(num, Array())
}

def decodeToInt(bytes: Array[Byte]): (Int, Int) = decodeToInt(bytes, 0)

def decodeToInt(bytes: Array[Byte]): Int = {
def decodeToInt(bytes: Array[Byte], offset: Int): (Int, Int) = {
@tailrec
def rec(index: Int, shift: Int, acc: Int): Int =
if (index >= bytes.length || (bytes(index) & MSB) == 0) acc | (bytes(index) << shift)
def rec(index: Int, shift: Int, acc: Int): (Int, Int) =
if (index >= bytes.length) throw new IllegalArgumentException("Cannot find the ending Byte.")
else if ((bytes(index) & MSB) == 0) (acc | (bytes(index) << shift), index + 1 - offset)
else rec(index + 1, shift + 7, acc | ((bytes(index) & LOW_7_BITS) << shift))

rec(0, 0, 0)
rec(offset, 0, 0)
}

def decodeToLong(bytes: Array[Byte]): Long = {
def decodeToLong(bytes: Array[Byte]): (Long, Int) = decodeToLong(bytes, 0)

def decodeToLong(bytes: Array[Byte], offset: Int): (Long, Int) = {
@tailrec
def rec(index: Int, shift: Long, acc: Long): Long =
if (index >= bytes.length || (bytes(index) & MSB) == 0) acc | (bytes(index).toLong << shift)
def rec(index: Int, shift: Long, acc: Long): (Long, Int) =
if (index >= bytes.length) throw new IllegalArgumentException("Cannot find the ending Byte.")
else if ((bytes(index) & MSB) == 0) (acc | (bytes(index).toLong << shift), index + 1 - offset)
else rec(index + 1, shift + 7, acc | ((bytes(index).toLong & LOW_7_BITS) << shift))

rec(0, 0L, 0L)
rec(offset, 0L, 0L)
}

def extractLength(bytes: Array[Byte], offset: Int): Int = {
@tailrec
def rec(index: Int): Int =
if (index >= bytes.length) 0
else if ((bytes(index) & MSB) == 0) index + 1 - offset
else rec(index + 1)

rec(offset)
}

}
41 changes: 36 additions & 5 deletions src/test/scala/com/github/fluency03/varint/VarintTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class VarintTest extends FlatSpec with Matchers {
it should "encode an Int to Varint Bytes and decode it back." in {
for (i <- 1 to 100) {
val num = rand.nextInt(Int.MaxValue)
Varint.decodeToInt(Varint.encodeInt(num)) shouldBe num
Varint.decodeToInt(Varint.encodeInt(num))._1 shouldBe num
}

Varint.encodeInt(300) shouldBe Array(0xac.toByte, 0x02.toByte)
Expand All @@ -18,12 +18,43 @@ class VarintTest extends FlatSpec with Matchers {
it should "encode an Long to Varint Bytes and decode it back." in {
for (i <- 1 to 200) {
val num = rand.nextLong
if (num > 0) Varint.decodeToLong(Varint.encodeLong(num)) shouldBe num
if (num > 0) Varint.decodeToLong(Varint.encodeLong(num))._1 shouldBe num
}

Varint.decodeToLong(Varint.encodeLong(278734309057836877L)) shouldBe 278734309057836877L
Varint.decodeToLong(Varint.encodeLong(100L)) shouldBe 100L
Varint.decodeToLong(Varint.encodeLong(10000000000L)) shouldBe 10000000000L
Varint.decodeToLong(Varint.encodeLong(278734309057836877L))._1 shouldBe 278734309057836877L
Varint.decodeToLong(Varint.encodeLong(100L))._1 shouldBe 100L
Varint.decodeToLong(Varint.encodeLong(10000000000L))._1 shouldBe 10000000000L
}

it should "throw exception if the given bytes is not Varint bytes." in {
the [IllegalArgumentException] thrownBy {
Varint.decodeToInt(Array())
} should have message "Cannot find the ending Byte."

the [IllegalArgumentException] thrownBy {
Varint.decodeToInt(Array(0xff.toByte))
} should have message "Cannot find the ending Byte."

the [IllegalArgumentException] thrownBy {
Varint.decodeToInt(Array(0xff.toByte, 0x8f.toByte))
} should have message "Cannot find the ending Byte."

the [IllegalArgumentException] thrownBy {
Varint.decodeToInt(Array(0x8f.toByte))
} should have message "Cannot find the ending Byte."
}

it should "be able to extract the valid length of Varint from bytes, given offset." in {
Varint.extractLength(Array(), 0) shouldBe 0
Varint.extractLength(Array(), 1) shouldBe 0
Varint.extractLength(Array(0xff.toByte), 0) shouldBe 0
Varint.extractLength(Array(0xff.toByte, 0x8f.toByte), 0) shouldBe 0
Varint.extractLength(Array(0x8f.toByte), 0) shouldBe 0

Varint.extractLength(Array(0xff.toByte, 0x0f.toByte), 0) shouldBe 2
Varint.extractLength(Array(0xff.toByte, 0x0f.toByte), 1) shouldBe 1
Varint.extractLength(Array(0xff.toByte, 0xff.toByte, 0x8f.toByte, 0x8f.toByte, 0x0f.toByte), 0) shouldBe 5
Varint.extractLength(Array(0x80.toByte, 0x80.toByte, 0x8f.toByte, 0x82.toByte, 0x01.toByte), 2) shouldBe 3
}

}

0 comments on commit c6e9715

Please sign in to comment.