Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: Added decode logic * Added comparatoin with old implementation * Added test for illegal arguments * Adds comment for referenced libraries
- Loading branch information
Showing
10 changed files
with
279 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package jkugiya.ulid | ||
|
||
import java.nio.ByteBuffer | ||
|
||
private[ulid] object Base32Codec { | ||
|
||
private val toBase32 = Array( | ||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | ||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', | ||
'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', | ||
'Y', 'Z' | ||
) | ||
|
||
def encode(ulid: ULID): String = { | ||
val chars = new Array[Char](26) | ||
// timestamp(10byte) | ||
// take last 5bit | ||
val timestamp = ulid.time | ||
chars(9) = toBase32((timestamp & 0x1F).toInt) | ||
chars(8) = toBase32((timestamp >>> 5 & 0x1F).toInt) | ||
chars(7) = toBase32((timestamp >>> 10 & 0x1F).toInt) | ||
chars(6) = toBase32((timestamp >>> 15 & 0x1F).toInt) | ||
chars(5) = toBase32((timestamp >>> 20 & 0x1F).toInt) | ||
chars(4) = toBase32((timestamp >>> 25 & 0x1F).toInt) | ||
chars(3) = toBase32((timestamp >>> 30 & 0x1F).toInt) | ||
chars(2) = toBase32((timestamp >>> 35 & 0x1F).toInt) | ||
chars(1) = toBase32((timestamp >>> 40 & 0x1F).toInt) | ||
chars(0) = toBase32((timestamp >>> 45 & 0x1F).toInt) | ||
// randomness(16byte / 80bits) | ||
// take 5bit | ||
val randomness = ulid.originalRandomness | ||
chars(10) = toBase32(((randomness(0) & 0xFF) >>> 3) & 0x1F) | ||
chars(11) = toBase32(((randomness(0) << 2) | ((randomness(1) & 0xFF) >>> 6)) & 0x1F) | ||
chars(12) = toBase32(((randomness(1) & 0xFF) >>> 1) & 0x1F) | ||
chars(13) = toBase32(((randomness(1) << 4) | ((randomness(2) & 0xFF) >>> 4)) & 0x1F) | ||
chars(14) = toBase32(((randomness(2) << 1) | ((randomness(3) & 0xFF) >>> 7)) & 0x1F) | ||
chars(15) = toBase32(((randomness(3) & 0xFF) >>> 2) & 0x1F) | ||
chars(16) = toBase32(((randomness(3) << 3) | ((randomness(4) & 0xFF) >>> 5)) & 0x1F) | ||
chars(17) = toBase32(randomness(4) & 0x1F) | ||
chars(18) = toBase32((randomness(5) & 0xFF) >>> 3 & 0x1F) | ||
chars(19) = toBase32(((randomness(5) << 2) | ((randomness(6) & 0xFF) >>> 6)) & 0x1F) | ||
chars(20) = toBase32(((randomness(6) & 0xFF) >>> 1) & 0x1F) | ||
chars(21) = toBase32(((randomness(6) << 4) | ((randomness(7) & 0xFF) >>> 4)) & 0x1F) | ||
chars(22) = toBase32(((randomness(7) << 1) | ((randomness(8) & 0xFF) >>> 7)) & 0x1F) | ||
chars(23) = toBase32(((randomness(8) & 0xFF) >>> 2) & 0x1F) | ||
chars(24) = toBase32(((randomness(8) << 3) | ((randomness(9) & 0xFF) >>> 5)) & 0x1F) | ||
chars(25) = toBase32(randomness(9) & 0x1F) | ||
new String(chars) | ||
} | ||
|
||
def decode(base32: String): ULID = { | ||
if (base32.length != 26) { | ||
throw new IllegalArgumentException("The length of Base32 string should be 26") | ||
} | ||
val chars = base32.toCharArray | ||
var i = 0 | ||
var time = 0L | ||
var next40 = 0L | ||
var last40 = 0L | ||
def bit = { | ||
val bit = toBase32.indexOf(chars(i)) | ||
if (bit < 0) { | ||
throw new IllegalArgumentException(s"Given string contains invalid character. (${chars(i)})") | ||
} | ||
bit | ||
} | ||
while(i < 10) { | ||
time = (time << 5) | bit | ||
i += 1 | ||
} | ||
while (i < 18) { | ||
next40 = (next40 << 5) | bit | ||
i += 1 | ||
} | ||
while(i < 26) { | ||
last40 = (last40 << 5) | bit | ||
i += 1 | ||
} | ||
val first64 = next40 << 24 | (last40 >>> 16) | ||
val last16: Short = (last40 & 0xFFFF).toShort | ||
val buffer = ByteBuffer.allocate(10) | ||
buffer.putLong(first64).putShort(last16) | ||
val ulid = new ULID(time, buffer.array()) | ||
ulid | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package jkugiya.ulid | ||
|
||
import java.nio.ByteBuffer | ||
|
||
import jkugiya.ulid.ULID.ByteLengthOfULID | ||
|
||
private[ulid] object BinaryCodec { | ||
|
||
def encode(ulid: ULID): Array[Byte] = { | ||
val buffer = ByteBuffer.allocate(ByteLengthOfULID) | ||
buffer.putLong(ulid.time << 16) // takes 48bit only | ||
buffer.position(6) | ||
buffer.put(ulid.originalRandomness) | ||
buffer.array() | ||
} | ||
|
||
def decode(binary: Array[Byte]): ULID = { | ||
if (binary.length != 16) { | ||
throw new IllegalArgumentException("Binary length should be 16.") | ||
} | ||
val binaryBuffer = ByteBuffer.wrap(binary) | ||
val m = binaryBuffer.getLong | ||
val l = binaryBuffer.getLong | ||
val time = m >>> 16 | ||
val next64 = (m << 48 | l >>> 16) | ||
val last16 = (l & 0xFFFF).toShort | ||
val buffer = ByteBuffer.allocate(10) | ||
buffer.putLong(next64).putShort(last16) | ||
new ULID(time, buffer.array()) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package jkugiya.ulid | ||
|
||
import java.nio.ByteBuffer | ||
import java.util.UUID | ||
|
||
private[ulid] object UUIDCodec { | ||
|
||
def encode(ulid: ULID): UUID = { | ||
val binary = ulid.binary | ||
val buffer = ByteBuffer.wrap(binary) | ||
new UUID(buffer.getLong(), buffer.getLong) | ||
} | ||
|
||
def decode(uuid: UUID): ULID = { | ||
val m = uuid.getMostSignificantBits | ||
val l = uuid.getLeastSignificantBits | ||
val time = m >>> 16 | ||
val next64 = (m << 48 | l >>> 16) | ||
val last16 = (l & 0xFFFF).toShort | ||
val buffer = ByteBuffer.allocate(10) | ||
buffer.putLong(next64).putShort(last16) | ||
new ULID(time, buffer.array()) | ||
} | ||
|
||
} | ||
|
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package jkugiya.ulid | ||
|
||
import java.nio.ByteBuffer | ||
|
||
object Base32Logics { | ||
private val toBase32 = Array( | ||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', | ||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', | ||
'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', | ||
'Y', 'Z' | ||
) | ||
|
||
// old implementation | ||
def logic1(time: Long, randomness: Array[Byte]): String = { | ||
val ulid = new ULID(time, randomness) | ||
val binary = ulid.binary | ||
(Stream(0, 0) ++ (0 to 127).toStream.map { i => | ||
val bitValueOfI = (binary(i / 8) << (i % 8)) & 0x80 | ||
if (bitValueOfI == 0) 0 | ||
else 1 | ||
}).grouped(5).map { buffer => | ||
val bit5 = buffer.toArray | ||
val index = | ||
(bit5(0) << 4) | | ||
(bit5(1) << 3) | | ||
(bit5(2) << 2) | | ||
(bit5(3) << 1) | | ||
bit5(4) | ||
toBase32(index) | ||
}.mkString | ||
} | ||
|
||
// old implementation | ||
val MaskForTake5 = 0xf800000000000000L | ||
def logic2(time: Long, randomness: Array[Byte]): String = { | ||
val ulid = new ULID(time, randomness) | ||
val binary = ulid.binary // len = 16 | ||
val chars = new Array[Char](26) | ||
val mostSigBits = ByteBuffer.wrap(binary.slice(0, 8)).getLong | ||
val leastSigBits = ByteBuffer.wrap(binary.slice(8, 16)).getLong | ||
var i = 0 | ||
// first 60bits(empty 2bits + 58bits) | ||
val firstBits = mostSigBits >>> 6 << 4 | ||
while(i < 12) { | ||
chars(i) = toBase32(((firstBits << (i * 5) & MaskForTake5) >>> 59).toInt) | ||
i += 1 | ||
} | ||
// second 60bits | ||
val secondBits = ((mostSigBits << 58) | (leastSigBits >>> 10 << 4)) | ||
i = 12 | ||
while(i < 24) { | ||
chars(i) = toBase32(((secondBits << (i - 12) * 5 & MaskForTake5) >>> 59).toInt) | ||
i += 1 | ||
} | ||
// third 10 bits | ||
i = 24 | ||
val thirdBits = leastSigBits << 54 | ||
while(i < 26) { | ||
chars(i) = toBase32(((thirdBits << (i - 24) * 5 & MaskForTake5) >>> 59).toInt) | ||
i += 1 | ||
} | ||
chars.mkString | ||
} | ||
} |
Oops, something went wrong.