resume in pointer

main
Matthew Butterick 6 years ago
parent ad99b7f9aa
commit e669cd70f0

@ -1,5 +1,11 @@
#lang debug racket/base #lang debug racket/base
(require racket/dict racket/sequence "helper.rkt" "number.rkt" "util.rkt" sugar/unstable/dict) (require racket/dict
racket/class
racket/sequence
"helper.rkt"
"number.rkt"
"util.rkt"
sugar/unstable/dict)
(provide (all-defined-out)) (provide (all-defined-out))
#| #|
@ -7,94 +13,96 @@ approximates
https://github.com/mbutterick/restructure/blob/master/src/Array.coffee https://github.com/mbutterick/restructure/blob/master/src/Array.coffee
|# |#
(define/post-decode (xarray-decode xa [port-arg (current-input-port)] #:parent [parent #f]) (define xarray-base%
(define port (->input-port port-arg)) (class* xenobase% ()
(parameterize ([current-input-port port]) (super-new)
(define new-parent (if (xint? (xarray-base-len xa)) (init-field type len)
(mhasheq 'parent parent (unless (xenomorphic? type)
'_startOffset (pos port) (raise-argument-error '+xarray "xenomorphic type" type))
'_currentOffset 0 (unless (length-resolvable? len)
'_length (xarray-base-len xa)) (raise-argument-error '+xarray "length-resolvable?" len))))
parent))
(define decoded-len (resolve-length (xarray-base-len xa) #:parent parent))
(cond
[(or (not decoded-len) (eq? (xarray-length-type xa) 'bytes))
(define end-pos (cond
;; decoded-len is byte length
[decoded-len (+ (pos port) decoded-len)]
;; no decoded-len, but parent has length
[(and parent (not (zero? (dict-ref parent '_length)))) (+ (dict-ref parent '_startOffset) (dict-ref parent '_length))]
;; no decoded-len or parent, so consume whole stream
[else +inf.0]))
(for/list ([i (in-naturals)]
#:break (or (eof-object? (peek-byte)) (= (pos port) end-pos)))
(xdecode (xarray-base-type xa) #:parent new-parent))]
;; we have decoded-len, which is treated as count of items
[else (for/list ([i (in-range decoded-len)])
(xdecode (xarray-base-type xa) #:parent new-parent))])))
(define/pre-encode (xarray-encode xa array [port-arg (current-output-port)] #:parent [parent #f]) (define xarray%
(unless (sequence? array) (class* xarray-base% ()
(raise-argument-error 'xarray-encode "sequence" array)) (super-new)
(define port (if (output-port? port-arg) port-arg (open-output-bytes))) (init-field length-type)
(parameterize ([current-output-port port]) (unless (memq length-type '(bytes count))
(define (encode-items parent) (raise-argument-error '+xarray "'bytes or 'count" length-type))
;; todo: should array with fixed length stop encoding after it reaches max?
;; cf. xstring, which rejects input that is too big for fixed length.
(let* (#;[items (sequence->list array)]
#;[item-count (length items)]
#;[max-items (if (number? (xarray-len xa)) (xarray-len xa) item-count)])
(for ([item array])
(encode (xarray-base-type xa) item #:parent parent))))
(cond
[(xint? (xarray-base-len xa))
(let ([parent (mhash 'pointers null
'startOffset (pos port)
'parent parent)])
(dict-set! parent 'pointerOffset (+ (pos port) (size xa array #:parent parent)))
(encode (xarray-base-len xa) (length array)) ; encode length at front
(encode-items parent)
(for ([ptr (in-list (dict-ref parent 'pointers))]) ; encode pointer data at end
(encode (dict-ref ptr 'type) (dict-ref ptr 'val))))]
[else (encode-items parent)])
(unless port-arg (get-output-bytes port))))
(define/finalize-size (xarray-size xa [val #f] #:parent [parent #f]) (inherit-field type len)
(when val (unless (sequence? val)
(raise-argument-error 'xarray-size "sequence" val)))
(cond
[val (define-values (new-parent len-size) (if (xint? (xarray-base-len xa))
(values (mhasheq 'parent parent) (size (xarray-base-len xa)))
(values parent 0)))
(define items-size (for/sum ([item val])
(size (xarray-base-type xa) item #:parent new-parent)))
(+ items-size len-size)]
[else (define item-count (resolve-length (xarray-base-len xa) #f #:parent parent))
(define item-size (size (xarray-base-type xa) #f #:parent parent))
(* item-size item-count)]))
(struct xarray-base xbase (type len) #:transparent) (define/augment (xxdecode port parent . _)
(struct xarray xarray-base (length-type) #:transparent (define new-parent (if (xint? len)
#:methods gen:xenomorphic (mhasheq 'parent parent
[(define decode xarray-decode) '_startOffset (pos port)
(define xdecode xarray-decode) '_currentOffset 0
(define encode xarray-encode) '_length len)
(define size xarray-size)]) parent))
(define decoded-len (resolve-length len #:parent parent))
(cond
[(or (not decoded-len) (eq? length-type 'bytes))
(define end-pos (cond
;; decoded-len is byte length
[decoded-len (+ (pos port) decoded-len)]
;; no decoded-len, but parent has length
[(and parent (not (zero? (dict-ref parent '_length))))
(+ (dict-ref parent '_startOffset) (dict-ref parent '_length))]
;; no decoded-len or parent, so consume whole stream
[else +inf.0]))
(for/list ([i (in-naturals)]
#:break (or (eof-object? (peek-byte)) (= (pos port) end-pos)))
(send type xxdecode port new-parent))]
;; we have decoded-len, which is treated as count of items
[else (for/list ([i (in-range decoded-len)])
(send type xxdecode port new-parent))]))
(define/augment (xxencode array port [parent #f])
(unless (sequence? array)
(raise-argument-error 'xarray-encode "sequence" array))
(define (encode-items parent)
;; todo: should array with fixed length stop encoding after it reaches max?
;; cf. xstring, which rejects input that is too big for fixed length.
(let* (#;[items (sequence->list array)]
#;[item-count (length items)]
#;[max-items (if (number? (xarray-len xa)) (xarray-len xa) item-count)])
(for ([item array])
(send type xxencode item port parent))))
(cond
[(xint? len)
(let ([parent (mhash 'pointers null
'startOffset (pos port)
'parent parent)])
(dict-set! parent 'pointerOffset (+ (pos port) (xxsize array parent)))
(send len xxencode (length array) port) ; encode length at front
(encode-items parent)
(for ([ptr (in-list (dict-ref parent 'pointers))]) ; encode pointer data at end
(send (dict-ref ptr 'type) xxencode (dict-ref ptr 'val) port)))]
[else (encode-items parent)]))
(define/augment (xxsize [val #f] [parent #f])
(when val (unless (sequence? val)
(raise-argument-error 'xarray-size "sequence" val)))
(cond
[val (define-values (new-parent len-size)
(if (xint? len)
(values (mhasheq 'parent parent) (send len xxsize))
(values parent 0)))
(define items-size (for/sum ([item val])
(send type xxsize item new-parent)))
(+ items-size len-size)]
[else (define item-count (resolve-length len #f #:parent parent))
(define item-size (send type xxsize #f parent))
(* item-size item-count)]))))
(define (+xarray [type-arg #f] [len-arg #f] [length-type-arg 'count] (define (+xarray [type-arg #f] [len-arg #f] [length-type-arg 'count]
#:type [type-kwarg #f] #:length [len-kwarg #f] #:count-bytes [count-bytes? #f]) #:type [type-kwarg #f]
(define type (or type-arg type-kwarg)) #:length [len-kwarg #f]
(define len (or len-arg len-kwarg)) #:count-bytes [count-bytes? #f]
(define length-type (if count-bytes? 'bytes length-type-arg)) #:subclass [class xarray%])
(unless (xenomorphic? type) (new class [type (or type-arg type-kwarg)]
(raise-argument-error '+xarray "xenomorphic type" type)) [len (or len-arg len-kwarg)]
(unless (length-resolvable? len) [length-type (if count-bytes? 'bytes length-type-arg)]))
(raise-argument-error '+xarray "length-resolvable?" len))
(unless (memq length-type '(bytes count))
(raise-argument-error '+xarray "'bytes or 'count" length-type))
(xarray type len length-type))
(module+ test (module+ test
(require rackunit) (require rackunit)
(check-equal? (decode (+xarray uint16be 3) #"ABCDEF") '(16706 17220 17734)) (check-equal? (decode (+xarray uint16be 3) #"ABCDEF") '(16706 17220 17734))

@ -2,6 +2,7 @@
(require "helper.rkt" (require "helper.rkt"
"number.rkt" "number.rkt"
racket/dict racket/dict
racket/class
racket/promise racket/promise
sugar/unstable/dict) sugar/unstable/dict)
(provide (all-defined-out)) (provide (all-defined-out))
@ -16,7 +17,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
[(dict-ref parent 'parent #f) => find-top-parent] [(dict-ref parent 'parent #f) => find-top-parent]
[else parent])) [else parent]))
(define/post-decode (xpointer-decode xp [port-arg (current-input-port)] #:parent [parent #f]) #;(define/post-decode (xpointer-decode xp [port-arg (current-input-port)] #:parent [parent #f])
(define port (->input-port port-arg)) (define port (->input-port port-arg))
(parameterize ([current-input-port port]) (parameterize ([current-input-port port])
(define offset (xdecode (xpointer-offset-type xp) #:parent parent)) (define offset (xdecode (xpointer-offset-type xp) #:parent parent))
@ -53,7 +54,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
[(xvoid-pointer? val) (values (xvoid-pointer-type val) (xvoid-pointer-value val))] [(xvoid-pointer? val) (values (xvoid-pointer-type val) (xvoid-pointer-value val))]
[else (raise-argument-error 'Pointer:size "VoidPointer" val)])) [else (raise-argument-error 'Pointer:size "VoidPointer" val)]))
(define/pre-encode (xpointer-encode xp val [port-arg (current-output-port)] #:parent [parent #f]) #;(define/pre-encode (xpointer-encode xp val [port-arg (current-output-port)] #:parent [parent #f])
(define port (if (output-port? port-arg) port-arg (open-output-bytes))) (define port (if (output-port? port-arg) port-arg (open-output-bytes)))
(unless parent ; todo: furnish default pointer context? adapt from Struct? (unless parent ; todo: furnish default pointer context? adapt from Struct?
(raise-argument-error 'xpointer-encode "valid pointer context" parent)) (raise-argument-error 'xpointer-encode "valid pointer context" parent))
@ -78,7 +79,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
(dict-set! new-parent 'pointerOffset (+ (dict-ref new-parent 'pointerOffset) (size type val #:parent parent))))))) (dict-set! new-parent 'pointerOffset (+ (dict-ref new-parent 'pointerOffset) (size type val #:parent parent)))))))
(unless port-arg (get-output-bytes port))) (unless port-arg (get-output-bytes port)))
(define (xpointer-size xp [val #f] #:parent [parent #f]) #;(define (xpointer-size xp [val #f] #:parent [parent #f])
(let*-values ([(parent) (case (pointer-relative-to xp) (let*-values ([(parent) (case (pointer-relative-to xp)
[(local immediate) parent] [(local immediate) parent]
[(parent) (dict-ref parent 'parent)] [(parent) (dict-ref parent 'parent)]
@ -90,7 +91,20 @@ https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
(+ (dict-ref parent 'pointerSize) (size type val #:parent parent))))) (+ (dict-ref parent 'pointerSize) (size type val #:parent parent)))))
(size (xpointer-offset-type xp)))) (size (xpointer-offset-type xp))))
(struct xpointer xbase (offset-type type options) #:transparent (define xpointer%
(class* xenobase% ()
(super-new)
(init-field offset-type type options)
(define pointer-relative-to (dict-ref options 'relative-to))
(define allow-null (dict-ref options 'allowNull))
(define null-value (dict-ref options 'nullValue))
(define pointer-lazy? (dict-ref options 'lazy))
))
#;(struct xpointer xbase (offset-type type options) #:transparent
#:methods gen:xenomorphic #:methods gen:xenomorphic
[(define decode xpointer-decode) [(define decode xpointer-decode)
(define xdecode xpointer-decode) (define xdecode xpointer-decode)
@ -111,14 +125,12 @@ https://github.com/mbutterick/restructure/blob/master/src/Pointer.coffee
'lazy lazy? 'lazy lazy?
'allowNull allow-null? 'allowNull allow-null?
'nullValue null-value)) 'nullValue null-value))
(define offset-type (or offset-arg offset-kwarg uint8))
(define type-in (or type-arg type-kwarg uint8)) (define type-in (or type-arg type-kwarg uint8))
(xpointer offset-type (case type-in [(void) #f][else type-in]) options)) (new xpointer%
[offset-type (or offset-arg offset-kwarg uint8)]
[type (case type-in [(void) #f][else type-in])]
[options options]))
(define (pointer-relative-to xp) (dict-ref (xpointer-options xp) 'relative-to))
(define (allow-null xp) (dict-ref (xpointer-options xp) 'allowNull))
(define (null-value xp) (dict-ref (xpointer-options xp) 'nullValue))
(define (pointer-lazy? xp) (dict-ref (xpointer-options xp) 'lazy))
;; A pointer whose type is determined at decode time ;; A pointer whose type is determined at decode time
(struct xvoid-pointer (type value) #:transparent) (struct xvoid-pointer (type value) #:transparent)

@ -1,5 +1,6 @@
#lang racket/base #lang racket/base
(require rackunit (require rackunit
racket/class
"../helper.rkt" "../helper.rkt"
"../array.rkt" "../array.rkt"
"../number.rkt" "../number.rkt"
@ -19,8 +20,10 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee
(test-case (test-case
"decode with post-decode" "decode with post-decode"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(define xa (+xarray #:type uint8 #:length 4)) (define myarray% (class xarray%
(set-post-decode! xa (λ (val . _) (map (λ (x) (* 2 x)) val))) (super-new)
(define/override (post-decode val) (map (λ (x) (* 2 x)) val))))
(define xa (+xarray #:type uint8 #:length 4 #:subclass myarray%))
(check-equal? (decode xa) '(2 4 6 8)))) (check-equal? (decode xa) '(2 4 6 8))))
(test-case (test-case
@ -31,12 +34,12 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee
(test-case (test-case
"decode length from parent key" "decode length from parent key"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(check-equal? (xdecode (+xarray #:type uint8 #:length 'len) #:parent (mhash 'len 4)) '(1 2 3 4)))) (check-equal? (send (+xarray #:type uint8 #:length 'len) xxdecode (current-input-port) (mhash 'len 4)) '(1 2 3 4))))
(test-case (test-case
"decode byte count from parent key" "decode byte count from parent key"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(check-equal? (xdecode (+xarray #:type uint16be #:length 'len #:count-bytes #t) #:parent (mhash 'len 4)) '(258 772)))) (check-equal? (send (+xarray #:type uint16be #:length 'len #:count-bytes #t) xxdecode (current-input-port) (mhash 'len 4)) '(258 772))))
(test-case (test-case
"decode length as number before array" "decode length as number before array"
@ -61,12 +64,12 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee
(test-case (test-case
"decode to the end of parent if no length given" "decode to the end of parent if no length given"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(check-equal? (xdecode (+xarray #:type uint8) #:parent (mhash '_length 4 '_startOffset 0)) '(1 2 3 4)))) (check-equal? (send (+xarray #:type uint8) xxdecode (current-input-port) (mhash '_length 4 '_startOffset 0)) '(1 2 3 4))))
(test-case (test-case
"decode to the end of the stream if parent exists, but its length is 0" "decode to the end of the stream if parent exists, but its length is 0"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(check-equal? (xdecode (+xarray #:type uint8) #:parent (mhash '_length 0 '_startOffset 0)) '(1 2 3 4 5)))) (check-equal? (send (+xarray #:type uint8) xxdecode (current-input-port) (mhash '_length 0 '_startOffset 0)) '(1 2 3 4 5))))
(test-case (test-case
"decode to the end of the stream if no parent and length is given" "decode to the end of the stream if no parent and length is given"
@ -92,8 +95,10 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee
(test-case (test-case
"encode with pre-encode" "encode with pre-encode"
(parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))])
(define xa (+xarray #:type uint8 #:length 4)) (define myarray% (class xarray%
(set-pre-encode! xa (λ (val . _) (map (λ (x) (* 2 x)) val))) (super-new)
(define/override (pre-encode val) (map (λ (x) (* 2 x)) val))))
(define xa (+xarray #:type uint8 #:length 4 #:subclass myarray%))
(check-equal? (encode xa '(1 2 3 4) #f) (bytes 2 4 6 8)))) (check-equal? (encode xa '(1 2 3 4) #f) (bytes 2 4 6 8))))
(test-case (test-case

Loading…
Cancel
Save