commit ea2e9f1b20e531d79e79218c5f0561737c5a5f3b
parent 4b4db93c39dd5376aa662931864c2d8018ae728e
Author: Drew DeVault <sir@cmpwn.com>
Date: Fri, 26 Mar 2021 09:29:37 -0400
uuid: new module
Diffstat:
3 files changed, 127 insertions(+), 1 deletion(-)
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -539,6 +539,13 @@ unix_passwd() {
gen_ssa unix::passwd bufio io os strconv strings
}
+uuid() {
+ printf '# uuid\n'
+ gen_srcs uuid \
+ uuid.ha
+ gen_ssa uuid crypto::random strio fmt endian io bytes
+}
+
printf '# This file is generated by the gen-stdlib script, do not edit it by hand\n\n'
modules="ascii
@@ -579,7 +586,8 @@ temp
time
types
unicode
-unix_passwd"
+unix_passwd
+uuid"
stdlib() {
rt
for module in $modules; do
diff --git a/stdlib.mk b/stdlib.mk
@@ -185,6 +185,9 @@ hare_stdlib_deps+=$(stdlib_unicode)
stdlib_unix_passwd=$(HARECACHE)/unix/passwd/unix_passwd.o
hare_stdlib_deps+=$(stdlib_unix_passwd)
+stdlib_uuid=$(HARECACHE)/uuid/uuid.o
+hare_stdlib_deps+=$(stdlib_uuid)
+
# ascii
stdlib_ascii_srcs= \
$(STDLIB)/ascii/ctype.ha \
@@ -660,6 +663,16 @@ $(HARECACHE)/unix/passwd/unix_passwd.ssa: $(stdlib_unix_passwd_srcs) $(stdlib_rt
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nunix::passwd \
-t$(HARECACHE)/unix/passwd/unix_passwd.td $(stdlib_unix_passwd_srcs)
+# uuid
+stdlib_uuid_srcs= \
+ $(STDLIB)/uuid/uuid.ha
+
+$(HARECACHE)/uuid/uuid.ssa: $(stdlib_uuid_srcs) $(stdlib_rt) $(stdlib_crypto_random) $(stdlib_strio) $(stdlib_fmt) $(stdlib_endian) $(stdlib_io) $(stdlib_bytes)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/uuid
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nuuid \
+ -t$(HARECACHE)/uuid/uuid.td $(stdlib_uuid_srcs)
+
# rt
testlib_rt_srcs= \
$(STDLIB)/rt/$(PLATFORM)/env.ha \
@@ -847,6 +860,9 @@ hare_testlib_deps+=$(testlib_unicode)
testlib_unix_passwd=$(TESTCACHE)/unix/passwd/unix_passwd.o
hare_testlib_deps+=$(testlib_unix_passwd)
+testlib_uuid=$(TESTCACHE)/uuid/uuid.o
+hare_testlib_deps+=$(testlib_uuid)
+
# ascii
testlib_ascii_srcs= \
$(STDLIB)/ascii/ctype.ha \
@@ -1332,3 +1348,13 @@ $(TESTCACHE)/unix/passwd/unix_passwd.ssa: $(testlib_unix_passwd_srcs) $(testlib_
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nunix::passwd \
-t$(TESTCACHE)/unix/passwd/unix_passwd.td $(testlib_unix_passwd_srcs)
+# uuid
+testlib_uuid_srcs= \
+ $(STDLIB)/uuid/uuid.ha
+
+$(TESTCACHE)/uuid/uuid.ssa: $(testlib_uuid_srcs) $(testlib_rt) $(testlib_crypto_random) $(testlib_strio) $(testlib_fmt) $(testlib_endian) $(testlib_io) $(testlib_bytes)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/uuid
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nuuid \
+ -t$(TESTCACHE)/uuid/uuid.td $(testlib_uuid_srcs)
+
diff --git a/uuid/uuid.ha b/uuid/uuid.ha
@@ -0,0 +1,92 @@
+// The uuid module implements RFC 4122-compatible Univerally Unique IDentifiers.
+//
+// This module only generates UUID4, aka "truly random" UUIDs, due to privacy
+// concerns.
+use bytes;
+use crypto::random;
+use endian;
+use fmt;
+use io;
+use strio;
+// TODO: decode, decodestr
+
+// A UUID.
+export type uuid = [16]u8;
+
+export def UUID_LEN: size = 16;
+export def UUID_STRLEN: size = 36;
+export def UUID_URILEN: size = 45;
+
+// The "nil" UUID, with all bits set to zero.
+export const nil: uuid = [0...];
+
+// Octet offsets of various fields as defined by the RFC.
+def TIME_LOW: size = 0;
+def TIME_MID: size = 4;
+def TIME_HI_AND_VERSION: size = 6;
+def CLOCK_SEQ_HI_AND_RESERVED: size = 8;
+def CLOCK_SEQ_LOW: size = 9;
+def NODE: size = 10;
+
+// Generates a new version 4 UUID.
+export fn generate() uuid = {
+ let id: [16]u8 = [0...];
+ random::buffer(id[..]);
+ let buf = id[CLOCK_SEQ_HI_AND_RESERVED..CLOCK_SEQ_HI_AND_RESERVED + 2];
+ let clock = (endian::begetu16(buf) & 0x3FFF) | 0x8000;
+ endian::beputu16(buf, clock);
+ let buf = id[TIME_HI_AND_VERSION..TIME_HI_AND_VERSION + 2];
+ let version = (endian::begetu16(buf) & 0x0FFF) | 0x4000;
+ endian::beputu16(buf, version);
+ return id;
+};
+
+// Returns true if two UUIDs are equal.
+export fn compare(a: uuid, b: uuid) bool = bytes::equal(a, b);
+
+// Encodes a UUID as a string and writes it to a stream.
+export fn encode(out: *io::stream, in: uuid) (size | io::error) = {
+ let z = 0z;
+ for (let i = TIME_LOW; i < TIME_LOW + 4; i += 1) {
+ z += fmt::fprintf(out, "{:02x}", in[i])?;
+ };
+ z += fmt::fprintf(out, "-")?;
+ for (let i = TIME_MID; i < TIME_MID + 2; i += 1) {
+ z += fmt::fprintf(out, "{:02x}", in[i])?;
+ };
+ z += fmt::fprintf(out, "-")?;
+ for (let i = TIME_HI_AND_VERSION; i < TIME_HI_AND_VERSION + 2; i += 1) {
+ z += fmt::fprintf(out, "{:02x}", in[i])?;
+ };
+ z += fmt::fprintf(out, "-{:02x}{:02x}-",
+ in[CLOCK_SEQ_HI_AND_RESERVED], in[CLOCK_SEQ_LOW])?;
+ for (let i = NODE; i < NODE + 6; i += 1) {
+ z += fmt::fprintf(out, "{:02x}", in[i])?;
+ };
+ return z;
+};
+
+// Encodes a UUID as a URI and writes it to a stream.
+export fn uri(out: *io::stream, in: uuid) (size | io::error) = {
+ return fmt::fprintf(out, "urn:uuid:")? + encode(out, in)?;
+};
+
+// Encodes a UUID as a string. The return value is statically allocated, the
+// caller must use [strings::dup] to extend its lifetime.
+export fn encodestr(in: uuid) str = {
+ static let buf: [UUID_STRLEN]u8 = [0...];
+ let sink = strio::fixed(buf);
+ defer io::close(sink);
+ encode(sink, in) as size;
+ return strio::string(sink);
+};
+
+// Encodes a UUID as a string. The return value is statically allocated, the
+// caller must use [strings::dup] to extend its lifetime.
+export fn encodeuri(in: uuid) str = {
+ static let buf: [UUID_URILEN]u8 = [0...];
+ let sink = strio::fixed(buf);
+ defer io::close(sink);
+ uri(sink, in) as size;
+ return strio::string(sink);
+};