You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/pitfall/restructure/number.rkt

96 lines
3.6 KiB
Racket

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

#lang restructure/racket
(require "decodestream.rkt" "encodestream.rkt" "streamcoder.rkt")
(provide Number)
#|
approximates
https://github.com/mbutterick/restructure/blob/master/src/Number.coffee
|#
(define (ends-with-8? type)
(define str (symbol->string type))
(equal? (substring str (sub1 (string-length str))) "8"))
(define (signed-type? type)
(not (equal? "U" (substring (symbol->string type) 0 1))))
(test-module
(check-false (signed-type? 'UInt16))
(check-true (signed-type? 'Int16)))
(define-subclass RStreamcoder (Number [type 'UInt16] [endian (if (system-big-endian?) 'BE 'LE)])
(getter-field [fn (string->symbol (format "~a~a" type (if (ends-with-8? type) "" endian)))])
(unless (hash-has-key? type-sizes fn)
(raise-argument-error 'Number "valid type and endian" (format "~v ~v" type endian)))
(getter-field [size (hash-ref type-sizes fn)])
(define/augment (decode stream [res #f])
(define bstr (send stream read size))
(if (= 1 size)
(bytes-ref bstr 0)
(integer-bytes->integer bstr (signed-type? type) (eq? endian 'BE))))
(define/augment (encode stream val)
(define bstr
(if (= 1 size)
(bytes val)
(integer->integer-bytes val size (signed-type? type) (eq? endian 'BE))))
(if stream (send stream write bstr) bstr)))
(test-module
(let ([o (make-object Number 'UInt16 'LE)]
[ip (make-object RDecodeStream (bytes 1 2 3 4))])
(check-equal? (send o decode ip) 513) ;; 1000 0000 0100 0000
(check-equal? (send o decode ip) 1027) ;; 1100 0000 0010 0000
(check-equal? (send o encode #f 513) (bytes 1 2))
(check-equal? (send o encode #f 1027) (bytes 3 4)))
(let ([o (make-object Number 'UInt16 'BE)]
[ip (make-object RDecodeStream (bytes 1 2 3 4))])
(check-equal? (send o decode ip) 258) ;; 0100 0000 1000 0000
(check-equal? (send o decode ip) 772) ;; 0010 0000 1100 0000
(check-equal? (send o encode #f 258) (bytes 1 2))
(check-equal? (send o encode #f 772) (bytes 3 4))))
(test-module
(check-equal? (send (make-object Number 'UInt8) size) 1)
(check-equal? (send (make-object Number) size) 2)
(check-equal? (send (make-object Number 'UInt32) size) 4)
(check-equal? (send (make-object Number 'Double) size) 8))
(require (for-syntax "decodestream.rkt" racket/match))
;; use keys of type-sizes hash to generate corresponding number definitions
(define-macro (make-int-types)
(with-pattern ([((ID BASE ENDIAN) ...) (for/list ([k (in-hash-keys type-sizes)])
(define kstr (format "~a" k))
(match-define (list* prefix suffix _)
(regexp-split #rx"(?=[BL]E|$)" kstr))
(map string->symbol
(list (string-downcase kstr)
prefix
(if (positive? (string-length suffix))
suffix
(if (system-big-endian?) "BE" "LE")))))]
[(ID ...) (suffix-id #'(ID ...) #:context caller-stx)])
#'(begin (define+provide ID (make-object Number 'BASE 'ENDIAN)) ...)))
(make-int-types)
(test-module
(check-equal? (send uint8 size) 1)
(check-equal? (send uint16 size) 2)
(check-equal? (send uint32 size) 4)
(check-equal? (send double size) 8))
(require "encodestream.rkt")
(define n (make-object Number 'UInt32))
(send n encode (make-object REncodeStream) 2351070438)