Skip to content

Commit 8d70179

Browse files
authored
Merge pull request scodec#147 from scodec/topic/dotty
Dotty Support
2 parents 0b28cd7 + 311a755 commit 8d70179

29 files changed

+863
-529
lines changed

.travis.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
language: scala
22

33
scala:
4+
- "0.22.0-bin-20200123-9982f0d-NIGHTLY"
45
- "2.12.10"
56
- "2.13.1"
67

@@ -21,6 +22,10 @@ cache:
2122
- $HOME/.sbt
2223

2324
script:
24-
- sbt ++$TRAVIS_SCALA_VERSION fmtCheck
25-
- sbt ++$TRAVIS_SCALA_VERSION test mimaReportBinaryIssues
25+
- if [[ "$TRAVIS_SCALA_VERSION" == 0.* ]];
26+
then
27+
sbt ++$TRAVIS_SCALA_VERSION coreJVM/test;
28+
else
29+
sbt ++$TRAVIS_SCALA_VERSION fmtCheck test mimaReportBinaryIssues;
30+
fi
2631

build.sbt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ lazy val commonSettings = Seq(
3636
List("-Xlint", "-Ywarn-unused")
3737
case v if v.startsWith("2.12") =>
3838
Nil
39+
case v if v.startsWith("0.") =>
40+
Nil
3941
case other => sys.error(s"Unsupported scala version: $other")
4042
}),
4143
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oD"),
@@ -94,11 +96,21 @@ lazy val core = crossProject(JVMPlatform, JSPlatform)
9496
.settings(commonSettings: _*)
9597
.settings(
9698
name := "scodec-bits",
99+
libraryDependencies ++= {
100+
if (isDotty.value)
101+
Seq(
102+
"dev.travisbrown" %%% "scalatest" % "3.1.0-20200123-9982f0d-NIGHTLY",
103+
"dev.travisbrown" %%% "scalacheck-1-14" % "3.1.0.1-20200123-9982f0d-NIGHTLY"
104+
)
105+
else
106+
Seq(
107+
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
108+
"org.scalatest" %%% "scalatest" % "3.1.0" % "test",
109+
"org.scalatestplus" %%% "scalacheck-1-14" % "3.1.0.1" % "test"
110+
)
111+
},
97112
libraryDependencies ++= Seq(
98-
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
99-
"org.scalacheck" %%% "scalacheck" % "1.14.3" % "test",
100-
"org.scalatest" %%% "scalatest" % "3.1.0" % "test",
101-
"org.scalatestplus" %%% "scalacheck-1-14" % "3.1.0.1" % "test"
113+
("org.scalacheck" %%% "scalacheck" % "1.14.3" % "test").withDottyCompat(scalaVersion.value)
102114
),
103115
autoAPIMappings := true,
104116
buildInfoPackage := "scodec.bits",

core/jvm/src/test/scala/scodec/bits/BitVectorJvmTest.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package scodec.bits
33
import java.security.MessageDigest
44
import org.scalacheck.{Arbitrary, Gen}
55
import Arbitraries._
6-
import org.scalatest.matchers.should.Matchers._
76

87
class BitVectorJvmTest extends BitsSuite {
98
implicit val arbitraryBitVector: Arbitrary[BitVector] = Arbitrary {
@@ -27,14 +26,14 @@ class BitVectorJvmTest extends BitsSuite {
2726
ok.compareAndSet(true, x.sizeLessThan(x.size + 1))
2827
t1.join
2928
t2.join
30-
ok.get shouldBe true
29+
assert(ok.get == true)
3130
}
3231
}
3332

3433
test("digest") {
3534
forAll { (x: BitVector) =>
3635
val sha256 = MessageDigest.getInstance("SHA-256")
37-
x.digest("SHA-256") shouldBe BitVector(ByteVector(sha256.digest(x.toByteArray)))
36+
assert(x.digest("SHA-256") == BitVector(ByteVector(sha256.digest(x.toByteArray))))
3837
}
3938
}
4039

core/jvm/src/test/scala/scodec/bits/ByteVectorJvmTest.scala

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,59 +2,62 @@ package scodec.bits
22

33
import org.scalacheck.{Arbitrary, Gen}
44
import Arbitrary.arbitrary
5-
import org.scalatest.matchers.should.Matchers._
65
import Arbitraries._
76

87
class ByteVectorJvmTest extends BitsSuite {
98

109
test("toBase64") {
1110
forAll { (b: ByteVector) =>
1211
val guavaB64 = com.google.common.io.BaseEncoding.base64
13-
ByteVector.view(guavaB64.decode(b.toBase64)) shouldBe b
12+
assert(ByteVector.view(guavaB64.decode(b.toBase64)) == b)
1413
}
1514
}
1615

1716
test("fromBase64") {
1817
forAll { (b: ByteVector) =>
1918
val guavaB64 = com.google.common.io.BaseEncoding.base64
20-
ByteVector.fromValidBase64(guavaB64.encode(b.toArray)) shouldBe b
19+
assert(ByteVector.fromValidBase64(guavaB64.encode(b.toArray)) == b)
2120
}
2221
}
2322

2423
test("fromBase64 - digit count non-divisble by 4") {
25-
ByteVector.fromBase64Descriptive("A") shouldBe Left(
26-
"Final base 64 quantum had only 1 digit - must have at least 2 digits"
24+
assert(
25+
ByteVector.fromBase64Descriptive("A") == Left(
26+
"Final base 64 quantum had only 1 digit - must have at least 2 digits"
27+
)
2728
)
28-
ByteVector.fromBase64Descriptive("AB") shouldBe Right(hex"00")
29-
ByteVector.fromBase64Descriptive("ABC") shouldBe Right(hex"0010")
30-
ByteVector.fromBase64Descriptive("ABCD") shouldBe Right(hex"001083")
31-
ByteVector.fromBase64Descriptive("ABCDA") shouldBe Left(
32-
"Final base 64 quantum had only 1 digit - must have at least 2 digits"
29+
assert(ByteVector.fromBase64Descriptive("AB") == Right(hex"00"))
30+
assert(ByteVector.fromBase64Descriptive("ABC") == Right(hex"0010"))
31+
assert(ByteVector.fromBase64Descriptive("ABCD") == Right(hex"001083"))
32+
assert(
33+
ByteVector.fromBase64Descriptive("ABCDA") == Left(
34+
"Final base 64 quantum had only 1 digit - must have at least 2 digits"
35+
)
3336
)
34-
ByteVector.fromBase64Descriptive("ABCDAB") shouldBe Right(hex"00108300")
37+
assert(ByteVector.fromBase64Descriptive("ABCDAB") == Right(hex"00108300"))
3538
}
3639

3740
test("fromBase64 - padding") {
38-
ByteVector.fromBase64Descriptive("AB==") shouldBe Right(hex"00")
41+
assert(ByteVector.fromBase64Descriptive("AB==") == Right(hex"00"))
3942
val paddingError = Left(
4043
"Malformed padding - final quantum may optionally be padded with one or two padding characters such that the quantum is completed"
4144
)
42-
ByteVector.fromBase64Descriptive("A=") shouldBe paddingError
43-
ByteVector.fromBase64Descriptive("A==") shouldBe paddingError
44-
ByteVector.fromBase64Descriptive("A===") shouldBe paddingError
45-
ByteVector.fromBase64Descriptive("A====") shouldBe paddingError
46-
ByteVector.fromBase64Descriptive("AB=") shouldBe paddingError
47-
ByteVector.fromBase64Descriptive("AB===") shouldBe paddingError
48-
ByteVector.fromBase64Descriptive("ABC==") shouldBe paddingError
49-
ByteVector.fromBase64Descriptive("=") shouldBe paddingError
50-
ByteVector.fromBase64Descriptive("==") shouldBe paddingError
51-
ByteVector.fromBase64Descriptive("===") shouldBe paddingError
52-
ByteVector.fromBase64Descriptive("====") shouldBe paddingError
53-
ByteVector.fromBase64Descriptive("=====") shouldBe paddingError
45+
assert(ByteVector.fromBase64Descriptive("A=") == paddingError)
46+
assert(ByteVector.fromBase64Descriptive("A==") == paddingError)
47+
assert(ByteVector.fromBase64Descriptive("A===") == paddingError)
48+
assert(ByteVector.fromBase64Descriptive("A====") == paddingError)
49+
assert(ByteVector.fromBase64Descriptive("AB=") == paddingError)
50+
assert(ByteVector.fromBase64Descriptive("AB===") == paddingError)
51+
assert(ByteVector.fromBase64Descriptive("ABC==") == paddingError)
52+
assert(ByteVector.fromBase64Descriptive("=") == paddingError)
53+
assert(ByteVector.fromBase64Descriptive("==") == paddingError)
54+
assert(ByteVector.fromBase64Descriptive("===") == paddingError)
55+
assert(ByteVector.fromBase64Descriptive("====") == paddingError)
56+
assert(ByteVector.fromBase64Descriptive("=====") == paddingError)
5457
}
5558

5659
test("fromBase64 - empty input string") {
57-
ByteVector.fromBase64Descriptive("") shouldBe Right(ByteVector.empty)
60+
assert(ByteVector.fromBase64Descriptive("") == Right(ByteVector.empty))
5861
}
5962

6063
test("buffer concurrency") {
@@ -69,30 +72,30 @@ class ByteVectorJvmTest extends BitsSuite {
6972
val b1b3 = new Callable[ByteVector] { def call = b1b ++ b3 }
7073
val rb1b2 = pool.submit(b1b2)
7174
val rb1b3 = pool.submit(b1b3)
72-
rb1b2.get shouldBe (b1 ++ b2)
73-
rb1b3.get shouldBe (b1 ++ b3)
75+
assert(rb1b2.get == (b1 ++ b2))
76+
assert(rb1b3.get == (b1 ++ b3))
7477
}
7578
pool.shutdown
7679
}
7780

7881
test("digest") {
7982
forAll { (x: ByteVector) =>
8083
val sha256 = java.security.MessageDigest.getInstance("SHA-256")
81-
x.digest("SHA-256") shouldBe ByteVector.view(sha256.digest(x.toArray))
84+
assert(x.digest("SHA-256") == ByteVector.view(sha256.digest(x.toArray)))
8285
}
8386
}
8487

8588
test("gzip") {
8689
forAll { (x: ByteVector) =>
87-
x.deflate().inflate() shouldBe Right(x)
90+
assert(x.deflate().inflate() == Right(x))
8891
}
8992

9093
val deflatableByteVectors = for {
9194
b <- arbitrary[Byte]
9295
sz <- Gen.chooseNum(1L, 8192L)
9396
} yield ByteVector.fill(sz)(b)
9497
forAll(deflatableByteVectors) { (x: ByteVector) =>
95-
if (x.size > 11) x.deflate().size shouldBe <(x.size)
98+
if (x.size > 11) assert(x.deflate().size < x.size)
9699
}
97100
}
98101

core/jvm/src/test/scala/scodec/bits/CrcJvmTest.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
package scodec.bits
22

3-
import org.scalatest.matchers.should.Matchers._
4-
53
import Arbitraries._
64

75
class CrcJvmTest extends BitsSuite {
86

97
test("crc32 is consistent with java.util.zip.CRC32") {
108
forAll { (b: ByteVector) =>
11-
crc.crc32(b.bits).bytes shouldBe {
9+
assert(crc.crc32(b.bits).bytes == {
1210
val c = new java.util.zip.CRC32
1311
c.update(b.toArray)
1412
ByteVector.fromLong(c.getValue, size = 4)
15-
}
13+
})
1614
}
1715
}
1816

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package scodec.bits
2+
3+
import scala.quoted._
4+
import scala.quoted.matching._
5+
import scala.util.FromDigits
6+
7+
private[bits] trait BitVectorPlatform { self: BitVector.type =>
8+
given as FromDigits.WithRadix[BitVector] = BitVectorFromDigits.Instance
9+
}
10+
11+
private[bits] object BitVectorFromDigits {
12+
13+
class Base extends FromDigits.WithRadix[BitVector] {
14+
def fromDigits(digits: String, radix: Int): BitVector =
15+
digitsToBitVector(digits, radix)
16+
}
17+
18+
private def digitsToBitVector(digits: String, radix: Int): BitVector =
19+
if (radix == 16) ByteVector.fromValidHex(digits.tail).bits
20+
else throw FromDigits.MalformedNumber(s"unsupported radix $radix")
21+
22+
object Instance extends Base {
23+
override inline def fromDigits(digits: String): BitVector =
24+
${digitsToBitVectorMacro('digits, Expr(10))}
25+
override inline def fromDigits(digits: String, radix: Int): BitVector =
26+
${digitsToBitVectorMacro('digits, 'radix)}
27+
}
28+
29+
private def digitsToBitVectorMacro(digits: Expr[String], radix: Expr[Int])(given qctx: QuoteContext): Expr[BitVector] =
30+
(digits, radix) match {
31+
case (Const(ds), Const(r)) =>
32+
if (r == 16) {
33+
'{ByteVector.fromValidHex($digits.tail).bits}
34+
} else {
35+
qctx.error(s"unsupported radix $r", radix)
36+
'{BitVector.empty}
37+
}
38+
case other =>
39+
'{digitsToBitVector($digits, $radix)}
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package scodec.bits
2+
3+
import scala.quoted._
4+
import scala.quoted.matching._
5+
import scala.util.FromDigits
6+
7+
private[bits] trait ByteVectorPlatform { self: ByteVector.type =>
8+
given as FromDigits.WithRadix[ByteVector] = ByteVectorFromDigits.Instance
9+
}
10+
11+
private[bits] object ByteVectorFromDigits {
12+
13+
class Base extends FromDigits.WithRadix[ByteVector] {
14+
def fromDigits(digits: String, radix: Int): ByteVector =
15+
digitsToByteVector(digits, radix)
16+
}
17+
18+
private def digitsToByteVector(digits: String, radix: Int): ByteVector =
19+
if (radix == 16) ByteVector.fromValidHex(digits.tail)
20+
else throw FromDigits.MalformedNumber(s"unsupported radix $radix")
21+
22+
object Instance extends Base {
23+
override inline def fromDigits(digits: String): ByteVector =
24+
${digitsToByteVectorMacro('digits, Expr(10))}
25+
override inline def fromDigits(digits: String, radix: Int): ByteVector =
26+
${digitsToByteVectorMacro('digits, 'radix)}
27+
}
28+
29+
private def digitsToByteVectorMacro(digits: Expr[String], radix: Expr[Int])(given qctx: QuoteContext): Expr[ByteVector] =
30+
(digits, radix) match {
31+
case (Const(ds), Const(r)) =>
32+
if (r == 16) {
33+
'{ByteVector.fromValidHex($digits.tail)}
34+
} else {
35+
qctx.error(s"unsupported radix $r", radix)
36+
'{ByteVector.empty}
37+
}
38+
case other =>
39+
'{digitsToByteVector($digits, $radix)}
40+
}
41+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package scodec.bits
2+
3+
import scala.quoted._
4+
import scala.quoted.matching._
5+
6+
/**
7+
* Provides the `hex` string interpolator, which returns `ByteVector` instances from hexadecimal strings.
8+
*
9+
* @example {{{
10+
* scala> val b = hex"deadbeef"
11+
* val b: scodec.bits.ByteVector = ByteVector(4 bytes, 0xdeadbeef)
12+
* }}}
13+
*/
14+
inline def (ctx: StringContext).hex (args: => ByteVector*): ByteVector =
15+
${hexInterpolator('ctx, 'args)}
16+
17+
private def hexInterpolator(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[ByteVector]])(given qctx: QuoteContext): Expr[ByteVector] = {
18+
(strCtxExpr, argsExpr) match {
19+
case ('{ StringContext(${ExprSeq(parts)}: _*) }, ExprSeq(args)) =>
20+
val partValues: Seq[String] = parts.map { case p @ Const(part) =>
21+
if (ByteVector.fromHex(part).isEmpty)
22+
qctx.error("hexadecimal string literal may only contain characters [0-9a-fA-f]", p)
23+
part
24+
}
25+
if (partValues.size == 1)
26+
'{ByteVector.fromValidHex(${Expr(partValues.head)})}
27+
else {
28+
val init: Expr[StringBuilder] = '{ new StringBuilder().append(${Expr(partValues.head)}) }
29+
val bldr: Expr[StringBuilder] = args.zip(partValues.tail).foldLeft(init) { case (sb, (arg, part)) =>
30+
'{$sb.append($arg.toHex).append(${Expr(part)})}
31+
}
32+
'{ByteVector.fromValidHex($bldr.toString)}
33+
}
34+
}
35+
}
36+
37+
/**
38+
* Provides the `bin` string interpolator, which returns `BitVector` instances from binary strings.
39+
*
40+
* @example {{{
41+
* scala> val b = bin"1010101010"
42+
* val b: scodec.bits.BitVector = BitVector(10 bits, 0xaa8)
43+
* }}}
44+
*/
45+
inline def (ctx: StringContext).bin (args: => BitVector*): BitVector =
46+
${binInterpolator('ctx, 'args)}
47+
48+
private def binInterpolator(strCtxExpr: Expr[StringContext], argsExpr: Expr[Seq[BitVector]])(given qctx: QuoteContext): Expr[BitVector] = {
49+
(strCtxExpr, argsExpr) match {
50+
case ('{ StringContext(${ExprSeq(parts)}: _*) }, ExprSeq(args)) =>
51+
val partValues: Seq[String] = parts.map { case p @ Const(part) =>
52+
if (BitVector.fromBin(part).isEmpty)
53+
qctx.error("binary string literal may only contain characters [0, 1]", p)
54+
part
55+
}
56+
if (partValues.size == 1)
57+
'{BitVector.fromValidBin(${Expr(partValues.head)})}
58+
else {
59+
val init: Expr[StringBuilder] = '{ new StringBuilder().append(${Expr(partValues.head)}) }
60+
val bldr: Expr[StringBuilder] = args.zip(partValues.tail).foldLeft(init) { case (sb, (arg, part)) =>
61+
'{$sb.append($arg.toBin).append(${Expr(part)})}
62+
}
63+
'{BitVector.fromValidBin($bldr.toString)}
64+
}
65+
}
66+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scodec
2+
3+
import scala.language.experimental.macros
4+
5+
/**
6+
* Provides immutable data types for working with bits and bytes.
7+
*
8+
* @see [[BitVector]] and [[ByteVector]]
9+
*/
10+
package object bits
11+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scodec.bits
2+
3+
private[bits] trait BitVectorPlatform

0 commit comments

Comments
 (0)