Skip to content
This repository was archived by the owner on Jul 26, 2025. It is now read-only.

Commit 004b2a4

Browse files
committed
fix rfc4648 Base16, Base32, and Base64
1 parent ed4bdc7 commit 004b2a4

File tree

8 files changed

+361
-56
lines changed

8 files changed

+361
-56
lines changed

src/main/scala/com/github/fluency03/multibase/Base.scala

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,60 @@
11
package com.github.fluency03.multibase
22

3-
sealed abstract class Base(val name: String, val code: Char, val alphabet: String, val size: Int)
3+
sealed abstract class Base(
4+
val name: String,
5+
val code: Char,
6+
val alphabet: String,
7+
val pad: Option[Char]) {
8+
9+
lazy val alphabetPos: Map[Char, Int] = (for (i <- alphabet.indices) yield alphabet(i) -> i).toMap
10+
11+
}
12+
13+
sealed class Base16RFC4648(
14+
override val name: String,
15+
override val code: Char,
16+
override val alphabet: String,
17+
override val pad: Option[Char])
18+
extends Base(name, code, alphabet, pad)
19+
20+
sealed class Base32RFC4648(
21+
override val name: String,
22+
override val code: Char,
23+
override val alphabet: String,
24+
override val pad: Option[Char])
25+
extends Base(name, code, alphabet, pad)
26+
27+
sealed class Base64RFC4648(
28+
override val name: String,
29+
override val code: Char,
30+
override val alphabet: String,
31+
override val pad: Option[Char])
32+
extends Base(name, code, alphabet, pad)
33+
434

535
object Base {
6-
case object Identity extends Base("identity", 0x00, "", 0)
7-
case object Base1 extends Base("base1", '1', "1", 1)
8-
case object Base2 extends Base("base2", '0', "01", 2)
9-
case object Base8 extends Base("base8", '7', "01234567", 8)
10-
case object Base10 extends Base("base10", '9', "0123456789", 10)
11-
case object Base16 extends Base("base16", 'f', "0123456789abcdef", 16)
12-
case object Base16Upper extends Base("base16upper", 'F', "0123456789ABCDEF", 16)
13-
case object Base32 extends Base("base32", 'b', "abcdefghijklmnopqrstuvwxyz234567", 32)
14-
case object Base32Upper extends Base("base32upper", 'B', "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", 32)
15-
case object Base32Pad extends Base("base32pad", 'c', "abcdefghijklmnopqrstuvwxyz234567=", 32)
16-
case object Base32PadUpper extends Base("base32padupper", 'C', "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=", 32)
17-
case object Base32Hex extends Base("base32hex", 'v', "0123456789abcdefghijklmnopqrstuv", 32)
18-
case object Base32HexUpper extends Base("base32hexupper", 'V', "0123456789ABCDEFGHIJKLMNOPQRSTUV", 32)
19-
case object Base32HexPad extends Base("base32hexpad", 't', "0123456789abcdefghijklmnopqrstuv=", 32)
20-
case object Base32HexPadUpper extends Base("base32hexpadupper", 'T', "0123456789ABCDEFGHIJKLMNOPQRSTUV=", 32)
21-
case object Base32Z extends Base("base32z", 'h', "ybndrfg8ejkmcpqxot1uwisza345h769", 32)
22-
case object Base58Flickr extends Base("base58flickr", 'Z', "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", 58)
23-
case object Base58BTC extends Base("base58btc", 'z', "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", 58)
24-
case object Base64 extends Base("base64", 'm', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 64)
25-
case object Base64Pad extends Base("base64pad", 'M', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 64)
26-
case object Base64URL extends Base("base64url", 'u', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", 64)
27-
case object Base64URLPad extends Base("base64urlpad", 'U', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=", 64)
36+
case object Identity extends Base("identity", 0x00, "", None)
37+
case object Base1 extends Base("base1", '1', "1", None)
38+
case object Base2 extends Base("base2", '0', "01", None)
39+
case object Base8 extends Base("base8", '7', "01234567", None)
40+
case object Base10 extends Base("base10", '9', "0123456789", None)
41+
case object Base16 extends Base16RFC4648("base16", 'f', "0123456789abcdef", None)
42+
case object Base16Upper extends Base16RFC4648("base16upper", 'F', "0123456789ABCDEF", None)
43+
case object Base32 extends Base32RFC4648("base32", 'b', "abcdefghijklmnopqrstuvwxyz234567", None)
44+
case object Base32Upper extends Base32RFC4648("base32upper", 'B', "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", None)
45+
case object Base32Pad extends Base32RFC4648("base32pad", 'c', "abcdefghijklmnopqrstuvwxyz234567", Some('='))
46+
case object Base32PadUpper extends Base32RFC4648("base32padupper", 'C', "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", Some('='))
47+
case object Base32Hex extends Base32RFC4648("base32hex", 'v', "0123456789abcdefghijklmnopqrstuv", None)
48+
case object Base32HexUpper extends Base32RFC4648("base32hexupper", 'V', "0123456789ABCDEFGHIJKLMNOPQRSTUV", None)
49+
case object Base32HexPad extends Base32RFC4648("base32hexpad", 't', "0123456789abcdefghijklmnopqrstuv", Some('='))
50+
case object Base32HexPadUpper extends Base32RFC4648("base32hexpadupper", 'T', "0123456789ABCDEFGHIJKLMNOPQRSTUV", Some('='))
51+
case object Base32Z extends Base("base32z", 'h', "ybndrfg8ejkmcpqxot1uwisza345h769", None)
52+
case object Base58Flickr extends Base("base58flickr", 'Z', "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", None)
53+
case object Base58BTC extends Base("base58btc", 'z', "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", None)
54+
case object Base64 extends Base64RFC4648("base64", 'm', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", None)
55+
case object Base64Pad extends Base64RFC4648("base64pad", 'M', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", Some('='))
56+
case object Base64URL extends Base64RFC4648("base64url", 'u', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", None)
57+
case object Base64URLPad extends Base64RFC4648("base64urlpad", 'U', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", Some('='))
2858

2959
lazy val codes: Map[Char, Base] = Map(
3060
Identity.code -> Identity,

src/main/scala/com/github/fluency03/multibase/Base16Impl.scala

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ package com.github.fluency03.multibase
22

33
object Base16Impl {
44

5-
def encodeToLower(data: Array[Byte]): String = data.map("%02x".format(_)).mkString
5+
def encode(data: Array[Byte], base16: Base16RFC4648): String =
6+
data.flatMap(b => byteToChars(b, base16.alphabet.toCharArray)).mkString
67

7-
def encodeToUpper(data: Array[Byte]): String = data.map("%02X".format(_)).mkString
8+
def byteToChars(byte: Byte, alphabet: Array[Char]): Array[Char] = Array(
9+
alphabet(MASK_4BITS(byte >>> 4)),
10+
alphabet(MASK_4BITS(byte))
11+
)
812

9-
def decode(data: String): Array[Byte] = BigInt(data, 16).toByteArray
13+
def decode(data: String, base16: Base16RFC4648): Array[Byte] =
14+
data.toCharArray.grouped(2).map(g => twoCharsToByte(g, base16.alphabetPos)).toArray
15+
16+
def twoCharsToByte(tuple: Array[Char], pos: Map[Char, Int]): Byte =
17+
(pos(tuple(0)) << 4 | pos(tuple(1))).toByte
1018

1119
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package com.github.fluency03.multibase
2+
3+
import scala.annotation.tailrec
4+
5+
object Base32Impl {
6+
// operator precedence in descending order: >>> or <<, &, |
7+
private val BitGroupSize: Int = 5
8+
private val CommonGroupSize: Int = 8
9+
10+
def encode(data: Array[Byte], base32: Base32RFC4648): String =
11+
if (data.isEmpty) ""
12+
else data.grouped(BitGroupSize)
13+
.map(g => encodeBytes(g, base32.alphabet.toCharArray, base32.pad))
14+
.mkString
15+
16+
private def encodeBytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
17+
group.length match {
18+
case 1 => encode1Bytes(group, alphabet, pad)
19+
case 2 => encode2Bytes(group, alphabet, pad)
20+
case 3 => encode3Bytes(group, alphabet, pad)
21+
case 4 => encode4Bytes(group, alphabet, pad)
22+
case 5 => encode5Bytes(group, alphabet)
23+
}
24+
25+
private def encode1Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
26+
Seq(
27+
alphabet(MASK_5BITS(group(0) >>> 3)), // 5
28+
alphabet(MASK_3BITS(group(0)) << 2) // 3
29+
).mkString + pad.map(_.toString).getOrElse("") * 6
30+
31+
private def encode2Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
32+
Seq(
33+
alphabet(MASK_5BITS(group(0) >>> 3)), // 5
34+
alphabet(MASK_3BITS(group(0)) << 2 | MASK_2BITS(group(1) >>> 6)), // 3 2
35+
alphabet(MASK_5BITS(group(1) >>> 1)), // 5
36+
alphabet(MASK_1BITS(group(1)) << 4), // 1
37+
).mkString + pad.map(_.toString).getOrElse("") * 4
38+
39+
private def encode3Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
40+
Seq(
41+
alphabet(MASK_5BITS(group(0) >>> 3)), // 5
42+
alphabet(MASK_3BITS(group(0)) << 2 | MASK_2BITS(group(1) >>> 6)), // 3 2
43+
alphabet(MASK_5BITS(group(1) >>> 1)), // 5
44+
alphabet(MASK_1BITS(group(1)) << 4 | MASK_4BITS(group(2) >>> 4)), // 1 4
45+
alphabet(MASK_4BITS(group(2)) << 1) // 4
46+
).mkString + pad.map(_.toString).getOrElse("") * 3
47+
48+
private def encode4Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
49+
Seq(
50+
alphabet(MASK_5BITS(group(0) >>> 3)), // 5
51+
alphabet(MASK_3BITS(group(0)) << 2 | MASK_2BITS(group(1) >>> 6)), // 3 2
52+
alphabet(MASK_5BITS(group(1) >>> 1)), // 5
53+
alphabet(MASK_1BITS(group(1)) << 4 | MASK_4BITS(group(2) >>> 4)), // 1 4
54+
alphabet(MASK_4BITS(group(2)) << 1 | MASK_1BITS(group(3) >>> 7)), // 4 1
55+
alphabet(MASK_5BITS(group(3) >>> 2)), // 5
56+
alphabet(MASK_2BITS(group(3)) << 3) // 2
57+
).mkString + pad.map(_.toString).getOrElse("")
58+
59+
private def encode5Bytes(group: Array[Byte], alphabet: Array[Char]): String =
60+
Seq(
61+
alphabet(MASK_5BITS(group(0) >>> 3)), // 5
62+
alphabet(MASK_3BITS(group(0)) << 2 | MASK_2BITS(group(1) >>> 6)), // 3 2
63+
alphabet(MASK_5BITS(group(1) >>> 1)), // 5
64+
alphabet(MASK_1BITS(group(1)) << 4 | MASK_4BITS(group(2) >>> 4)), // 1 4
65+
alphabet(MASK_4BITS(group(2)) << 1 | MASK_1BITS(group(3) >>> 7)), // 4 1
66+
alphabet(MASK_5BITS(group(3) >>> 2)), // 5
67+
alphabet(MASK_2BITS(group(3)) << 3 | MASK_3BITS(group(4) >>> 5)), // 2 3
68+
alphabet(MASK_5BITS(group(4))) // 5
69+
).mkString
70+
71+
72+
73+
def decode(data: String, base32: Base32RFC4648): Array[Byte] = {
74+
val chars = data.toCharArray
75+
val length = chars.length
76+
val pads = if (base32.pad.isEmpty) 0 else numPads(chars, 0, length - 1, base32.pad.get)
77+
val pos = base32.alphabetPos
78+
chars.slice(0, length - pads).grouped(CommonGroupSize)
79+
.map(g => decodeBytes(g, pos))
80+
.foldLeft(Array[Byte]())( _ ++ _ )
81+
}
82+
83+
@tailrec
84+
private def numPads(chars: Array[Char], pads: Int, last: Int, pad: Char): Int =
85+
if (last < 0 || pads >= BitGroupSize || chars(last) != pad) pads
86+
else numPads(chars, pads + 1, last - 1, pad)
87+
88+
private def decodeBytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] =
89+
group.length match {
90+
case 1 => decode1Byte(group, pos)
91+
case 2 => decode2Bytes(group, pos)
92+
case 3 => decode3Bytes(group, pos)
93+
case 4 => decode4Bytes(group, pos)
94+
case 5 => decode5Bytes(group, pos)
95+
case 6 => decode6Bytes(group, pos)
96+
case 7 => decode7Bytes(group, pos)
97+
case 8 => decode8Bytes(group, pos)
98+
}
99+
100+
private def decode8Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
101+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte, // 5 3
102+
(MASK_2BITS(pos(group(1))) << 6 | pos(group(2)) << 1 | MASK_1BITS(pos(group(3)) >>> 4)).toByte, // 2 5 1
103+
(MASK_4BITS(pos(group(3))) << 4 | MASK_4BITS(pos(group(4)) >>> 1)).toByte, // 4 4
104+
(MASK_1BITS(pos(group(4))) << 7 | pos(group(5)) << 2 | MASK_2BITS(pos(group(6)) >>> 3)).toByte, // 1 5 2
105+
(MASK_3BITS(pos(group(6))) << 5 | pos(group(7))).toByte // 3 5
106+
)
107+
108+
private def decode7Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
109+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte, // 5 3
110+
(MASK_2BITS(pos(group(1))) << 6 | pos(group(2)) << 1 | MASK_1BITS(pos(group(3)) >>> 4)).toByte, // 2 5 1
111+
(MASK_4BITS(pos(group(3))) << 4 | MASK_4BITS(pos(group(4)) >>> 1)).toByte, // 4 4
112+
(MASK_1BITS(pos(group(4))) << 7 | pos(group(5)) << 2 | MASK_2BITS(pos(group(6)) >>> 3)).toByte // 1 5 2
113+
)
114+
115+
private def decode6Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
116+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte, // 5 3
117+
(MASK_2BITS(pos(group(1))) << 6 | pos(group(2)) << 1 | MASK_1BITS(pos(group(3)) >>> 4)).toByte, // 2 5 1
118+
(MASK_4BITS(pos(group(3))) << 4 | MASK_4BITS(pos(group(4)) >>> 1)).toByte // 4 4
119+
)
120+
121+
private def decode5Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
122+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte, // 5 3
123+
(MASK_2BITS(pos(group(1))) << 6 | pos(group(2)) << 1 | MASK_1BITS(pos(group(3)) >>> 4)).toByte, // 2 5 1
124+
(MASK_4BITS(pos(group(3))) << 4 | MASK_4BITS(pos(group(4)) >>> 1)).toByte // 4 4
125+
)
126+
127+
private def decode4Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
128+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte, // 5 3
129+
(MASK_2BITS(pos(group(1))) << 6 | pos(group(2)) << 1 | MASK_1BITS(pos(group(3)) >>> 4)).toByte // 2 5 1
130+
)
131+
132+
private def decode3Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
133+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte // 5 3
134+
)
135+
136+
private def decode2Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
137+
(pos(group(0)) << 3 | MASK_3BITS(pos(group(1)) >>> 2)).toByte // 5 3
138+
)
139+
140+
private def decode1Byte(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array()
141+
142+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.github.fluency03.multibase
2+
3+
import scala.annotation.tailrec
4+
5+
object Base64Impl {
6+
// operator precedence in descending order: >>> or <<, &, |
7+
private val BitGroupSize: Int = 3
8+
private val CommonGroupSize: Int = 4
9+
10+
def encode(data: Array[Byte], base64: Base64RFC4648): String =
11+
if (data.isEmpty) ""
12+
else data.grouped(BitGroupSize)
13+
.map(g => encodeBytes(g, base64.alphabet.toCharArray, base64.pad))
14+
.mkString
15+
16+
private def encodeBytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
17+
group.length match {
18+
case 1 => encode1Bytes(group, alphabet, pad)
19+
case 2 => encode2Bytes(group, alphabet, pad)
20+
case 3 => encode3Bytes(group, alphabet, pad)
21+
}
22+
23+
private def encode1Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
24+
Seq(
25+
alphabet(MASK_6BITS(group(0) >>> 2)), // 6
26+
alphabet(MASK_2BITS(group(0)) << 4) // 2
27+
).mkString + pad.map(_.toString).getOrElse("") * 2
28+
29+
private def encode2Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
30+
Seq(
31+
alphabet(MASK_6BITS(group(0) >>> 2)), // 6
32+
alphabet(MASK_2BITS(group(0)) << 4 | MASK_4BITS(group(1) >>> 4)), // 2 4
33+
alphabet(MASK_4BITS(group(1)) << 2) // 4
34+
).mkString + pad.map(_.toString).getOrElse("")
35+
36+
private def encode3Bytes(group: Array[Byte], alphabet: Array[Char], pad: Option[Char]): String =
37+
Seq(
38+
alphabet(MASK_6BITS(group(0) >>> 2)), // 6
39+
alphabet(MASK_2BITS(group(0)) << 4 | MASK_4BITS(group(1) >>> 4)), // 2 4
40+
alphabet(MASK_4BITS(group(1)) << 2 | MASK_2BITS(group(2) >>> 6)), // 4 2
41+
alphabet(MASK_6BITS(group(2))) // 6
42+
).mkString
43+
44+
45+
def decode(data: String, base64: Base64RFC4648): Array[Byte] = {
46+
val chars = data.toCharArray
47+
val length = chars.length
48+
val pads = if (base64.pad.isEmpty) 0 else numPads(chars, 0, length - 1, base64.pad.get)
49+
val pos = base64.alphabetPos
50+
chars.slice(0, length - pads).grouped(CommonGroupSize)
51+
.map(g => decodeBytes(g, pos))
52+
.foldLeft(Array[Byte]())( _ ++ _ )
53+
}
54+
55+
@tailrec
56+
private def numPads(chars: Array[Char], pads: Int, last: Int, pad: Char): Int =
57+
if (last < 0 || pads >= BitGroupSize || chars(last) != pad) pads
58+
else numPads(chars, pads + 1, last - 1, pad)
59+
60+
private def decodeBytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] =
61+
group.length match {
62+
case 1 => decode1Byte(group, pos)
63+
case 2 => decode2Bytes(group, pos)
64+
case 3 => decode3Bytes(group, pos)
65+
case 4 => decode4Bytes(group, pos)
66+
}
67+
68+
private def decode4Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
69+
(pos(group(0)) << 2 | MASK_2BITS(pos(group(1)) >>> 4)).toByte, // 6 2
70+
(MASK_4BITS(pos(group(1))) << 4 | MASK_4BITS(pos(group(2)) >>> 2)).toByte, // 4 4
71+
(MASK_2BITS(pos(group(2))) << 6 | pos(group(3))).toByte // 2 6
72+
)
73+
74+
private def decode3Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
75+
(pos(group(0)) << 2 | MASK_2BITS(pos(group(1)) >>> 4)).toByte, // 6 2
76+
(MASK_4BITS(pos(group(1))) << 4 | MASK_4BITS(pos(group(2)) >>> 2)).toByte // 4 4
77+
)
78+
79+
private def decode2Bytes(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array(
80+
(pos(group(0)) << 2 | MASK_2BITS(pos(group(1)) >>> 4)).toByte // 6 2
81+
)
82+
83+
private def decode1Byte(group: Array[Char], pos: Map[Char, Int]): Array[Byte] = Array()
84+
85+
}

src/main/scala/com/github/fluency03/multibase/BaseN.scala renamed to src/main/scala/com/github/fluency03/multibase/BaseNImpl.scala

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,32 @@ package com.github.fluency03.multibase
22

33
import scala.annotation.tailrec
44

5-
object BaseN {
5+
object BaseNImpl {
66

77
def encode(base: Base, data: Array[Byte]): String =
88
if (data.isEmpty) ""
9-
else {
10-
val alphabet: Array[Char] = base.alphabet.toCharArray
11-
val baseSize = base.size
12-
val ZERO = alphabet(0)
9+
else {
10+
val alphabet: Array[Char] = base.alphabet.toCharArray
11+
val baseSize = base.alphabet.length
12+
val ZERO = alphabet(0)
1313

14-
@tailrec
15-
def buildBase(res: String, bi: BigInt): String =
16-
if (bi <= 0) res
17-
else buildBase(alphabet((bi % baseSize).toInt) + res, bi / baseSize)
14+
@tailrec
15+
def buildBase(res: String, bi: BigInt): String =
16+
if (bi <= 0) res
17+
else buildBase(alphabet((bi % baseSize).toInt) + res, bi / baseSize)
1818

19-
@tailrec
20-
def confirmZeroByte(res: String, bytes: Array[Byte], idx: Int): String =
21-
if (bytes(idx) != 0 || idx >= bytes.length) res
22-
else confirmZeroByte(ZERO + res, bytes, idx + 1)
19+
@tailrec
20+
def confirmZeroByte(res: String, bytes: Array[Byte], idx: Int): String =
21+
if (bytes(idx) != 0 || idx >= bytes.length) res
22+
else confirmZeroByte(ZERO + res, bytes, idx + 1)
2323

24-
confirmZeroByte(buildBase("", BigInt(data)), data, 0)
25-
}
24+
confirmZeroByte(buildBase("", BigInt(1, data)), data, 0)
25+
}
2626

2727

2828
def decode(base: Base, data: String): Array[Byte] = {
2929
val alphabet: Array[Char] = base.alphabet.toCharArray
30-
val baseSize = base.size
30+
val baseSize = base.alphabet.length
3131
val ZERO = alphabet(0)
3232

3333
@tailrec

0 commit comments

Comments
 (0)