hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit ea2e9f1b20e531d79e79218c5f0561737c5a5f3b
parent 4b4db93c39dd5376aa662931864c2d8018ae728e
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 26 Mar 2021 09:29:37 -0400

uuid: new module

Diffstat:
Mscripts/gen-stdlib | 10+++++++++-
Mstdlib.mk | 26++++++++++++++++++++++++++
Auuid/uuid.ha | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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); +};