;;> \section{Additional accessors} ;;> Retrieve a 16-bit unsigned integer value from the given bytevector ;;> \var{bv} at offset \var{i}, in little-endian order. (define (bytevector-u16-ref-le bv i) (+ (bytevector-u8-ref bv i) (arithmetic-shift (bytevector-u8-ref bv (+ i 1)) 8))) ;;> Retrieve a 16-bit unsigned integer value from the given bytevector ;;> \var{bv} at offset \var{i}, in big-endian order. (define (bytevector-u16-ref-be bv i) (+ (arithmetic-shift (bytevector-u8-ref bv i) 8) (bytevector-u8-ref bv (+ i 1)))) ;;> Retrieve a 32-bit unsigned integer value from the given bytevector ;;> \var{bv} at offset \var{i}, in little-endian order. (define (bytevector-u32-ref-le bv i) (+ (bytevector-u8-ref bv i) (arithmetic-shift (bytevector-u8-ref bv (+ i 1)) 8) (arithmetic-shift (bytevector-u8-ref bv (+ i 2)) 16) (arithmetic-shift (bytevector-u8-ref bv (+ i 3)) 24))) ;;> Retrieve a 32-bit unsigned integer value from the given bytevector ;;> \var{bv} at offset \var{i}, in big-endian order. (define (bytevector-u32-ref-be bv i) (+ (arithmetic-shift (bytevector-u8-ref bv i) 24) (arithmetic-shift (bytevector-u8-ref bv (+ i 1)) 16) (arithmetic-shift (bytevector-u8-ref bv (+ i 2)) 8) (bytevector-u8-ref bv (+ i 3)))) ;;> \section{Bignum encodings} ;;> A BER compressed integer (X.209) is an unsigned integer in base 128, ;;> most significant digit first, where the high bit is set on all but the ;;> final (least significant) byte. Thus any size integer can be ;;> encoded, but the encoding is efficient and small integers don't take ;;> up any more space than they would in normal char/short/int encodings. (define (bytevector-ber-ref bv . o) (let ((end (if (and (pair? o) (pair? (cdr o))) (cadr o) (bytevector-length bv)))) (let lp ((acc 0) (i (if (pair? o) (car o) 0))) (if (>= i end) (error "unterminated ber integer in bytevector" bv) (let ((b (bytevector-u8-ref bv i))) (if (< b 128) (+ acc b) (lp (arithmetic-shift (+ acc (bitwise-and b 127)) 7) (+ i 1)))))))) (define (bytevector-ber-set! bv n . o) ;;(assert (integer? number) (not (negative? number))) (let ((start (if (pair? o) (car o) 0)) (end (if (and (pair? o) (pair? (cdr o))) (cadr o) (bytevector-length bv)))) (let lp ((n (arithmetic-shift n -7)) (ls (list (bitwise-and n 127)))) (if (zero? n) (do ((i start (+ i 1)) (ls ls (cdr ls))) ((null? ls)) (if (>= i end) (error "integer doesn't fit in bytevector as ber" bv n start end) (bytevector-u8-set! bv i (car ls)))) (lp (arithmetic-shift n -7) (cons (+ 128 (bitwise-and n 127)) ls)))))) ;;> \section{Integer conversion} ;;> Convert an unsigned integer \var{n} to a bytevector representing ;;> the base-256 big-endian form (the zero index holds the MSB). (define (integer->bytevector n) (cond ((zero? n) (make-bytevector 1 0)) ((negative? n) (error "can't convert a negative integer to bytevector" n)) (else (let lp ((n n) (res '())) (if (zero? n) (let* ((len (length res)) (bv (make-bytevector len 0))) (do ((i 0 (+ i 1)) (ls res (cdr ls))) ((= i len) bv) (bytevector-u8-set! bv i (car ls)))) (lp (quotient n 256) (cons (remainder n 256) res))))))) ;;> The inverse of \scheme{integer->bytevector}. Convert a bytevector ;;> representing the base-256 big-endian form (the zero index holds ;;> the MSB) to the corresponding unsigned integer. (define (bytevector->integer bv) (let ((len (bytevector-length bv))) (let lp ((i 0) (n 0)) (if (>= i len) n (lp (+ i 1) (+ (arithmetic-shift n 8) (bytevector-u8-ref bv i))))))) ;;> Utility to pad a bytevector with zeros. Padding is added to the ;;> left so as not to change the big-endian value. (define (bytevector-pad-left bv len) (let ((diff (- len (bytevector-length bv)))) (if (positive? diff) (bytevector-append bv (make-bytevector diff 0)) bv))) ;;> \section{Hex string conversion} ;;> Big-endian conversion, guaranteed padded to even length. (define (integer->hex-string n) (let* ((res (number->string n 16)) (len (string-length res))) (if (even? len) res (string-append "0" res)))) (define (hex-string->integer str) (string->number str 16)) (define (bytevector->hex-string bv) (let ((out (open-output-string)) (len (bytevector-length bv))) (let lp ((i 0)) (cond ((>= i len) (get-output-string out)) (else (write-string (integer->hex-string (bytevector-u8-ref bv i)) out) (lp (+ i 1))))))) (define (hex-string->bytevector str) (integer->bytevector (hex-string->integer str)))