Skip to content

Commit

Permalink
Tweak Uuidm.v4_ns (#14).
Browse files Browse the repository at this point in the history
  • Loading branch information
dbuenzli committed Sep 25, 2024
1 parent c35ea5b commit 6880e40
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 42 deletions.
5 changes: 3 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
34 changes: 17 additions & 17 deletions src/uuidm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

(* Bits *)

type bits48 = int64
type bits62 = int64
type bits4 = int
type bits12 = int

Expand Down Expand Up @@ -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

Expand Down
42 changes: 20 additions & 22 deletions src/uuidm.mli
Original file line number Diff line number Diff line change
Expand Up @@ -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. *)
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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} *)

Expand Down
2 changes: 1 addition & 1 deletion test/test_uuidm.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6880e40

Please sign in to comment.