diff --git a/pkgs/anoia/init.fnl b/pkgs/anoia/init.fnl index 06695ba7..1b629857 100644 --- a/pkgs/anoia/init.fnl +++ b/pkgs/anoia/init.fnl @@ -13,4 +13,47 @@ (fn system [s] (assert (os.execute s))) -{ : merge : split : file-exists? : system } +(fn hash [str] + (accumulate [h 5381 + c (str:gmatch ".")] + (+ (* h 33) (string.byte c)))) + + +(local + base64-indices + (doto [ + "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" + "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z" "a" "b" "c" "d" "e" "f" + "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" + "w" "x" "y" "z" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "-" "_" + ] + (tset 0 "A"))) + +;; local function base64(s) +;; local byte, rep = string.byte, string.rep +;; local pad = 2 - ((#s-1) % 3) +;; s = (s..rep('\0', pad)):gsub("...", function(cs) +;; local a, b, c = byte(cs, 1, 3) +;; return bs[a>>2] .. bs[(a&3)<<4|b>>4] .. bs[(b&15)<<2|c>>6] .. bs[c&63] +;; end) +;; return s:sub(1, #s-pad) .. rep('=', pad) +;; end + +(fn base64url [s] + "URL-safe Base64-encoded form of s (no trailing padding)" + (let [pad (- 2 (% (- (# s) 1) 3)) + bs base64-indices + blank (string.rep "\0" pad) + s (-> (.. s blank) + (: :gsub + "..." + (fn [cs] + (let [(a b c) (string.byte cs 1 3)] + (.. (. bs (rshift a 2)) + (. bs (bor (lshift (band a 3) 4) (rshift b 4))) + (. bs (bor (lshift (band b 15) 2) (rshift c 6))) + (. bs (band c 63)))))))] + (s:sub 1 (- (# s) pad)))) + + +{ : merge : split : file-exists? : system : hash : base64url } diff --git a/pkgs/anoia/test.fnl b/pkgs/anoia/test.fnl new file mode 100644 index 00000000..b940758b --- /dev/null +++ b/pkgs/anoia/test.fnl @@ -0,0 +1,9 @@ +(local { : hash : base64url } (require :anoia)) + +(assert (= (hash "") 5381)) + +;; these examples from https://theartincode.stanis.me/008-djb2/ +(assert (= (hash "Hello") 210676686969)) +(assert (= (hash "Hello!") 6952330670010)) + +(assert (= (base64url "hello world") "aGVsbG8gd29ybGQ"))