59 lines
2.6 KiB
Racket
59 lines
2.6 KiB
Racket
|
#lang racket/base
|
|||
|
;; "varints" from Google Protocol Buffers,
|
|||
|
;; https://developers.google.com/protocol-buffers/docs/encoding#varints
|
|||
|
;;
|
|||
|
;; "Each byte in a varint, except the last byte, has the most
|
|||
|
;; significant bit (msb) set – this indicates that there are further
|
|||
|
;; bytes to come. The lower 7 bits of each byte are used to store the
|
|||
|
;; two's complement representation of the number in groups of 7 bits,
|
|||
|
;; least significant group first."
|
|||
|
|
|||
|
(provide encode-varint
|
|||
|
decode-varint)
|
|||
|
|
|||
|
(require bitsyntax)
|
|||
|
|
|||
|
(define (encode-varint v)
|
|||
|
(if (< v 128)
|
|||
|
(bytes v)
|
|||
|
(bit-string ((+ (modulo v 128) 128) :: bits 8)
|
|||
|
((encode-varint (quotient v 128)) :: binary))))
|
|||
|
|
|||
|
(define (decode-varint bs ks kf)
|
|||
|
(bit-string-case bs
|
|||
|
#:on-short (lambda (fail) (kf #t))
|
|||
|
([ (= 1 :: bits 1) (v :: bits 7) (rest :: binary) ]
|
|||
|
(decode-varint rest (lambda (acc tail) (ks (+ (* acc 128) v) tail)) kf))
|
|||
|
([ (= 0 :: bits 1) (v :: bits 7) (rest :: binary) ]
|
|||
|
(ks v rest))
|
|||
|
(else
|
|||
|
(kf))))
|
|||
|
|
|||
|
(module+ test
|
|||
|
(require rackunit)
|
|||
|
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 0)) (bytes 0))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 1)) (bytes 1))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 127)) (bytes 127))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 128)) (bytes 128 1))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 255)) (bytes 255 1))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 256)) (bytes 128 2))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 300)) (bytes #b10101100 #b00000010))
|
|||
|
(check-equal? (bit-string->bytes (encode-varint 1000000000)) (bytes 128 148 235 220 3))
|
|||
|
|
|||
|
(define (ks* v rest) (list v (bit-string->bytes rest)))
|
|||
|
(define (kf* [short? #f]) (if short? 'short (void)))
|
|||
|
|
|||
|
(check-equal? (decode-varint (bytes 0) ks* kf*) (list 0 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 0 99) ks* kf*) (list 0 (bytes 99)))
|
|||
|
(check-equal? (decode-varint (bytes 1) ks* kf*) (list 1 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 127) ks* kf*) (list 127 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 128) ks* kf*) 'short)
|
|||
|
(check-equal? (decode-varint (bytes 128 1) ks* kf*) (list 128 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 128 1 99) ks* kf*) (list 128 (bytes 99)))
|
|||
|
(check-equal? (decode-varint (bytes 255 1) ks* kf*) (list 255 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 128 2) ks* kf*) (list 256 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes #b10101100 #b00000010) ks* kf*) (list 300 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 128 148 235 220 3) ks* kf*) (list 1000000000 (bytes)))
|
|||
|
(check-equal? (decode-varint (bytes 128 148 235 220 3 99) ks* kf*) (list 1000000000 (bytes 99))))
|