#lang scheme/base
(require (prefix-in z: (planet zitterbewegung/uuid-v4:1:0/uuid-v4))
"base.ss"
scheme/list
scheme/string
)
(define (bytes->integer bytes)
(define (helper rest num)
(if (null? rest) num
(helper (cdr rest) (+ (* num 255) (car rest)))))
(helper (bytes->list bytes) 0))
(define (bytes->hex bytes)
(define (helper rest acc)
(if (null? rest) (list->string (map hex-byte->char (reverse acc)))
(helper (cdr rest)
(let-values (((quotient remainder)
(quotient/remainder (car rest) 16)))
(list* remainder quotient acc)))))
(helper (bytes->list bytes) '()))
(define-struct *uuid (bytes)
#:property prop:custom-write
(lambda (u out write?)
(display (format "#<uuid:~a>" (uuid->string u)) out))
#:property prop:equal+hash
(list (lambda (u1 u2 sub?)
(bytes=? (*uuid-bytes u1) (*uuid-bytes u2)))
(lambda (u recur)
(bytes->integer (*uuid-bytes u)))
(lambda (u recur)
(bytes->integer (*uuid-bytes u)))))
(define (uuid-time-low u)
(integer-bytes->integer (subbytes (*uuid-bytes u) 0 4) #f #t))
(define (uuid-time-mid u)
(integer-bytes->integer (subbytes (*uuid-bytes u) 4 6) #f #t))
(define (uuid-time-high u)
(integer-bytes->integer (subbytes (*uuid-bytes u) 6 8) #f #t))
(define (uuid-clock-high u)
(integer-bytes->integer (bytes-append (list->bytes (list 0))
(subbytes (*uuid-bytes u) 8 9)) #f #t))
(define (uuid-clock-low u)
(integer-bytes->integer (bytes-append (list->bytes (list 0))
(subbytes (*uuid-bytes u) 9 10)) #f #t))
(define (uuid-node u)
(integer-bytes->integer (bytes-append (list->bytes (list 0 0))
(subbytes (*uuid-bytes u) 10 16)) #f #t))
(define (uuid->string u (dash? #t))
(define (sub start end)
(subbytes (*uuid-bytes u) start end))
(if (not dash?)
(bytes->hex (*uuid-bytes u))
(string-join (map (lambda (b)
(bytes->hex b))
(list (sub 0 4) (sub 4 6) (sub 6 8) (sub 8 10) (sub 10 16)))
"-")))
(define (uuid-string? u)
(and (string? u)
(regexp-match #px"^(?i:([0-9a-f]{,8})-?([0-9a-f]{,4})-?([0-9a-f]{,4})-?([0-9a-f]{,4})-?([0-9a-f]{,12}))$" u)))
(define (uuid-symbol? u)
(and (symbol? u)
(uuid-string? (symbol->string u))))
(define (uuid-bytes? u)
(and (bytes? u)
(= (bytes-length u) 16)))
(define (uuid? u)
(or (*uuid? u)
(uuid-bytes? u)
(uuid-string? u)))
(define (make-uuid (u (symbol->string (z:make-uuid))))
(cond ((*uuid? u)
(make-*uuid (*uuid-bytes u)))
((uuid-bytes? u)
(make-*uuid u))
(else
(uuid-string->uuid u))))
(define (hex-byte->char h)
(case h
((0) #\0)
((1) #\1)
((2) #\2)
((3) #\3)
((4) #\4)
((5) #\5)
((6) #\6)
((7) #\7)
((8) #\8)
((9) #\9)
((10) #\a)
((11) #\b)
((12) #\c)
((13) #\d)
((14) #\e)
((15) #\f)
(else (error 'hex-byte->char "Not an hex byte: ~a" h))))
(define (hex-char->integer c)
(case c
((#\1) 1)
((#\2) 2)
((#\3) 3)
((#\4) 4)
((#\5) 5)
((#\6) 6)
((#\7) 7)
((#\8) 8)
((#\9) 9)
((#\0) 0)
((#\a #\A) 10)
((#\b #\B) 11)
((#\c #\C) 12)
((#\d #\D) 13)
((#\e #\E) 14)
((#\f #\F) 15)
(else (error 'hex-char->integer "char ~a out of range" c))))
(define (hex-char? c)
(member c '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\a #\A #\b #\B #\c #\C #\d #\D #\e #\E #\f #\F)))
(define (hex-chars->byte chars)
(define (helper rest num)
(if (null? rest)
num
(helper (cdr rest) (+ (* 16 num) (hex-char->integer (car rest))))))
(helper chars 0))
(define (hex-string->bytes h)
(define (helper rest acc)
(cond ((null? rest) (reverse acc))
((null? (cdr rest)) (error 'hex-string->bytes "Uneven # of hexdecimal strings: ~a" h))
(else
(helper (cddr rest)
(cons (hex-chars->byte (list (car rest) (cadr rest)))
acc)))))
(helper (string->list h) '()))
(define (uuid-string->uuid uuid)
(make-*uuid (list->bytes (flatten (map hex-string->bytes (cdr (uuid-string? uuid)))))))
(provide/contract
(make-uuid (->* ()
(uuid?)
*uuid?))
(uuid->string (->* (*uuid?)
(boolean?)
string?))
(rename *uuid-bytes uuid->bytes (-> *uuid? bytes?))
(uuid-string? (-> any/c any))
(uuid-bytes? (-> any/c any))
(uuid-time-low (-> *uuid? number?))
(uuid-time-mid (-> *uuid? number?))
(uuid-time-high (-> *uuid? number?))
(uuid-clock-low (-> *uuid? number?))
(uuid-clock-high (-> *uuid? number?))
(uuid-node (-> *uuid? number?))
(uuid? (-> any/c any))
(bytes->hex (-> bytes? string?))
(bytes->integer (-> bytes? number?))
)