1:- module(uuid, [random_uuid/1, uuid_atom/2, uuid/1, uuid/2]). 2:- use_module(library(crypto), [crypto_n_random_bytes/2, hex_bytes/2]). 3:- use_module(library(list_util), [split/3]). 4
6bytes_integer(Bs, N) :-
7 foldl([B, N0, N1]>>(N1 is N0<<8 + B), Bs, 0, N).
8
9unsigned64_signed64(Un, Si) :-
10 integer(Un),
11 Un >= 0x8000_0000_0000_0000,
12 !,
13 Inv is 0xffff_ffff_ffff_ffff - Un,
14 Si is -Inv - 1.
15unsigned64_signed64(Un, Si) :-
16 integer(Si),
17 Si < 0,
18 !,
19 Inv is -Si - 1,
20 Un is 0xffff_ffff_ffff_ffff - Inv.
21unsigned64_signed64(Un, Un).
25random_uuid(uuid(Hi, Lo)) :-
26 crypto_n_random_bytes(8, HiBytes),
27 bytes_integer(HiBytes, Hi64),
28 29 Hi_ is Hi64 /\ \ (0b1111 << 12 ),
30 HiUn is Hi_ \/ (4 << 12),
31 unsigned64_signed64(HiUn, Hi),
32
33 crypto_n_random_bytes(8, LoBytes),
34 bytes_integer(LoBytes, Lo64),
35 36 Lo_ is Lo64 /\ \ (1 << (64-6)),
37 LoUn is Lo_ \/ (1 << (64-7)),
38 unsigned64_signed64(LoUn, Lo).
43uuid_atom(uuid(Hi_, Lo_), A) :-
44 integer(Hi_), integer(Lo_), !,
45 unsigned64_signed64(Hi, Hi_),
46 unsigned64_signed64(Lo, Lo_),
47 TimeLow is (Hi >> 32),
48 TimeMid is (Hi >> 16) /\ 0xffff,
49 TimeHi is Hi /\ 0xffff,
50 ClockSeq is (Lo >> 48) /\ 0xffff,
51 Node is Lo /\ 0xffff_ffff_ffff,
52 format(atom(TL), '~`0t~16r~8|', [TimeLow]),
53 format(atom(TM), '~`0t~16r~4|', [TimeMid]),
54 format(atom(TH), '~`0t~16r~4|', [TimeHi]),
55 format(atom(CS), '~`0t~16r~4|', [ClockSeq]),
56 format(atom(N), '~`0t~16r~12|', [Node]),
57 atomic_list_concat([TL, TM, TH, CS, N], '-', A).
58uuid_atom(uuid(Hi, Lo), A) :-
59 atom(A), !,
60 atom_chars(A, Chars),
61 split(Chars, '-', CharParts),
62 maplist(atom_chars, AtomParts, CharParts),
63 maplist(hex_bytes, AtomParts, Bytes),
64 maplist(bytes_integer, Bytes, Nums),
65 [TimeLow, TimeMid, TimeHi, ClockSeq, Node] = Nums,
66 Hi_ is TimeLow << 32 + TimeMid << 16 + TimeHi,
67 Lo_ is ClockSeq << 48 + Node,
68 unsigned64_signed64(Hi_, Hi),
69 unsigned64_signed64(Lo_, Lo).
74uuid(UUID) :-
75 random_uuid(U),
76 uuid_atom(U, UUID).
80uuid(_UUID, Options) :-
81 member(version(V), Options),
82 V \= 4, !,
83 throw(error(domain_error(4, V),
84 context(uuid/2, 'Only version 4 UUIDs supported'))).
85uuid(UUID, Options) :-
86 memberchk(format(integer), Options), !,
87 random_uuid(uuid(SHi, SLo)),
88 unsigned64_signed64(Hi, SHi),
89 unsigned64_signed64(Lo, SLo),
90 UUID is Hi << 64 + Lo.
91uuid(UUID, _Options) :-
92 uuid(UUID)