guix-devel/guix/utils.scm

201 lines
7.3 KiB
Scheme
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;; Guix --- Nix package management from Guile. -*- coding: utf-8 -*-
;;; Copyright (C) 2012 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of Guix.
;;;
;;; Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (guix utils)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-60)
#:use-module (rnrs bytevectors)
#:use-module (ice-9 format)
#:use-module ((chop hash)
#:select (bytevector-hash
hash-method/sha256))
#:export (bytevector-quintet-length
bytevector->base32-string
bytevector->nix-base32-string
bytevector->base16-string
sha256))
;;;
;;; Base 32.
;;;
(define bytevector-quintet-ref
(let* ((ref bytevector-u8-ref)
(ref+ (lambda (bv offset)
(let ((o (+ 1 offset)))
(if (>= o (bytevector-length bv))
0
(bytevector-u8-ref bv o)))))
(ref0 (lambda (bv offset)
(bit-field (ref bv offset) 3 8)))
(ref1 (lambda (bv offset)
(logior (ash (bit-field (ref bv offset) 0 3) 2)
(bit-field (ref+ bv offset) 6 8))))
(ref2 (lambda (bv offset)
(bit-field (ref bv offset) 1 6)))
(ref3 (lambda (bv offset)
(logior (ash (bit-field (ref bv offset) 0 1) 4)
(bit-field (ref+ bv offset) 4 8))))
(ref4 (lambda (bv offset)
(logior (ash (bit-field (ref bv offset) 0 4) 1)
(bit-field (ref+ bv offset) 7 8))))
(ref5 (lambda (bv offset)
(bit-field (ref bv offset) 2 7)))
(ref6 (lambda (bv offset)
(logior (ash (bit-field (ref bv offset) 0 2) 3)
(bit-field (ref+ bv offset) 5 8))))
(ref7 (lambda (bv offset)
(bit-field (ref bv offset) 0 5)))
(refs (vector ref0 ref1 ref2 ref3 ref4 ref5 ref6 ref7)))
(lambda (bv index)
"Return the INDEXth quintet of BV."
(let ((p (vector-ref refs (modulo index 8))))
(p bv (quotient (* index 5) 8))))))
(define bytevector-quintet-ref-right
(let* ((ref bytevector-u8-ref)
(ref+ (lambda (bv offset)
(let ((o (+ 1 offset)))
(if (>= o (bytevector-length bv))
0
(bytevector-u8-ref bv o)))))
(ref0 (lambda (bv offset)
(bit-field (ref bv offset) 0 5)))
(ref1 (lambda (bv offset)
(logior (bit-field (ref bv offset) 5 8)
(ash (bit-field (ref+ bv offset) 0 2) 3))))
(ref2 (lambda (bv offset)
(bit-field (ref bv offset) 2 7)))
(ref3 (lambda (bv offset)
(logior (bit-field (ref bv offset) 7 8)
(ash (bit-field (ref+ bv offset) 0 4) 1))))
(ref4 (lambda (bv offset)
(logior (bit-field (ref bv offset) 4 8)
(ash (bit-field (ref+ bv offset) 0 1) 4))))
(ref5 (lambda (bv offset)
(bit-field (ref bv offset) 1 6)))
(ref6 (lambda (bv offset)
(logior (bit-field (ref bv offset) 6 8)
(ash (bit-field (ref+ bv offset) 0 3) 2))))
(ref7 (lambda (bv offset)
(bit-field (ref bv offset) 3 8)))
(refs (vector ref0 ref1 ref2 ref3 ref4 ref5 ref6 ref7)))
(lambda (bv index)
"Return the INDEXth quintet of BV, assuming quintets start from the
least-significant bits, contrary to what RFC 4648 describes."
(let ((p (vector-ref refs (modulo index 8))))
(p bv (quotient (* index 5) 8))))))
(define (bytevector-quintet-length bv)
"Return the number of quintets (including truncated ones) available in BV."
(ceiling (/ (* (bytevector-length bv) 8) 5)))
(define (bytevector-quintet-fold proc init bv)
"Return the result of applying PROC to each quintet of BV and the result of
the previous application or INIT."
(define len
(bytevector-quintet-length bv))
(let loop ((i 0)
(r init))
(if (= i len)
r
(loop (1+ i) (proc (bytevector-quintet-ref bv i) r)))))
(define (bytevector-quintet-fold-right proc init bv)
"Return the result of applying PROC to each quintet of BV and the result of
the previous application or INIT."
(define len
(bytevector-quintet-length bv))
(let loop ((i len)
(r init))
(if (zero? i)
r
(let ((j (- i 1)))
(loop j (proc (bytevector-quintet-ref-right bv j) r))))))
(define (make-bytevector->base32-string quintet-fold base32-chars)
(lambda (bv)
"Return a base32 encoding of BV using BASE32-CHARS as the alphabet."
(let ((chars (quintet-fold (lambda (q r)
(cons (vector-ref base32-chars q)
r))
'()
bv)))
(list->string (reverse chars)))))
(define %nix-base32-chars
;; See `libutil/hash.cc'.
#(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9
#\a #\b #\c #\d #\f #\g #\h #\i #\j #\k #\l #\m #\n
#\p #\q #\r #\s #\v #\w #\x #\y #\z))
(define %rfc4648-base32-chars
#(#\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
#\2 #\3 #\4 #\5 #\6 #\7))
(define bytevector->base32-string
(make-bytevector->base32-string bytevector-quintet-fold
%rfc4648-base32-chars))
(define bytevector->nix-base32-string
(make-bytevector->base32-string bytevector-quintet-fold-right
%nix-base32-chars))
;;;
;;; Base 16.
;;;
(define (bytevector->base16-string bv)
"Return the hexadecimal representation of BV's contents."
(define len
(bytevector-length bv))
(let-syntax ((base16-chars (lambda (s)
(syntax-case s ()
(_
(let ((v (list->vector
(unfold (cut > <> 255)
(lambda (n)
(format #f "~2,'0x" n))
1+
0))))
v))))))
(define chars base16-chars)
(let loop ((i 0)
(r '()))
(if (= i len)
(string-concatenate-reverse r)
(loop (+ 1 i)
(cons (vector-ref chars (bytevector-u8-ref bv i)) r))))))
;;;
;;; Hash.
;;;
(define (sha256 bv)
"Return the SHA256 of BV as a bytevector."
(bytevector-hash hash-method/sha256 bv))