From 6880e404d2810046ede54bb5e19c53bb623af17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20B=C3=BCnzli?= Date: Wed, 25 Sep 2024 17:14:02 +0200 Subject: [PATCH] Tweak Uuidm.v4_ns (#14). --- CHANGES.md | 5 +++-- src/uuidm.ml | 34 +++++++++++++++++----------------- src/uuidm.mli | 42 ++++++++++++++++++++---------------------- test/test_uuidm.ml | 2 +- 4 files changed, 41 insertions(+), 42 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 47e4815..111f6a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,7 @@ -- Add `Uuidm.v7`, create time and random based V7 UUIDs using client provided - random bytes and time. Thanks to Robin Newton for the patch (#14). +- Add `Uuidm.{v7,v7_ns}` to create time and random based V7 UUIDs. + Thanks to Robin Newton for the patch (#14) and Christian Linding + and Pau Ruiz Safont for the help. - Add `Uuidm.v8` to create V8 custom UUIDs. - Add `Uuidm.max` the RFC 9569 Max UUID. - Add `Uuidm.{variant,version,time_ms}` UUID property accessors. diff --git a/src/uuidm.ml b/src/uuidm.ml index bc8f5a4..d8d0183 100644 --- a/src/uuidm.ml +++ b/src/uuidm.ml @@ -5,7 +5,7 @@ (* Bits *) -type bits48 = int64 +type bits62 = int64 type bits4 = int type bits12 = int @@ -129,23 +129,23 @@ let v7 ~t_ms ~rand_a ~rand_b = Bytes.set_int64_be u 8 rand_b; make u ~version:7 -let v7_ns = - let open Int64 in +let v7_ns ~t_ns ~rand_b = let ns_in_ms = 1_000_000L in - let sub_ms_frac_multiplier = unsigned_div minus_one ns_in_ms in - fun ~t_ns:ts ~rand_b:b -> - let u = Bytes.create 16 in - Bytes.blit b 0 u 8 8; - (* RFC9562 requires we use 48 bits for a timestamp in milliseconds, and - allows for 12 bits to store a sub-millisecond fraction. We get the - latter by multiplying to put the fraction in a 64-bit range, then - shifting into 12 bits. *) - let ms = unsigned_div ts ns_in_ms in - let ns = unsigned_rem ts ns_in_ms in - let sub_ms_frac = shift_right_logical (mul ns sub_ms_frac_multiplier) 52 in - Bytes.set_int64_be u 0 (shift_left ms 16); - Bytes.set_int16_be u 6 (to_int sub_ms_frac); - make u ~version:7 + let sub_ms_frac_multiplier = Int64.unsigned_div Int64.minus_one ns_in_ms in + let u = Bytes.create 16 in + (* RFC 9562 requires we use 48 bits for a timestamp in milliseconds, and + allows for 12 bits to store a sub-millisecond fraction. We get the + latter by multiplying to put the fraction in a 64-bit range, then + shifting into 12 bits. *) + let ms = Int64.unsigned_div t_ns ns_in_ms in + let ns = Int64.unsigned_rem t_ns ns_in_ms in + let sub_ms_frac = + Int64.shift_right_logical (Int64.mul ns sub_ms_frac_multiplier) 52 + in + Bytes.set_int64_be u 0 (Int64.shift_left ms 16); + Bytes.set_int16_be u 6 (Int64.to_int sub_ms_frac); + Bytes.set_int64_be u 8 rand_b; + make u ~version:7 let v8 s = make (Bytes.of_string s) ~version:8 diff --git a/src/uuidm.mli b/src/uuidm.mli index e39168e..5b0e12f 100644 --- a/src/uuidm.mli +++ b/src/uuidm.mli @@ -14,10 +14,6 @@ (** {1:bits Bits} *) -type bits48 = int64 -(** The type for 48 bits stored in the 48 lower bits of an [int64] value. - The higher bits are either set to zero or ignored on use. *) - type bits4 = int (** The type for 4 bits stored in the 4 lower bits of an [int] value. The higher bits are either set to zero or ignored on use. *) @@ -26,6 +22,10 @@ type bits12 = int (** The type for 12 bits stored in the 12 lower bits of an [int] value. The higher bits are either set to zero or ignored on use. *) +type bits62 = int64 +(** The type for 62 bits stored in the 62 lower bits of an [int64] value. + The higher bits are either set to zero or ignored on use. *) + (** {1:uuids UUIDs} *) type t @@ -49,26 +49,24 @@ val v5 : t -> string -> t (name based with SHA-1 hashing) named by [n] and namespaced by [ns]. *) -val v7 : t_ms:bits48 -> rand_a:bits12 -> rand_b:int64 -> t +val v7 : t_ms:int64 -> rand_a:bits12 -> rand_b:bits62 -> t (** [v7 t_ms ~rand_a ~rand_b] is a {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}V7 UUID} - (time and random based) using the millisecond POSIX timestamp [t_ms] and - random bits [rand_a] and [rand_b]. + (time and random based) using the 64-bit millisecond POSIX timestamp + [t_ms] and random bits [rand_a] and [rand_b]. - {b Warning.} The timestamp and the randomness is seen literally + {b Warning.} The timestamp and the randomness are seen literally in the result. *) -val v7_ns : t_ns:bits48 -> rand_b:bytes -> t -(** [v7_ns ts b] is a - {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}V7 UUID} - (time and random based) that uses the - 62 lower bits of [b] for randomness and takes [ts] to be the - {e unsigned} number of nanoseconds since midnight 1 Jan 1970 UTC, leap - seconds excluded. The timestamp will be represented in the UUID - - with a resolution of about 244 nanoseconds - such that the - ordering of UUIDs will match the ordering of timestamps. - - {b Warning.} The timestamp and the randomness is seen literally in +val v7_ns : t_ns:int64 -> rand_b:bits62 -> t +(** [v7_ns ~ts_ns ~rand_b] is a + {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7}V7 + UUID} (time and random based) using the {e unsigned} 64-bit + nanosecond POSIX timestamp [t_ns] and random bits [rand_b]. The + [rand_a] field is used with the timestamp's submilisecond (about + 244 nanoseconds resolution). + + {b Warning.} The timestamp and the randomness are seen literally in the result. *) val v8 : string -> t @@ -131,11 +129,11 @@ val version : t -> bits4 {{:https://www.rfc-editor.org/rfc/rfc9562#name-version-field}version field} of [u]. *) -val time_ms : t -> bits48 option +val time_ms : t -> int64 option (** [time_ms u] is the {{:https://www.rfc-editor.org/rfc/rfc9562#name-uuid-version-7} - [unit_ts_ms]} POSIX timestamp of [u]. This is [None] if [u] - is not a V7 UUID. *) + [unit_ts_ms]} millisecond POSIX timestamp of [u] as a 64-bit + integer. This is [None] if [u] is not a V7 UUID. *) (** {1:preds Predicates and comparisons} *) diff --git a/test/test_uuidm.ml b/test/test_uuidm.ml index 2a8c7d3..89f9c92 100644 --- a/test/test_uuidm.ml +++ b/test/test_uuidm.ml @@ -46,7 +46,7 @@ let test_constructors () = "2ed6657d-e927-568b-95e1-2665a8aea6a2"; test_uuid ~__POS__ 7 ~time_ms:0x1020_3040_5060L (Uuidm.v7_ns ~t_ns:Int64.(add (mul 1_000_000L 0x1020_3040_5060L) 213135L) - ~rand_b:(Bytes.of_string "\x12\x34\x56\x78\x9a\xbc\xde\xf0")) + ~rand_b:0x123456789abcdef0L) "10203040-5060-7369-9234-56789abcdef0"; test_uuid ~__POS__ 7 ~time_ms:0x017F22E279B0L (Uuidm.v7