]> Repositorios git - scryer-prolog.git/commitdiff
UUIDv4 generation
authorAdrián Arroyo Calle <[email protected]>
Thu, 18 Feb 2021 21:16:24 +0000 (22:16 +0100)
committerAdrián Arroyo Calle <[email protected]>
Thu, 18 Feb 2021 21:16:24 +0000 (22:16 +0100)
README.md
src/lib/uuid.pl [new file with mode: 0644]

index 985ae4a0b394b4f7c8179439874e291fbbdbedcc..2c86d62061426401413167e6ab805c92c48285ac 100644 (file)
--- a/README.md
+++ b/README.md
@@ -519,6 +519,7 @@ The modules that ship with Scryer&nbsp;Prolog are also called
   public key signatures and signature verification with&nbsp;Ed25519,
   ECDH key&nbsp;exchange over Curve25519 (X25519), authenticated symmetric
   encryption with ChaCha20-Poly1305, and reasoning about elliptic curves.
+* [`uuid`](src/lib/uuid.pl) UUIDv4 generation and hex representation
 
 To use predicates provided by the `lists` library, write:
 
diff --git a/src/lib/uuid.pl b/src/lib/uuid.pl
new file mode 100644 (file)
index 0000000..affa3b6
--- /dev/null
@@ -0,0 +1,107 @@
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+   Written in February 2021 by Adrián Arroyo ([email protected])
+   Part of Scryer-Prolog
+   This library provides reasoning about UUID (only version 4 right now).
+   There are three predicates:
+    * uuidv4/1, to generate a new UUIDv4
+    * uuidv4_string/1, to generate a new UUIDv4 in string hex representation 
+    * uuid_string/2, to converte between UUID list of bytes and UUID hex representation
+   
+   Examples:
+   ?- uuidv4(X).
+      X = [42,147,248,242,117,196,79,2,129,159|...].
+   ?- uuidv4_string(X).
+      X = "428499fc-76e3-4240- ...".
+   ?- uuidv4(X), uuid_string(X, S).
+      X = [173,12,244,152,139,118,64,139,137,4|...], S = "ad0cf498-8b76-408b- ...".
+   ?- uuid_string(X, "61ae692e-eaf6-4199-8dd3-9f01db70a20b").
+      X = [97,174,105,46,234,246,65,153,141,211|...].
+
+   I place this code in the public domain. Use it in any way you want.
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+:- module(uuid, [
+    uuidv4/1,
+    uuidv4_string/1,
+    uuid_string/2
+]).
+
+:- use_module(library(crypto)).
+:- use_module(library(dcgs)).
+:- use_module(library(lists)).
+
+/* 
+An UUID is made of 16 bytes, composed of 5 sections:
+time_low - 4
+time_mid - 2
+time_hi_and_version - 2
+clock_seq_hi_and_res_clock_seq_low - 2
+node - 6
+UUID v4 can be generated from a set of 16 random bytes: https://www.rfc-archive.org/getrfc.php?rfc=4122#gsc.tab=0 (section 4.4)
+*/
+uuidv4(Uuid) :-
+    crypto_n_random_bytes(16, Bytes),
+    Bytes = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
+    byte_bits(B9, BitsClockSeqHi0),
+    BitsClockSeqHi0 = [_X7, _X6, X5, X4, X3, X2, X1, X0],
+    NewBitsClockSeqHi0 = [1, 0, X5, X4, X3, X2, X1, X0],
+    byte_bits(NewClockSeqHi0, NewBitsClockSeqHi0),
+    byte_bits(B7, BitsTimeHi),
+    BitsTimeHi = [_Y7, _Y6, _Y5, _Y4, Y3, Y2, Y1, Y0],
+    NewBitsTimeHi = [0, 1, 0, 0, Y3, Y2, Y1, Y0],
+    byte_bits(NewTimeHi, NewBitsTimeHi),
+    Uuid = [B1, B2, B3, B4, B5, B6, NewTimeHi, B8, NewClockSeqHi0, B10, B11, B12, B13, B14, B15, B16].
+
+uuidv4_string(String) :- uuidv4(Uuid), uuid_string(Uuid, String).
+
+uuid_string(Uuid, String) :-
+    Uuid = [B1, B2, B3, B4, B5, B6, B7, B8, B9, B10, B11, B12, B13, B14, B15, B16],
+    phrase(uuid_([S1, S2, S3, S4, S5]), String),
+    hex_bytes(S1, [B1, B2, B3, B4]),
+    hex_bytes(S2, [B5, B6]),
+    hex_bytes(S3, [B7, B8]),
+    hex_bytes(S4, [B9, B10]),
+    hex_bytes(S5, [B11, B12, B13, B14, B15, B16]).
+
+uuid_([S1, S2, S3, S4, S5]) -->
+    {
+        length(S1, 8),
+        length(S2, 4),
+        length(S3, 4),
+        length(S4, 4),
+        length(S5, 12)
+    },
+    S1,
+    "-",
+    S2,
+    "-",
+    S3,
+    "-",
+    S4,
+    "-",
+    S5.
+
+byte_bits(Byte, Bits) :-
+    \+ var(Byte),
+    byte_bits_(Byte, Bits),
+    length(Bits, 8),!.
+
+byte_bits(Byte, Bits) :-
+    \+ var(Bits),
+    length(Bits, 8),
+    byte_bits__(Byte, Bits),!.
+
+byte_bits_(0, [0]).
+byte_bits_(1, [1]).
+byte_bits_(Byte, Bits) :-
+    R is Byte // 2,
+    M is Byte mod 2,
+    byte_bits_(R, Bits0),
+    append(Bits0, [M], Bits).
+
+byte_bits__(0, []).
+byte_bits__(Byte, Bits) :-
+    length(Bits, N),
+    Bits = [Bit|Bits0],
+    byte_bits__(Byte0, Bits0),
+    Byte is Byte0 + Bit*(2 ^ (N-1)).