2025-09-13 15:56:19 +02:00
|
|
|
(* Crockford's variant of Base32
|
|
|
|
|
http://www.crockford.com/wrmg/base32.html
|
|
|
|
|
except that:
|
|
|
|
|
- 'U' is not excluded but also decodes to 'V'
|
|
|
|
|
- '-' is not allowed
|
|
|
|
|
- checksum is not allowed *)
|
|
|
|
|
|
2026-02-06 21:39:53 +01:00
|
|
|
(* 'I' 'L' 'O' 'U' excluded
|
|
|
|
|
no '=' padding in encoded string *)
|
2025-09-13 15:56:19 +02:00
|
|
|
type t = string
|
|
|
|
|
|
|
|
|
|
let alphabet = Base32.make_alphabet "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
2026-02-06 21:39:53 +01:00
|
|
|
|
|
|
|
|
let encode s =
|
|
|
|
|
let s = Base32.encode_string ~alphabet s in
|
|
|
|
|
(* remove '=' padding *)
|
|
|
|
|
match String.index_opt s '=' with
|
|
|
|
|
| None -> s
|
|
|
|
|
| Some i -> String.sub s 0 i
|
2025-09-13 15:56:19 +02:00
|
|
|
|
|
|
|
|
let decode s =
|
|
|
|
|
let s =
|
|
|
|
|
String.map
|
|
|
|
|
(fun c ->
|
|
|
|
|
match Char.uppercase_ascii c with
|
|
|
|
|
| 'O' -> '0'
|
|
|
|
|
| 'I' | 'L' -> '1'
|
|
|
|
|
| 'U' -> 'V'
|
|
|
|
|
| c -> c)
|
|
|
|
|
s
|
|
|
|
|
in
|
2026-02-10 16:47:08 +01:00
|
|
|
(* restore padding for base32 lib *)
|
|
|
|
|
let n = 8 - (String.length s mod 8) in
|
|
|
|
|
let pad = String.make n '=' in
|
|
|
|
|
let s = s ^ pad in
|
2025-09-13 15:56:19 +02:00
|
|
|
match Base32.decode ~alphabet ~off:0 ~len:(String.length s) s with
|
|
|
|
|
| Error (`Msg e) -> Error e
|
|
|
|
|
| Ok v -> Ok v
|