diff --git a/xenomorph/xenomorph/number.rkt b/xenomorph/xenomorph/number.rkt index 97807d93..dc49ec6b 100644 --- a/xenomorph/xenomorph/number.rkt +++ b/xenomorph/xenomorph/number.rkt @@ -22,11 +22,11 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (real->floating-point-bytes val @size (eq? @endian 'be))))) (define/contract (x:float [size-arg #f] - #:size [size-kwarg 4] - #:endian [endian system-endian] - #:pre-encode [pre-proc #f] - #:post-decode [post-proc #f] - #:base-class [base-class x:float%]) + #:size [size-kwarg 4] + #:endian [endian system-endian] + #:pre-encode [pre-proc #f] + #:post-decode [post-proc #f] + #:base-class [base-class x:float%]) (() ((or/c exact-positive-integer? #false) #:size exact-positive-integer? @@ -37,6 +37,8 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee . ->* . x:float?) (define size (or size-arg size-kwarg)) + (unless (exact-positive-integer? size) + (raise-argument-error 'x:float% "exact positive integer" size)) (new (generate-subclass base-class pre-proc post-proc) [size size] [endian endian])) (define float (x:float 4)) @@ -47,12 +49,17 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (define doublebe (x:float 8 #:endian 'be)) (define doublele (x:float 8 #:endian 'le)) +(define (x:fixed? x) (is-a? x x:fixed%)) + (define x:fixed% (class x:int% (super-new) (init-field [(@fracbits fracbits)]) + (inherit-field (@size size)) (unless (exact-positive-integer? @fracbits) - (raise-argument-error 'xfixed "exact positive integer for fracbits" @fracbits)) + (raise-argument-error 'x:fixed% "exact positive integer" @fracbits)) + (unless (<= @fracbits (* 8 @size)) + (raise-argument-error 'x:fixed% "fracbits no bigger than size bits" (list @fracbits @size))) (define fixed-shift (arithmetic-shift 1 @fracbits)) @@ -62,13 +69,30 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (define/override (pre-encode val) (exact-if-possible (floor (* val fixed-shift)))))) -(define (x:fixed [size 2] +(define/contract (x:fixed [size-arg #false] + #:size [size-kwarg 2] #:signed [signed #true] #:endian [endian system-endian] - #:fracbits [fracbits (/ (* size 8) 2)] + #:fracbits [fracbits-arg #f] #:pre-encode [pre-proc #f] #:post-decode [post-proc #f] - #:base-class [base-class x:fixed%]) + #:base-class [base-class x:fixed%]) + (() + ((or/c exact-positive-integer? #false) + #:size exact-positive-integer? + #:endian endian-value? + #:fracbits exact-positive-integer? + #:pre-encode (or/c (any/c . -> . any/c) #false) + #:post-decode (or/c (any/c . -> . any/c) #false) + #:base-class (λ (c) (subclass? c x:fixed%))) + . ->* . + x:fixed?) + (define size (or size-arg size-kwarg)) + (unless (exact-positive-integer? size) + (raise-argument-error 'x:fixed "exact positive integer" size)) + (define fracbits (or fracbits-arg (/ (* size 8) 2))) + (unless (<= fracbits (* size 8)) + (raise-argument-error 'x:fixed "fracbits no bigger than size bits" fracbits)) (new (generate-subclass base-class pre-proc post-proc) [size size] [signed signed] [endian endian] [fracbits fracbits])) (define fixed16 (x:fixed 2)) @@ -100,10 +124,10 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (module+ test (define (bigint) (string->number (list->string (for/list ([i (in-range (random 10 30))]) - (integer->char (+ 48 (random 10))))))) + (integer->char (+ 48 (random 10))))))) (for ([i (in-range 100)]) - (define int (bigint)) - (check-= int (decode x:bigint (encode x:bigint int #f)) 0))) + (define int (bigint)) + (check-= int (decode x:bigint (encode x:bigint int #f)) 0))) (define x:exact (x:list @@ -115,8 +139,8 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (module+ test (define (exact) (/ (bigint) (bigint))) (for ([i (in-range 100)]) - (define ex (exact)) - (check-= ex (decode x:exact (encode x:exact ex #f)) 0))) + (define ex (exact)) + (check-= ex (decode x:exact (encode x:exact ex #f)) 0))) (define x:complex (x:list @@ -128,5 +152,5 @@ https://github.com/mbutterick/restructure/blob/master/src/Number.coffee (module+ test (define (complex) (+ (exact) (* +i (exact) 1.0) 1.0)) (for ([i (in-range 100)]) - (define c (complex)) - (check-= c (decode x:complex (encode x:complex c #f)) 0.1))) \ No newline at end of file + (define c (complex)) + (check-= c (decode x:complex (encode x:complex c #f)) 0.1))) \ No newline at end of file diff --git a/xenomorph/xenomorph/scribblings/xenomorph.scrbl b/xenomorph/xenomorph/scribblings/xenomorph.scrbl index df283188..29d6666e 100644 --- a/xenomorph/xenomorph/scribblings/xenomorph.scrbl +++ b/xenomorph/xenomorph/scribblings/xenomorph.scrbl @@ -291,8 +291,9 @@ Use this value carefully, however, as most binary formats are defined using one @defconstructor[ ([size exact-positive-integer?] +[signed? boolean?] [endian endian-value?])]{ -Create class instance that represents a binary number format @racket[size] bytes long with @racket[endian] byte ordering. The endian arugment can be @racket[system-endian]. +Create class instance that represents a binary number format @racket[size] bytes long, either @racket[signed?] or not, with @racket[endian] byte ordering. The endian arugment can be @racket[system-endian]. } } @@ -427,7 +428,70 @@ The common 64-bit floating-point types. They differ in byte-ordering convention: } -@subsubsection{Fixed-points} +@subsubsection{Fixed-point numbers} + + +@defclass[x:fixed% x:int% ()]{ +Base class for fixed-point number objects. Use @racket[x:fixed] to conveniently instantiate new fixed-point number objects. + +@defconstructor[ +([size exact-positive-integer?] +[signed? boolean?] +[endian endian-value?] +[fracbits exact-positive-integer?])]{ +Create class instance that represents a fixed-point number format @racket[size] bytes long, either @racket[signed?] or not, with @racket[endian] byte ordering and @racket[fracbits] of precision. +} + + +} + +@defproc[ +(x:fixed? +[x any/c]) +boolean?]{ +Predicate for whether @racket[x] is an object of type @racket[x:fixed%]. +} + +@defproc[ +(x:fixed +[size-arg (or/c exact-positive-integer? #false) #false] +[#:size size-kw exact-positive-integer? 2] +[#:endian endian endian-value? system-endian] +[#:fracbits fracbits (or/c exact-positive-integer? #false) #false] +[#:pre-encode pre-encode-proc (or/c (any/c . -> . any/c) #false) #false] +[#:post-decode post-decode-proc (or/c (any/c . -> . any/c) #false) #false] +[#:base-class base-class (λ (c) (subclass? c x:fixed%)) x:fixed%] +) +x:int?]{ +Generate an instance of @racket[x:fixed%] (or a subclass of @racket[x:fixed%]) with certain optional attributes. + +@racket[size-arg] or @racket[size-kw] (whichever is provided, though @racket[size-kw] takes precedence) controls the encoded size. + +@racket[endian] controls the byte-ordering convention. + +@racket[fracbits] controls the number of bits of precision. If no value or @racket[#false] is passed, defaults to @racket[(/ (* _size 8) 2)]. + +@racket[pre-encode-proc] and @racket[post-decode-proc] control the pre-encoding and post-decodeing procedures, respectively. + +@racket[base-class] controls the class used for instantiation of the new object. +} + +@deftogether[ +(@defthing[fixed16 x:fixed?] +@defthing[fixed16be x:fixed?] +@defthing[fixed16le x:fixed?]) +]{ +The common 16-bit fixed-point number types with 2 bits of precision. They differ in byte-ordering convention: @racket[fixed16be] uses big endian, @racket[fixed16le] uses little endian, @racket[fixed16] uses @racket[system-endian]. +} + +@deftogether[ +(@defthing[fixed32 x:fixed?] +@defthing[fixed32be x:fixed?] +@defthing[fixed32le x:fixed?]) +]{ +The common 32-bit fixed-point number types with 4 bits of precision. They differ in byte-ordering convention: @racket[fixed32be] uses big endian, @racket[fixed32le] uses little endian, @racket[fixed32] uses @racket[system-endian]. +} + @subsection{Strings}