base32.scm 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012, 2015, 2017, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;;
  4. ;;; This file is part of GNU Guix.
  5. ;;;
  6. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  7. ;;; under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation; either version 3 of the License, or (at
  9. ;;; your option) any later version.
  10. ;;;
  11. ;;; GNU Guix is distributed in the hope that it will be useful, but
  12. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (guix base32)
  19. #:use-module (srfi srfi-1)
  20. #:use-module (srfi srfi-34)
  21. #:use-module (srfi srfi-35)
  22. #:use-module (srfi srfi-60)
  23. #:use-module (rnrs bytevectors)
  24. #:use-module (ice-9 vlist)
  25. #:export (bytevector-quintet-length
  26. bytevector->base32-string
  27. bytevector->nix-base32-string
  28. base32-string->bytevector
  29. nix-base32-string->bytevector
  30. %nix-base32-charset
  31. %rfc4648-base32-charset
  32. &invalid-base32-character
  33. invalid-base32-character?
  34. invalid-base32-character-value
  35. invalid-base32-character-string))
  36. ;;; Commentary:
  37. ;;;
  38. ;;; A generic, customizable to convert bytevectors to/from a base32
  39. ;;; representation.
  40. ;;;
  41. ;;; Code:
  42. (define-syntax bit-field
  43. (lambda (s)
  44. ;; This inline version of 'bit-field' assumes that START and END are
  45. ;; literals and pre-computes the mask. In an ideal world, using 'define'
  46. ;; or 'define-inlinable' would be enough, but as of 3.0.7, peval doesn't
  47. ;; expand calls to 'expt' (and 'bit-field' is a subr.)
  48. (syntax-case s ()
  49. ((_ n start end)
  50. (let* ((s (syntax->datum #'start))
  51. (e (syntax->datum #'end))
  52. (mask (- (expt 2 (- e s)) 1)))
  53. ;; The baseline compiler in Guile <= 3.0.7 miscompiles (ash x N) as
  54. ;; (ash x (- N)) when N is a literal: <https://bugs.gnu.org/50696>.
  55. ;; Here we take advantage of another bug in the baseline compiler,
  56. ;; fixed in Guile commit 330c6ea83f492672578b62d0683acbb532d1a5d9: we
  57. ;; introduce 'minus-start' such that it has a different source
  58. ;; location, which in turn means that the baseline compiler pattern
  59. ;; for (ash x N) doesn't match, thus avoiding the bug (!).
  60. (with-syntax ((minus-start (datum->syntax #'start (- s))))
  61. #`(logand (ash n minus-start) #,mask)))))))
  62. (define bytevector-quintet-ref
  63. (let* ((ref bytevector-u8-ref)
  64. (ref+ (lambda (bv offset)
  65. (let ((o (+ 1 offset)))
  66. (if (>= o (bytevector-length bv))
  67. 0
  68. (bytevector-u8-ref bv o)))))
  69. (ref0 (lambda (bv offset)
  70. (bit-field (ref bv offset) 3 8)))
  71. (ref1 (lambda (bv offset)
  72. (logior (ash (bit-field (ref bv offset) 0 3) 2)
  73. (bit-field (ref+ bv offset) 6 8))))
  74. (ref2 (lambda (bv offset)
  75. (bit-field (ref bv offset) 1 6)))
  76. (ref3 (lambda (bv offset)
  77. (logior (ash (bit-field (ref bv offset) 0 1) 4)
  78. (bit-field (ref+ bv offset) 4 8))))
  79. (ref4 (lambda (bv offset)
  80. (logior (ash (bit-field (ref bv offset) 0 4) 1)
  81. (bit-field (ref+ bv offset) 7 8))))
  82. (ref5 (lambda (bv offset)
  83. (bit-field (ref bv offset) 2 7)))
  84. (ref6 (lambda (bv offset)
  85. (logior (ash (bit-field (ref bv offset) 0 2) 3)
  86. (bit-field (ref+ bv offset) 5 8))))
  87. (ref7 (lambda (bv offset)
  88. (bit-field (ref bv offset) 0 5)))
  89. (refs (vector ref0 ref1 ref2 ref3 ref4 ref5 ref6 ref7)))
  90. (lambda (bv index)
  91. "Return the INDEXth quintet of BV."
  92. (let ((p (vector-ref refs (modulo index 8))))
  93. (p bv (quotient (* index 5) 8))))))
  94. (define bytevector-quintet-ref-right
  95. (let* ((ref bytevector-u8-ref)
  96. (ref+ (lambda (bv offset)
  97. (let ((o (+ 1 offset)))
  98. (if (>= o (bytevector-length bv))
  99. 0
  100. (bytevector-u8-ref bv o)))))
  101. (ref0 (lambda (bv offset)
  102. (bit-field (ref bv offset) 0 5)))
  103. (ref1 (lambda (bv offset)
  104. (logior (bit-field (ref bv offset) 5 8)
  105. (ash (bit-field (ref+ bv offset) 0 2) 3))))
  106. (ref2 (lambda (bv offset)
  107. (bit-field (ref bv offset) 2 7)))
  108. (ref3 (lambda (bv offset)
  109. (logior (bit-field (ref bv offset) 7 8)
  110. (ash (bit-field (ref+ bv offset) 0 4) 1))))
  111. (ref4 (lambda (bv offset)
  112. (logior (bit-field (ref bv offset) 4 8)
  113. (ash (bit-field (ref+ bv offset) 0 1) 4))))
  114. (ref5 (lambda (bv offset)
  115. (bit-field (ref bv offset) 1 6)))
  116. (ref6 (lambda (bv offset)
  117. (logior (bit-field (ref bv offset) 6 8)
  118. (ash (bit-field (ref+ bv offset) 0 3) 2))))
  119. (ref7 (lambda (bv offset)
  120. (bit-field (ref bv offset) 3 8)))
  121. (refs (vector ref0 ref1 ref2 ref3 ref4 ref5 ref6 ref7)))
  122. (lambda (bv index)
  123. "Return the INDEXth quintet of BV, assuming quintets start from the
  124. least-significant bits, contrary to what RFC 4648 describes."
  125. (let ((p (vector-ref refs (modulo index 8))))
  126. (p bv (quotient (* index 5) 8))))))
  127. (define (bytevector-quintet-length bv)
  128. "Return the number of quintets (including truncated ones) available in BV."
  129. (ceiling (/ (* (bytevector-length bv) 8) 5)))
  130. (define (bytevector-quintet-fold proc init bv)
  131. "Return the result of applying PROC to each quintet of BV and the result of
  132. the previous application or INIT."
  133. (define len
  134. (bytevector-quintet-length bv))
  135. (let loop ((i 0)
  136. (r init))
  137. (if (= i len)
  138. r
  139. (loop (1+ i) (proc (bytevector-quintet-ref bv i) r)))))
  140. (define (bytevector-quintet-fold-right proc init bv)
  141. "Return the result of applying PROC to each quintet of BV and the result of
  142. the previous application or INIT."
  143. (define len
  144. (bytevector-quintet-length bv))
  145. (let loop ((i len)
  146. (r init))
  147. (if (zero? i)
  148. r
  149. (let ((j (- i 1)))
  150. (loop j (proc (bytevector-quintet-ref-right bv j) r))))))
  151. (define (make-bytevector->base32-string quintet-fold base32-chars)
  152. (lambda (bv)
  153. "Return a base32 encoding of BV using BASE32-CHARS as the alphabet."
  154. (let ((chars (quintet-fold (lambda (q r)
  155. (cons (vector-ref base32-chars q)
  156. r))
  157. '()
  158. bv)))
  159. (list->string (reverse chars)))))
  160. (define %nix-base32-chars
  161. ;; See `libutil/hash.cc'.
  162. #(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
  163. #\a #\b #\c #\d #\f #\g #\h #\i #\j #\k #\l #\m #\n
  164. #\p #\q #\r #\s #\v #\w #\x #\y #\z))
  165. (define %nix-base32-charset
  166. (list->char-set (vector->list %nix-base32-chars)))
  167. (define %rfc4648-base32-chars
  168. #(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m
  169. #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z
  170. #\2 #\3 #\4 #\5 #\6 #\7))
  171. (define %rfc4648-base32-charset
  172. (list->char-set (vector->list %rfc4648-base32-chars)))
  173. (define bytevector->base32-string
  174. (make-bytevector->base32-string bytevector-quintet-fold
  175. %rfc4648-base32-chars))
  176. (define bytevector->nix-base32-string
  177. (make-bytevector->base32-string bytevector-quintet-fold-right
  178. %nix-base32-chars))
  179. (define bytevector-quintet-set!
  180. (let* ((setq! (lambda (bv offset start stop value)
  181. (let ((v (bytevector-u8-ref bv offset))
  182. (w (arithmetic-shift value start))
  183. (m (bitwise-xor (1- (expt 2 stop))
  184. (1- (expt 2 start)))))
  185. (bytevector-u8-set! bv offset
  186. (bitwise-merge m w v)))))
  187. (set0! (lambda (bv offset value)
  188. (setq! bv offset 3 8 value)))
  189. (set1! (lambda (bv offset value)
  190. (setq! bv offset 0 3 (bit-field value 2 5))
  191. (or (= (+ 1 offset) (bytevector-length bv))
  192. (setq! bv (+ 1 offset) 6 8 (bit-field value 0 2)))))
  193. (set2! (lambda (bv offset value)
  194. (setq! bv offset 1 6 value)))
  195. (set3! (lambda (bv offset value)
  196. (setq! bv offset 0 1 (bit-field value 4 5))
  197. (or (= (+ 1 offset) (bytevector-length bv))
  198. (setq! bv (+ 1 offset) 4 8 (bit-field value 0 4)))))
  199. (set4! (lambda (bv offset value)
  200. (setq! bv offset 0 4 (bit-field value 1 5))
  201. (or (= (+ 1 offset) (bytevector-length bv))
  202. (setq! bv (+ 1 offset) 7 8 (bit-field value 0 1)))))
  203. (set5! (lambda (bv offset value)
  204. (setq! bv offset 2 7 value)))
  205. (set6! (lambda (bv offset value)
  206. (setq! bv offset 0 2 (bit-field value 3 5))
  207. (or (= (+ 1 offset) (bytevector-length bv))
  208. (setq! bv (+ 1 offset) 5 8 (bit-field value 0 3)))))
  209. (set7! (lambda (bv offset value)
  210. (setq! bv offset 0 5 value)))
  211. (sets (vector set0! set1! set2! set3! set4! set5! set6! set7!)))
  212. (lambda (bv index value)
  213. "Set the INDEXth quintet of BV to VALUE."
  214. (let ((p (vector-ref sets (modulo index 8))))
  215. (p bv (quotient (* index 5) 8) (logand value #x1f))))))
  216. (define bytevector-quintet-set-right!
  217. (let* ((setq! (lambda (bv offset start stop value)
  218. (let ((v (bytevector-u8-ref bv offset))
  219. (w (arithmetic-shift value start))
  220. (m (bitwise-xor (1- (expt 2 stop))
  221. (1- (expt 2 start)))))
  222. (bytevector-u8-set! bv offset
  223. (bitwise-merge m w v)))))
  224. (set0! (lambda (bv offset value)
  225. (setq! bv offset 0 5 value)))
  226. (set1! (lambda (bv offset value)
  227. (setq! bv offset 5 8 (bit-field value 0 3))
  228. (or (= (+ 1 offset) (bytevector-length bv))
  229. (setq! bv (+ 1 offset) 0 2 (bit-field value 3 5)))))
  230. (set2! (lambda (bv offset value)
  231. (setq! bv offset 2 7 value)))
  232. (set3! (lambda (bv offset value)
  233. (setq! bv offset 7 8 (bit-field value 0 1))
  234. (or (= (+ 1 offset) (bytevector-length bv))
  235. (setq! bv (+ 1 offset) 0 4 (bit-field value 1 5)))))
  236. (set4! (lambda (bv offset value)
  237. (setq! bv offset 4 8 (bit-field value 0 4))
  238. (or (= (+ 1 offset) (bytevector-length bv))
  239. (setq! bv (+ 1 offset) 0 1 (bit-field value 4 5)))))
  240. (set5! (lambda (bv offset value)
  241. (setq! bv offset 1 6 value)))
  242. (set6! (lambda (bv offset value)
  243. (setq! bv offset 6 8 (bit-field value 0 2))
  244. (or (= (+ 1 offset) (bytevector-length bv))
  245. (setq! bv (+ 1 offset) 0 3 (bit-field value 2 5)))))
  246. (set7! (lambda (bv offset value)
  247. (setq! bv offset 3 8 value)))
  248. (sets (vector set0! set1! set2! set3! set4! set5! set6! set7!)))
  249. (lambda (bv index value)
  250. "Set the INDEXth quintet of BV to VALUE, assuming quintets start from
  251. the least-significant bits."
  252. (let ((p (vector-ref sets (modulo index 8))))
  253. (p bv (quotient (* index 5) 8) (logand value #x1f))))))
  254. (define (base32-string-unfold f s)
  255. "Given procedure F which, when applied to a character, returns the
  256. corresponding quintet, return the bytevector corresponding to string S."
  257. (define len (string-length s))
  258. (let ((bv (make-bytevector (quotient (* len 5) 8))))
  259. (string-fold (lambda (chr index)
  260. (bytevector-quintet-set! bv index (f chr))
  261. (+ 1 index))
  262. 0
  263. s)
  264. bv))
  265. (define (base32-string-unfold-right f s)
  266. "Given procedure F which, when applied to a character, returns the
  267. corresponding quintet, return the bytevector corresponding to string S,
  268. starting from the right of S."
  269. (define len (string-length s))
  270. (let ((bv (make-bytevector (quotient (* len 5) 8))))
  271. (string-fold-right (lambda (chr index)
  272. (bytevector-quintet-set-right! bv index (f chr))
  273. (+ 1 index))
  274. 0
  275. s)
  276. bv))
  277. ;; Invalid base32 character error condition when decoding base32.
  278. (define-condition-type &invalid-base32-character &error
  279. invalid-base32-character?
  280. (character invalid-base32-character-value)
  281. (string invalid-base32-character-string))
  282. (define (make-base32-string->bytevector base32-string-unfold base32-chars)
  283. (let ((char->value (let loop ((i 0)
  284. (v vlist-null))
  285. (if (= i (vector-length base32-chars))
  286. v
  287. (loop (+ 1 i)
  288. (vhash-consv (vector-ref base32-chars i)
  289. i v))))))
  290. (lambda (s)
  291. "Return the binary representation of base32 string S as a bytevector."
  292. (base32-string-unfold (lambda (chr)
  293. (or (and=> (vhash-assv chr char->value) cdr)
  294. (raise (condition
  295. (&invalid-base32-character
  296. (character chr)
  297. (string s))))))
  298. s))))
  299. (define base32-string->bytevector
  300. (make-base32-string->bytevector base32-string-unfold %rfc4648-base32-chars))
  301. (define nix-base32-string->bytevector
  302. (make-base32-string->bytevector base32-string-unfold-right %nix-base32-chars))
  303. ;;; base32.scm ends here