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/xenomorph/xenomorph/list.rkt

138 lines
5.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 debug racket/base
(require racket/class
racket/sequence
racket/contract
"base.rkt"
"int.rkt"
"util.rkt"
sugar/unstable/dict)
(provide (all-defined-out))
#|
approximates
https://github.com/mbutterick/restructure/blob/master/src/Array.coffee
|#
(define x:list%
(class x:base%
(super-new)
(init-field [(@type type)] [(@len len)] [(@count-bytes? count-bytes?)])
(unless (xenomorphic-type? @type)
(raise-argument-error 'x:list "xenomorphic type" @type))
(unless (length-resolvable? @len)
(raise-argument-error 'x:list "length-resolvable?" @len))
(unless (boolean? @count-bytes?)
(raise-argument-error 'x:list "boolean" @count-bytes?))
(define/augride (x:decode port parent)
(define new-parent (if (x:int? @len)
(mhasheq x:parent-key parent
x:start-offset-key (pos port)
x:current-offset-key 0
x:length-key @len)
parent))
(define len (resolve-length @len port parent))
(cond
[(or (not len) @count-bytes?)
(define end-pos (cond
;; len is byte length
[len (+ (pos port) len)]
;; no len, but parent has length
[(and parent (not (zero? (hash-ref parent x:length-key))))
(+ (hash-ref parent x:start-offset-key) (hash-ref parent x:length-key))]
;; no len or parent, so consume whole stream
[else +inf.0]))
(for/list ([i (in-naturals)]
#:break (or (eof-object? (peek-byte port)) (= (pos port) end-pos)))
(send @type x:decode port new-parent))]
;; we have len, which is treated as count of items
[else (for/list ([i (in-range len)])
(when (eof-object? (peek-byte port))
(raise-argument-error 'decode (format "bytes for ~a items" len) i))
(send @type x:decode port new-parent))]))
(define/augride (x:encode val-arg port [parent #f])
(unless (sequence? val-arg)
(raise-argument-error 'encode "sequence" val-arg))
(define vals (if (list? val-arg) val-arg (sequence->list val-arg)))
;; if @len is not an integer, we have variable length
(define maybe-fixed-len (and (integer? @len) @len))
(when maybe-fixed-len
(unless (eq? (length vals) maybe-fixed-len)
(raise-argument-error 'encode (format "sequence of ~a values" maybe-fixed-len) (length vals))))
(define (encode-items parent)
(for ([item (in-list vals)]
[idx (in-range (or maybe-fixed-len +inf.0))])
(send @type x:encode item port parent)))
(cond
[(x:int? @len)
(define new-parent (mhasheq x:pointers-key null
x:start-offset-key (pos port)
x:parent-key parent))
(hash-set! new-parent x:pointer-offset-key (+ (pos port) (x:size vals new-parent)))
(send @len x:encode (length vals) port) ; encode length at front
(encode-items new-parent)
(for ([ptr (in-list (hash-ref new-parent x:pointers-key))]) ; encode pointer data at end
(send (x:ptr-type ptr) x:encode (x:ptr-val ptr) port))]
[else (encode-items parent)]))
(define/augride (x:size [val #f] [parent #f])
(when val (unless (sequence? val)
(raise-argument-error 'size "sequence" val)))
(cond
[val (define-values (new-parent len-size)
(if (x:int? @len)
(values (mhasheq x:parent-key parent) (send @len x:size))
(values parent 0)))
(define items-size (for/sum ([item val])
(send @type x:size item new-parent)))
(+ items-size len-size)]
[else (define count (resolve-length @len #f parent))
(define size (send @type x:size #f parent))
(* size count)]))))
(define (x:list? x) (is-a? x x:list%))
(define/contract (x:list
[type-arg #f]
[len-arg #f]
#:type [type-kwarg uint8]
#:length [len-kwarg #f]
#:count-bytes [count-bytes? #f]
#:pre-encode [pre-proc #f]
#:post-decode [post-proc #f]
#:base-class [base-class x:list%])
(()
((or/c xenomorphic? #false)
(or/c length-resolvable? #false)
#:type (or/c xenomorphic? #false)
#:length (or/c length-resolvable? #false)
#:count-bytes boolean?
#:pre-encode (or/c (any/c . -> . any/c) #false)
#:post-decode (or/c (any/c . -> . any/c) #false)
#:base-class (λ (c) (subclass? c x:list%)))
. ->* .
x:list?)
(define type (or type-arg type-kwarg))
(unless (xenomorphic? type)
(raise-argument-error 'x:list "xenomorphic type" type))
(define len (or len-arg len-kwarg))
(unless (length-resolvable? len)
(raise-argument-error 'x:list "resolvable length" len))
(new (generate-subclass base-class pre-proc post-proc)
[type type]
[len len]
[count-bytes? count-bytes?]))
(define x:array% x:list%)
(define x:array x:list)
(define x:array? x:list?)
(module+ test
(require rackunit "base.rkt")
(check-equal? (decode (x:list uint16be 3) #"ABCDEF") '(16706 17220 17734))
(check-equal? (encode (x:list uint16be 3) '(16706 17220 17734) #f) #"ABCDEF")
(check-equal? (send (x:list uint16be) x:size '(1 2 3)) 6))