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/versioned-struct.rkt

118 lines
5.4 KiB
Racket

5 years ago
#lang debug racket/base
(require "base.rkt" "struct.rkt"
racket/dict
6 years ago
racket/match
racket/class
6 years ago
sugar/unstable/dict)
6 years ago
(provide (all-defined-out))
6 years ago
#|
approximates
6 years ago
https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee
6 years ago
|#
6 years ago
(define x:versioned-struct%
(class x:struct%
(super-new)
(init-field [(@type type)] [(@versions versions)])
6 years ago
(unless (for/or ([proc (list integer? procedure? xenomorphic-type? symbol?)])
6 years ago
(proc @type))
6 years ago
(raise-argument-error 'x:versioned-struct "integer, procedure, symbol, or xenomorphic" @type))
6 years ago
(unless (and (dict? @versions) (andmap (λ (v) (or (dict? v) (x:struct? v))) (dict-values @versions)))
6 years ago
(raise-argument-error 'x:versioned-struct "dict of dicts or structish" @versions))
6 years ago
6 years ago
(define (select-field-set val)
(define version-key
(or (dict-ref val x:version-key #f)
6 years ago
(raise-argument-error 'x:versioned-struct-encode "value for version key" x:version-key)))
(define field-object
(or (dict-ref @versions version-key #f)
6 years ago
(raise-argument-error 'x:versioned-struct-encode (format "valid field version: ~v" (dict-keys @versions)) version-key)))
6 years ago
(if (x:struct? field-object) (get-field fields field-object) field-object))
(define/override (decode port parent [length 0])
6 years ago
(define res (setup-private-fields port parent length))
6 years ago
(define which-version (match @type
[(? integer? int) int]
[(? symbol? key) #:when parent (dict-ref parent key)]
[(? procedure? proc) #:when parent (proc parent)]
[(or (? symbol?) (? procedure?))
(raise-argument-error 'x:versioned-struct-decode "valid parent" parent)]
[_ (send @type decode port parent)]))
6 years ago
(dict-set! res x:version-key which-version)
6 years ago
6 years ago
(cond
[(dict-ref @versions 'header #f)
=> (λ (header-val) (parse-fields port res header-val))])
6 years ago
6 years ago
(define field-object
6 years ago
(cond
[(dict-ref @versions which-version #f) => values]
[else
(raise-argument-error 'x:versioned-struct-decode (format "valid field version: ~v" (dict-keys @versions)) which-version)]))
(match field-object
[(? x:versioned-struct?) (send field-object decode port parent)]
6 years ago
[_ (parse-fields port res field-object)]))
6 years ago
5 years ago
(define/override (pre-encode val)
(cond
[(and (not (dict-has-key? val x:version-key)) (dict-has-key? val 'version))
(dict-set val x:version-key (dict-ref val 'version))]
[else val]))
(define/override (encode field-data port [parent-arg #f])
6 years ago
(unless (dict? field-data)
(raise-argument-error 'x:versioned-struct-encode "dict" field-data))
(define parent (mhasheq x:pointers-key null
x:start-offset-key (pos port)
x:parent-key parent-arg
x:val-key field-data
x:pointer-size-key 0))
(hash-set! parent x:pointer-offset-key (+ (pos port) (size field-data parent #f)))
(unless (or (symbol? @type) (procedure? @type))
(send @type encode (dict-ref field-data x:version-key #f) port parent))
6 years ago
(for ([(key type) (in-dict (dict-ref @versions 'header null))])
(send type encode (dict-ref field-data key) port parent))
6 years ago
(define fields (select-field-set field-data))
5 years ago
(unless (andmap (λ (key) (member key (dict-keys field-data))) (dict-keys fields))
(raise-argument-error 'x:versioned-struct-encode (format "hash that contains superset of xversioned-struct keys: ~a" (dict-keys fields)) (dict-keys field-data)))
(for ([(key type) (in-dict fields)])
5 years ago
#R (list key 'in-verseioned-struct)
5 years ago
(send type encode (dict-ref field-data key) port parent))
(for ([ptr (in-list (dict-ref parent x:pointers-key))])
6 years ago
(match ptr
[(x:ptr type val parent) (send type encode val port parent)])))
5 years ago
(define/override (size [val-arg #f] [parent-arg #f] [include-pointers #t])
(unless val-arg
(raise-argument-error 'x:versioned-struct-size "value" val-arg))
(define val (pre-encode val-arg))
6 years ago
(define parent (mhasheq x:parent-key parent-arg
x:val-key val
x:pointer-size-key 0))
(define version-size
6 years ago
(match @type
[(or (? symbol?) (? procedure?)) 0]
[_ (send @type size (dict-ref val x:version-key) parent)]))
6 years ago
(define header-size
6 years ago
(for/sum ([(key type) (in-dict (dict-ref @versions 'header null))])
(send type size (and val (dict-ref val key)) parent)))
(define fields-size
6 years ago
(for/sum ([(key type) (in-dict (select-field-set val))])
(send type size (and val (dict-ref val key)) parent)))
(define pointer-size (if include-pointers (dict-ref parent x:pointer-size-key) 0))
(+ version-size header-size fields-size pointer-size))))
6 years ago
6 years ago
(define (x:versioned-struct? x) (is-a? x x:versioned-struct%))
6 years ago
(define (x:versioned-struct type [versions (dictify)]
#:pre-encode [pre-proc #f]
#:post-decode [post-proc #f]
#:base-class [base-class x:versioned-struct%])
(new (generate-subclass base-class pre-proc post-proc) [type type] [versions versions][fields #f]))