Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/main/scala/uuid.scala → src/main/scala/id/UUID.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,21 @@ object UUID:
def apply(msb: Byte, lsb: Byte): CountryCode =
CountryCode(String(Array(msb, lsb), "US-ASCII"))

def decodeTarget(lsb: Long): CountryCode =
private def decodeTarget(lsb: Long): CountryCode =
val node5 = (lsb & 0x0000_0000_0000_001f) + 0x41
val node4 = ((lsb >>> 5) & 0x0000_0000_0000_001f) + 0x41
CountryCode(node4.toByte, node5.toByte)

def decodeSource(lsb: Long): CountryCode =
private def decodeSource(lsb: Long): CountryCode =
val node3 = ((lsb >>> 10) & 0x0000_0000_0000_001f) + 0x41
val node2 = ((lsb >>> 15) & 0x0000_0000_0000_001f) + 0x41
CountryCode(node2.toByte, node3.toByte)

private def encode(source: CountryCode, target: CountryCode)(lsb: Long): Long =
def extractor(accumulator: Long, byte: Byte): Long = (accumulator << 5) + ((byte - 0x41) & 0x1f)
val scode = source.asBytes.foldLeft(0L)(extractor)
val tcode = target.asBytes.foldLeft(0L)(extractor)
(lsb & 0xffff_ffff_fff0_0000L) + (scode << 10) + tcode
val sourceCode = source.asBytes.foldLeft(0L)(extractor)
val targetCode = target.asBytes.foldLeft(0L)(extractor)
(lsb & 0xffff_ffff_fff0_0000L) + (sourceCode << 10) + targetCode

def iso3166(source: CountryCode, target: CountryCode, from: JavaUUID = JavaUUID.randomUUID): UUID =
assert(from.asScala.version.contains(Version.RandomBased), s"invalid java uuid version: ${from.asScala.version}")
Expand Down Expand Up @@ -102,6 +102,7 @@ object compat:
type JavaUUID = java.util.UUID

object JavaUUID:

def apply(msb: Long, lsb: Long): JavaUUID =
java.util.UUID.apply(msb, lsb)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package id
package test

import org.scalacheck.*
import id.compat.JavaUUID

object JavaUUIDCompatibilityProps extends Properties("uuid.compat"):

import generators.*
import Prop.*
import generators.*

property("applyIsJavaUUIDCompatible") =
forAll(javaApplyUUIDs)(isJavaUUIDCompatible)
Expand All @@ -18,11 +16,9 @@ object JavaUUIDCompatibilityProps extends Properties("uuid.compat"):
property("v3IsJavaUUIDCompatible - MD5HashBased") =
forAll(javaVersion3UUIDs)(isJavaUUIDVersion3Compatible)

import id.*
import compat.*
import Variant.*
import Version.*

import compat.*
import compat.JavaUUID.*

def isJavaUUIDCompatible(javaUUID: JavaUUID): Boolean =
Expand All @@ -39,39 +35,40 @@ object JavaUUIDCompatibilityProps extends Properties("uuid.compat"):

def isJavaUUIDVersionCompatible(javaUUID: JavaUUID): Boolean =
javaUUID.version match
case 1 => javaUUID.asScala.version == Some(TimeBased)
case 2 => javaUUID.asScala.version == Some(DCESecurityBased)
case 3 => javaUUID.asScala.version == Some(MD5HashBased)
case 4 => javaUUID.asScala.version == Some(RandomBased)
case 5 => javaUUID.asScala.version == Some(SHA1HashBased)
case 6 => javaUUID.asScala.version == Some(Version6)
case 7 => javaUUID.asScala.version == Some(Version7)
case 8 => javaUUID.asScala.version == Some(ISO3166Based)
case _ => javaUUID.asScala.version == None
case 1 => javaUUID.asScala.version.contains(TimeBased)
case 2 => javaUUID.asScala.version.contains(DCESecurityBased)
case 3 => javaUUID.asScala.version.contains(MD5HashBased)
case 4 => javaUUID.asScala.version.contains(RandomBased)
case 5 => javaUUID.asScala.version.contains(SHA1HashBased)
case 6 => javaUUID.asScala.version.contains(Version6)
case 7 => javaUUID.asScala.version.contains(Version7)
case 8 => javaUUID.asScala.version.contains(ISO3166Based)
case _ => javaUUID.asScala.version.isEmpty

def isJavaUUIDVersion4Compatible(javaUUID: JavaUUID): Boolean =
val isRandomBased = javaUUID.asScala.version == Some(RandomBased)
val isRandomBased = javaUUID.asScala.version.contains(RandomBased)
isRandomBased && isJavaUUIDVariantCompatible(javaUUID)

def isJavaUUIDVersion3Compatible(javaUUID: JavaUUID, name: Array[Byte]): Boolean =
val isNameBased = java.util.UUID.nameUUIDFromBytes(name) == javaUUID
val isMD5Hased = javaUUID.asScala.version == Some(MD5HashBased)
isNameBased && isMD5Hased && isJavaUUIDVariantCompatible(javaUUID)
val isMD5Hashed = javaUUID.asScala.version.contains(MD5HashBased)
isNameBased && isMD5Hashed && isJavaUUIDVariantCompatible(javaUUID)

object generators:

import Arbitrary.*

val javaApplyUUIDs: Gen[JavaUUID] =
for {
for
msb <- arbitrary[Long]
lsb <- arbitrary[Long]
} yield JavaUUID(msb, lsb)
yield
JavaUUID(msb, lsb)

val javaVersion4UUIDs: Gen[JavaUUID] =
Gen.map(_ => JavaUUID.randomUUID)

val javaVersion3UUIDs: Gen[(JavaUUID,Array[Byte])] =
val javaVersion3UUIDs: Gen[(JavaUUID, Array[Byte])] =
Gen
.containerOf[Array,Byte](arbitrary[Byte])
.containerOf[Array, Byte](arbitrary[Byte])
.map(bytes => (JavaUUID.nameUUIDFromBytes(bytes), bytes))
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
package id

import org.scalacheck.*
import id.UUID.CountryCode

object UUIDProps extends Properties("uuid.UUID"):

import id.*

import util.*
import util.given
import Prop.*
import UUID.*
import Variant.*
import Version.*

property("variant") = forAll { (msb: Long, lsb: Long) =>
property("variant") = forAll: (msb: Long, lsb: Long) =>
UUID(msb, lsb).variant match
case Reserved => lsb.toBinaryString.startsWith("111")
case MicrosoftBackwardsCompatible => lsb.toBinaryString.startsWith("110")
case LeachSalz => lsb.toBinaryString.startsWith("10")
case NCSBackwardsCompatible => true
}

property("version") = forAll { (msb: Long, lsb: Long) =>
property("version") = forAll: (msb: Long, lsb: Long) =>
UUID(msb, lsb).version match
case Some(TimeBased) => (msb & 0xF000L) == 0x1000L
case Some(DCESecurityBased) => (msb & 0xF000L) == 0x2000L
Expand All @@ -32,18 +27,17 @@ object UUIDProps extends Properties("uuid.UUID"):
case Some(Version7) => (msb & 0xF000L) == 0x7000L
case Some(ISO3166Based) => (msb & 0xF000L) == 0x8000L
case None => ((msb & 0xF000L) == 0x0000L) || ((msb & 0xF000L) >= 0x7000L)
}

property("iso3166") = forAll(isoSourcesAndTargets) { (source: CountryCode, target: CountryCode) =>
property("iso3166") = forAll(isoSourcesAndTargets): (source: CountryCode, target: CountryCode) =>
val uuid = UUID.iso3166(source, target)
val correctType = (uuid.variant == LeachSalz) && (uuid.version.get == ISO3166Based)
val correctData = (uuid.sourceCountryCode.get == source) && (uuid.targetCountryCode.get == target)
correctType && correctData
}

val isoSourcesAndTargets: Gen[(CountryCode, CountryCode)] =
def isoSourcesAndTargets: Gen[(CountryCode, CountryCode)] =
import compat.JavaCountryCodes
for {
for
source <- Gen.oneOf(JavaCountryCodes).map(CountryCode.apply)
target <- Gen.oneOf(JavaCountryCodes).map(CountryCode.apply)
} yield (source, target)
yield
(source, target)