Skip to content

Commit

Permalink
More efficient encoding of UUIDs (#1295)
Browse files Browse the repository at this point in the history
  • Loading branch information
plokhotnyuk authored Feb 9, 2025
1 parent 2d6cc6f commit addd7ff
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 9 deletions.
51 changes: 49 additions & 2 deletions zio-json/js/src/main/scala/zio/json/internal/SafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package zio.json.internal

import java.util.UUID

/**
* Total, fast, number parsing.
*
Expand Down Expand Up @@ -75,13 +77,19 @@ object SafeNumbers {
def toString(x: Double): String = {
val out = new FastStringWrite(24)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: Float): String = {
val out = new FastStringWrite(16)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: UUID): String = {
val out = writes.get
write(x, out)
out.buffer.toString
}

// Based on the amazing work of Raffaello Giulietti
Expand Down Expand Up @@ -303,6 +311,26 @@ object SafeNumbers {
}
}

def write(x: UUID, out: Write): Unit = {
val ds = lowerCaseHexDigits
val msb = x.getMostSignificantBits
val lsb = x.getLeastSignificantBits
val msb1 = (msb >> 32).toInt
out.write(ds(msb1 >>> 24), ds(msb1 >> 16 & 0xff), ds(msb1 >> 8 & 0xff), ds(msb1 & 0xff))
out.write('-')
val msb2 = msb.toInt
out.write(ds(msb2 >>> 24), ds(msb2 >> 16 & 0xff))
out.write('-')
out.write(ds(msb2 >> 8 & 0xff), ds(msb2 & 0xff))
out.write('-')
val lsb1 = (lsb >>> 32).toInt
out.write(ds(lsb1 >>> 24), ds(lsb1 >> 16 & 0xff))
out.write('-')
out.write(ds(lsb1 >> 8 & 0xff), ds(lsb1 & 0xff))
val lsb2 = lsb.toInt
out.write(ds(lsb2 >>> 24), ds(lsb2 >> 16 & 0xff), ds(lsb2 >> 8 & 0xff), ds(lsb2 & 0xff))
}

private[json] def writeNano(x: Int, out: Write): Unit = {
out.write('.')
var coeff = 100000000
Expand Down Expand Up @@ -549,6 +577,25 @@ object SafeNumbers {
else 10
}

private final val lowerCaseHexDigits: Array[Short] = Array(
12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160,
12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161,
12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162,
12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163,
12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164,
12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165,
12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166,
12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167,
12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168,
12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169,
12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209,
12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210,
12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211,
12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212,
12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213,
12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214
)

private[this] val gs: Array[Long] = Array(
5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L,
1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L,
Expand Down
51 changes: 49 additions & 2 deletions zio-json/jvm/src/main/scala/zio/json/internal/SafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package zio.json.internal

import java.util.UUID

/**
* Total, fast, number parsing.
*
Expand Down Expand Up @@ -75,13 +77,19 @@ object SafeNumbers {
def toString(x: Double): String = {
val out = new FastStringWrite(24)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: Float): String = {
val out = new FastStringWrite(16)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: UUID): String = {
val out = writes.get
write(x, out)
out.buffer.toString
}

// Based on the amazing work of Raffaello Giulietti
Expand Down Expand Up @@ -294,6 +302,26 @@ object SafeNumbers {
}
}

def write(x: UUID, out: Write): Unit = {
val ds = lowerCaseHexDigits
val msb = x.getMostSignificantBits
val lsb = x.getLeastSignificantBits
val msb1 = (msb >> 32).toInt
out.write(ds(msb1 >>> 24), ds(msb1 >> 16 & 0xff), ds(msb1 >> 8 & 0xff), ds(msb1 & 0xff))
out.write('-')
val msb2 = msb.toInt
out.write(ds(msb2 >>> 24), ds(msb2 >> 16 & 0xff))
out.write('-')
out.write(ds(msb2 >> 8 & 0xff), ds(msb2 & 0xff))
out.write('-')
val lsb1 = (lsb >>> 32).toInt
out.write(ds(lsb1 >>> 24), ds(lsb1 >> 16 & 0xff))
out.write('-')
out.write(ds(lsb1 >> 8 & 0xff), ds(lsb1 & 0xff))
val lsb2 = lsb.toInt
out.write(ds(lsb2 >>> 24), ds(lsb2 >> 16 & 0xff), ds(lsb2 >> 8 & 0xff), ds(lsb2 & 0xff))
}

private[json] def writeNano(x: Int, out: Write): Unit = {
out.write('.')
var coeff = 100000000
Expand Down Expand Up @@ -482,6 +510,25 @@ object SafeNumbers {
576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L
)

private final val lowerCaseHexDigits: Array[Short] = Array(
12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160,
12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161,
12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162,
12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163,
12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164,
12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165,
12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166,
12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167,
12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168,
12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169,
12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209,
12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210,
12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211,
12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212,
12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213,
12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214
)

private[this] val gs: Array[Long] = Array(
5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L,
1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L,
Expand Down
51 changes: 49 additions & 2 deletions zio-json/native/src/main/scala/zio/json/internal/SafeNumbers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package zio.json.internal

import java.util.UUID

/**
* Total, fast, number parsing.
*
Expand Down Expand Up @@ -75,13 +77,19 @@ object SafeNumbers {
def toString(x: Double): String = {
val out = new FastStringWrite(24)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: Float): String = {
val out = new FastStringWrite(16)
write(x, out)
out.toString
out.buffer.toString
}

def toString(x: UUID): String = {
val out = writes.get
write(x, out)
out.buffer.toString
}

// Based on the amazing work of Raffaello Giulietti
Expand Down Expand Up @@ -294,6 +302,26 @@ object SafeNumbers {
}
}

def write(x: UUID, out: Write): Unit = {
val ds = lowerCaseHexDigits
val msb = x.getMostSignificantBits
val lsb = x.getLeastSignificantBits
val msb1 = (msb >> 32).toInt
out.write(ds(msb1 >>> 24), ds(msb1 >> 16 & 0xff), ds(msb1 >> 8 & 0xff), ds(msb1 & 0xff))
out.write('-')
val msb2 = msb.toInt
out.write(ds(msb2 >>> 24), ds(msb2 >> 16 & 0xff))
out.write('-')
out.write(ds(msb2 >> 8 & 0xff), ds(msb2 & 0xff))
out.write('-')
val lsb1 = (lsb >>> 32).toInt
out.write(ds(lsb1 >>> 24), ds(lsb1 >> 16 & 0xff))
out.write('-')
out.write(ds(lsb1 >> 8 & 0xff), ds(lsb1 & 0xff))
val lsb2 = lsb.toInt
out.write(ds(lsb2 >>> 24), ds(lsb2 >> 16 & 0xff), ds(lsb2 >> 8 & 0xff), ds(lsb2 & 0xff))
}

private[json] def writeNano(x: Int, out: Write): Unit = {
out.write('.')
var coeff = 100000000
Expand Down Expand Up @@ -482,6 +510,25 @@ object SafeNumbers {
576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L, 576460752303423478L
)

private final val lowerCaseHexDigits: Array[Short] = Array(
12336, 12592, 12848, 13104, 13360, 13616, 13872, 14128, 14384, 14640, 24880, 25136, 25392, 25648, 25904, 26160,
12337, 12593, 12849, 13105, 13361, 13617, 13873, 14129, 14385, 14641, 24881, 25137, 25393, 25649, 25905, 26161,
12338, 12594, 12850, 13106, 13362, 13618, 13874, 14130, 14386, 14642, 24882, 25138, 25394, 25650, 25906, 26162,
12339, 12595, 12851, 13107, 13363, 13619, 13875, 14131, 14387, 14643, 24883, 25139, 25395, 25651, 25907, 26163,
12340, 12596, 12852, 13108, 13364, 13620, 13876, 14132, 14388, 14644, 24884, 25140, 25396, 25652, 25908, 26164,
12341, 12597, 12853, 13109, 13365, 13621, 13877, 14133, 14389, 14645, 24885, 25141, 25397, 25653, 25909, 26165,
12342, 12598, 12854, 13110, 13366, 13622, 13878, 14134, 14390, 14646, 24886, 25142, 25398, 25654, 25910, 26166,
12343, 12599, 12855, 13111, 13367, 13623, 13879, 14135, 14391, 14647, 24887, 25143, 25399, 25655, 25911, 26167,
12344, 12600, 12856, 13112, 13368, 13624, 13880, 14136, 14392, 14648, 24888, 25144, 25400, 25656, 25912, 26168,
12345, 12601, 12857, 13113, 13369, 13625, 13881, 14137, 14393, 14649, 24889, 25145, 25401, 25657, 25913, 26169,
12385, 12641, 12897, 13153, 13409, 13665, 13921, 14177, 14433, 14689, 24929, 25185, 25441, 25697, 25953, 26209,
12386, 12642, 12898, 13154, 13410, 13666, 13922, 14178, 14434, 14690, 24930, 25186, 25442, 25698, 25954, 26210,
12387, 12643, 12899, 13155, 13411, 13667, 13923, 14179, 14435, 14691, 24931, 25187, 25443, 25699, 25955, 26211,
12388, 12644, 12900, 13156, 13412, 13668, 13924, 14180, 14436, 14692, 24932, 25188, 25444, 25700, 25956, 26212,
12389, 12645, 12901, 13157, 13413, 13669, 13925, 14181, 14437, 14693, 24933, 25189, 25445, 25701, 25957, 26213,
12390, 12646, 12902, 13158, 13414, 13670, 13926, 14182, 14438, 14694, 24934, 25190, 25446, 25702, 25958, 26214
)

private[this] val gs: Array[Long] = Array(
5696189077778435540L, 6557778377634271669L, 9113902524445496865L, 1269073367360058862L, 7291122019556397492L,
1015258693888047090L, 5832897615645117993L, 6346230177223303157L, 4666318092516094394L, 8766332956520552849L,
Expand Down
15 changes: 12 additions & 3 deletions zio-json/shared/src/main/scala/zio/json/JsonEncoder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -723,12 +723,12 @@ private[json] trait EncoderLowPriority3 extends EncoderLowPriority4 {
implicit val zoneId: JsonEncoder[ZoneId] = new JsonEncoder[ZoneId] {
def unsafeEncode(a: ZoneId, indent: Option[Int], out: Write): Unit = {
out.write('"')
serializers.write(a, out)
out.write(a.getId)
out.write('"')
}

override final def toJsonAST(a: ZoneId): Either[String, Json] =
new Right(new Json.Str(serializers.toString(a)))
new Right(new Json.Str(a.getId))
}

implicit val zoneOffset: JsonEncoder[ZoneOffset] = new JsonEncoder[ZoneOffset] {
Expand All @@ -742,7 +742,16 @@ private[json] trait EncoderLowPriority3 extends EncoderLowPriority4 {
new Right(new Json.Str(serializers.toString(a)))
}

implicit val uuid: JsonEncoder[UUID] = stringify(_.toString)
implicit val uuid: JsonEncoder[UUID] = new JsonEncoder[UUID] {
def unsafeEncode(a: UUID, indent: Option[Int], out: Write): Unit = {
out.write('"')
SafeNumbers.write(a, out)
out.write('"')
}

override final def toJsonAST(a: UUID): Either[String, Json] =
new Right(new Json.Str(SafeNumbers.toString(a)))
}

implicit val currency: JsonEncoder[java.util.Currency] = stringify(_.toString)
}
Expand Down

0 comments on commit addd7ff

Please sign in to comment.