From 3d15b505f41c6c8dd6dc5f828e31306a2408e5a8 Mon Sep 17 00:00:00 2001 From: Matthew Butterick Date: Sun, 28 Apr 2019 21:40:19 -0700 Subject: [PATCH] =?UTF-8?q?struct=20=E2=86=92=20dict?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xenomorph/xenomorph/{struct.rkt => dict.rkt} | 33 +++--- xenomorph/xenomorph/main.rkt | 4 +- .../xenomorph/scribblings/xenomorph.scrbl | 26 ++--- .../test/{struct-test.rkt => dict-test.rkt} | 26 ++--- xenomorph/xenomorph/test/list-test.rkt | 4 +- xenomorph/xenomorph/test/main.rkt | 4 +- xenomorph/xenomorph/test/pointer-test.rkt | 4 +- xenomorph/xenomorph/test/vector-test.rkt | 4 +- ...truct-test.rkt => versioned-dict-test.rkt} | 110 +++++++++--------- ...ersioned-struct.rkt => versioned-dict.rkt} | 41 ++++--- 10 files changed, 133 insertions(+), 123 deletions(-) rename xenomorph/xenomorph/{struct.rkt => dict.rkt} (83%) rename xenomorph/xenomorph/test/{struct-test.rkt => dict-test.rkt} (73%) rename xenomorph/xenomorph/test/{versioned-struct-test.rkt => versioned-dict-test.rkt} (76%) rename xenomorph/xenomorph/{versioned-struct.rkt => versioned-dict.rkt} (76%) diff --git a/xenomorph/xenomorph/struct.rkt b/xenomorph/xenomorph/dict.rkt similarity index 83% rename from xenomorph/xenomorph/struct.rkt rename to xenomorph/xenomorph/dict.rkt index 17b4d838..81c6d6ef 100644 --- a/xenomorph/xenomorph/struct.rkt +++ b/xenomorph/xenomorph/dict.rkt @@ -25,9 +25,9 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee mheq) (define (parse-fields port mheq fields-arg) - (define fields (if (x:struct? fields-arg) (get-field fields fields-arg) fields-arg)) + (define fields (if (x:dict? fields-arg) (get-field fields fields-arg) fields-arg)) (unless (assocs? fields) - (raise-argument-error 'x:struct-parse-fields "assocs" fields)) + (raise-argument-error 'x:dict-parse-fields "assocs" fields)) (for ([(key type) (in-dict fields)]) (define val (match type [(? procedure? proc) (proc mheq)] @@ -37,7 +37,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (hash-set! mheq x:current-offset-key (- (pos port) (hash-ref mheq x:start-offset-key)))) mheq) -(define x:struct% +(define x:dict% (class x:base% (super-new) (init-field [(@fields fields)]) @@ -54,10 +54,10 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (define/augride (x:encode field-data port [parent-arg #f]) (unless (dict? field-data) - (raise-result-error 'x:struct-encode "dict" field-data)) + (raise-result-error 'x:dict-encode "dict" field-data)) ;; check keys, because `size` also relies on keys being valid (unless (andmap (λ (field-key) (memq field-key (dict-keys field-data))) (dict-keys @fields)) - (raise-argument-error 'x:struct-encode + (raise-argument-error 'x:dict-encode (format "dict that contains superset of xstruct keys: ~a" (dict-keys @fields)) (dict-keys field-data))) (define parent (mhasheq x:pointers-key null @@ -82,22 +82,22 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (define pointers-size (if include-pointers (dict-ref parent x:pointer-size-key) 0)) (+ fields-size pointers-size)))) -(define (x:struct? x) (is-a? x x:struct%)) +(define (x:dict? x) (is-a? x x:dict%)) -(define/contract (x:struct #:pre-encode [pre-proc #f] +(define/contract (x:dict #:pre-encode [pre-proc #f] #:post-decode [post-proc #f] - #:base-class [base-class x:struct%] + #:base-class [base-class x:dict%] . dicts) (() (#:pre-encode (or/c (any/c . -> . any/c) #false) #:post-decode (or/c (any/c . -> . any/c) #false) - #:base-class (λ (c) (subclass? c x:struct%))) + #:base-class (λ (c) (subclass? c x:dict%))) #:rest (listof any/c) . ->* . - x:struct?) + x:dict?) (define args (flatten dicts)) (unless (even? (length args)) - (raise-argument-error 'x:struct "equal number of keys and values" dicts)) + (raise-argument-error 'x:dict "equal number of keys and values" dicts)) (define fields (for/list ([kv (in-slice 2 args)]) (unless (symbol? (car kv)) (raise-argument-error '+xstruct "symbol" (car kv))) @@ -107,7 +107,7 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (module+ test (require rackunit "number.rkt" "base.rkt") (define (random-pick xs) (list-ref xs (random (length xs)))) - (check-exn exn:fail:contract? (λ () (x:struct 42))) + (check-exn exn:fail:contract? (λ () (x:dict 42))) (for ([i (in-range 20)]) ;; make random structs and make sure we can round trip (define field-types @@ -116,8 +116,13 @@ https://github.com/mbutterick/restructure/blob/master/src/Struct.coffee (define size-num-types (for/sum ([num-type (in-list field-types)]) (size num-type))) - (define xs (x:struct (for/list ([num-type (in-list field-types)]) + (define xs (x:dict (for/list ([num-type (in-list field-types)]) (cons (gensym) num-type)))) (define bs (apply bytes (for/list ([i (in-range size-num-types)]) (random 256)))) - (check-equal? (encode xs (decode xs bs) #f) bs))) \ No newline at end of file + (check-equal? (encode xs (decode xs bs) #f) bs))) + +;; bw compat +(define x:struct% x:dict%) +(define x:struct? x:dict?) +(define x:struct x:dict) \ No newline at end of file diff --git a/xenomorph/xenomorph/main.rkt b/xenomorph/xenomorph/main.rkt index f6e87350..5484ddce 100644 --- a/xenomorph/xenomorph/main.rkt +++ b/xenomorph/xenomorph/main.rkt @@ -6,6 +6,7 @@ (r+p "bitfield.rkt" "bytes.rkt" + "dict.rkt" "enum.rkt" "base.rkt" "list.rkt" @@ -15,8 +16,7 @@ "reserved.rkt" "string.rkt" "stream.rkt" - "struct.rkt" "symbol.rkt" "vector.rkt" - "versioned-struct.rkt" + "versioned-dict.rkt" "util.rkt") diff --git a/xenomorph/xenomorph/scribblings/xenomorph.scrbl b/xenomorph/xenomorph/scribblings/xenomorph.scrbl index 753ae1f3..30f3ccd5 100644 --- a/xenomorph/xenomorph/scribblings/xenomorph.scrbl +++ b/xenomorph/xenomorph/scribblings/xenomorph.scrbl @@ -21,7 +21,7 @@ OK, just a few of you, in the back. You're free to go. As for everyone else: Xenomorph eases the pain of working with binary formats. Instead of laboriously counting bytes — @itemlist[#:style 'ordered -@item{You describe a binary format declaratively by using smaller ingredients — e.g., integers, strings, lists, pointers, structs, and perhaps other nested encodings. This is known as a @deftech{xenomorphic object}.} +@item{You describe a binary format declaratively by using smaller ingredients — e.g., integers, strings, lists, pointers, dicts, and perhaps other nested encodings. This is known as a @deftech{xenomorphic object}.} @item{This xenomorphic object can then be used as a binary encoder, allowing you to convert Racket values to binary and write them out to a file.} @@ -823,13 +823,13 @@ Generate an instance of @racket[x:vector%] (or a subclass of @racket[x:vector%]) } -@subsection{Structs} +@subsection{Dicts} -@defmodule[xenomorph/struct] +@defmodule[xenomorph/dict] -@defclass[x:struct% x:base% ()]{ -Base class for struct formats. Use @racket[x:struct] to conveniently instantiate new struct formats. +@defclass[x:dict% x:base% ()]{ +Base class for struct formats. Use @racket[x:dict] to conveniently instantiate new struct formats. @defconstructor[ ([fields dict?])]{ @@ -859,22 +859,22 @@ Take the keys and values in @racket[kvs] and encode them as a @tech{byte string} } @defproc[ -(x:struct? +(x:dict? [x any/c]) boolean?]{ -Whether @racket[x] is an object of type @racket[x:struct%]. +Whether @racket[x] is an object of type @racket[x:dict%]. } @defproc[ -(x:struct +(x:dict [#: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:struct%)) x:struct%] +[#:base-class base-class (λ (c) (subclass? c x:dict%)) x:dict%] [dict (listof (pairof symbol? xenomorphic?))] ... [key symbol?] [val-type xenomorphic?] ... ... ) -x:struct?]{ -Generate an instance of @racket[x:struct%] (or a subclass of @racket[x:struct%]) with certain optional attributes. +x:dict?]{ +Generate an instance of @racket[x:dict%] (or a subclass of @racket[x:dict%]) with certain optional attributes. The rest arguments determine the keys and value types of the struct. These arguments can either be alternating keys and value-type arguments (similar to the calling pattern for @racket[hasheq]) or @tech{association lists}. @@ -885,9 +885,9 @@ The rest arguments determine the keys and value types of the struct. These argum -@subsection{Versioned structs} +@subsection{Versioned dicts} -@defmodule[xenomorph/versioned-struct] +@defmodule[xenomorph/versioned-dict] @subsection{Pointers} diff --git a/xenomorph/xenomorph/test/struct-test.rkt b/xenomorph/xenomorph/test/dict-test.rkt similarity index 73% rename from xenomorph/xenomorph/test/struct-test.rkt rename to xenomorph/xenomorph/test/dict-test.rkt index 13d1ec68..2af3e54f 100644 --- a/xenomorph/xenomorph/test/struct-test.rkt +++ b/xenomorph/xenomorph/test/dict-test.rkt @@ -2,7 +2,7 @@ (require rackunit racket/dict racket/class "../base.rkt" - "../struct.rkt" + "../dict.rkt" "../string.rkt" "../pointer.rkt" "../number.rkt" @@ -18,21 +18,21 @@ https://github.com/mbutterick/restructure/blob/master/test/Struct.coffee "struct: decode into an object" (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x15")]) (check-equal? - (decode (x:struct 'name (x:string #:length uint8) 'age uint8)) + (decode (x:dict 'name (x:string #:length uint8) 'age uint8)) (mhasheq 'name "roxyb" 'age 21)))) (test-case "struct: decode nested struct into an object" (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x15\x05roxyb\x15")]) (check-equal? - (decode (x:struct 'name (x:string #:length uint8) 'age uint8 - 'nested (x:struct 'name (x:string #:length uint8) 'age uint8))) + (decode (x:dict 'name (x:string #:length uint8) 'age uint8 + 'nested (x:dict 'name (x:string #:length uint8) 'age uint8))) (mhasheq 'name "roxyb" 'age 21 'nested (mhasheq 'name "roxyb" 'age 21))))) (test-case "struct: decode with process hook" (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x20")]) - (define struct (x:struct #:post-decode (λ (o) (hash-set! o 'canDrink (>= (hash-ref o 'age) 21)) o) + (define struct (x:dict #:post-decode (λ (o) (hash-set! o 'canDrink (>= (hash-ref o 'age) 21)) o) 'name (x:string #:length uint8) 'age uint8)) (check-equal? (decode struct) (mhasheq 'name "roxyb" 'age 32 'canDrink #t)))) @@ -40,40 +40,40 @@ https://github.com/mbutterick/restructure/blob/master/test/Struct.coffee (test-case "struct: decode supports function keys" (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x20")]) - (define struct (x:struct 'name (x:string #:length uint8) 'age uint8 'canDrink (λ (o) (>= (hash-ref o 'age) 21)))) + (define struct (x:dict 'name (x:string #:length uint8) 'age uint8 'canDrink (λ (o) (>= (hash-ref o 'age) 21)))) (check-equal? (decode struct) (mhasheq 'name "roxyb" 'age 32 'canDrink #t)))) (test-case "struct: compute the correct size" - (check-equal? (size (x:struct 'name (x:string #:length uint8) 'age uint8) + (check-equal? (size (x:dict 'name (x:string #:length uint8) 'age uint8) (hasheq 'name "roxyb" 'age 32)) 7)) (test-case "struct: compute the correct size with pointers" - (check-equal? (size (x:struct 'name (x:string #:length uint8) + (check-equal? (size (x:dict 'name (x:string #:length uint8) 'age uint8 'ptr (x:pointer #:type (x:string #:length uint8))) (mhash 'name "roxyb" 'age 21 'ptr "hello")) 14)) (test-case "struct: get the correct size when no value is given" - (check-equal? (size (x:struct 'name (x:string 4) 'age uint8)) 5)) + (check-equal? (size (x:dict 'name (x:string 4) 'age uint8)) 5)) (test-case "struct: throw when getting non-fixed length size and no value is given" - (check-exn exn:fail:contract? (λ () (size (x:struct 'name (x:string #:length uint8) 'age uint8))))) + (check-exn exn:fail:contract? (λ () (size (x:dict 'name (x:string #:length uint8) 'age uint8))))) (test-case "struct: encode objects to buffers" (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x15")]) - (check-equal? (decode (x:struct 'name (x:string #:length uint8) 'age uint8)) + (check-equal? (decode (x:dict 'name (x:string #:length uint8) 'age uint8)) (mhasheq 'name "roxyb" 'age 21)))) (test-case "struct: support pre-encode hook" (parameterize ([current-output-port (open-output-bytes)]) - (define struct (x:struct #:pre-encode (λ (val) + (define struct (x:dict #:pre-encode (λ (val) (hash-set! val 'nameLength (string-length (hash-ref val 'name))) val) 'nameLength uint8 'name (x:string 'nameLength) @@ -84,7 +84,7 @@ https://github.com/mbutterick/restructure/blob/master/test/Struct.coffee (test-case "struct: encode pointer data after structure" (parameterize ([current-output-port (open-output-bytes)]) - (define struct (x:struct 'name (x:string #:length uint8) + (define struct (x:dict 'name (x:string #:length uint8) 'age uint8 'ptr (x:pointer #:type (x:string #:length uint8)))) (encode struct (hasheq 'name "roxyb" 'age 21 'ptr "hello")) diff --git a/xenomorph/xenomorph/test/list-test.rkt b/xenomorph/xenomorph/test/list-test.rkt index 0f0d7924..668c9799 100644 --- a/xenomorph/xenomorph/test/list-test.rkt +++ b/xenomorph/xenomorph/test/list-test.rkt @@ -2,7 +2,7 @@ (require rackunit racket/class "../list.rkt" - "../struct.rkt" + "../dict.rkt" "../number.rkt" "../pointer.rkt" "../base.rkt" @@ -21,7 +21,7 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee (test-case "list: decode nested" (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) - (check-equal? (decode (x:list #:type (x:struct 'foo uint8) #:length 4)) + (check-equal? (decode (x:list #:type (x:dict 'foo uint8) #:length 4)) (list (mhasheq 'foo 1) (mhasheq 'foo 2) (mhasheq 'foo 3) diff --git a/xenomorph/xenomorph/test/main.rkt b/xenomorph/xenomorph/test/main.rkt index 86fafe9a..058f2987 100644 --- a/xenomorph/xenomorph/test/main.rkt +++ b/xenomorph/xenomorph/test/main.rkt @@ -2,6 +2,7 @@ (require "bitfield-test.rkt" "bytes-test.rkt" + "dict-test.rkt" "enum-test.rkt" "list-test.rkt" "number-test.rkt" @@ -11,6 +12,5 @@ "stream-test.rkt" "string-test.rkt" "symbol-test.rkt" - "struct-test.rkt" "vector-test.rkt" - "versioned-struct-test.rkt") + "versioned-dict-test.rkt") diff --git a/xenomorph/xenomorph/test/pointer-test.rkt b/xenomorph/xenomorph/test/pointer-test.rkt index 95fd45b2..e22163a8 100644 --- a/xenomorph/xenomorph/test/pointer-test.rkt +++ b/xenomorph/xenomorph/test/pointer-test.rkt @@ -5,7 +5,7 @@ "../base.rkt" "../pointer.rkt" "../number.rkt" - "../struct.rkt" + "../dict.rkt" "../base.rkt" racket/promise sugar/unstable/dict) @@ -54,7 +54,7 @@ https://github.com/mbutterick/restructure/blob/master/test/Pointer.coffee (test-case "pointer: decode should support decoding pointers lazily" (parameterize ([current-input-port (open-input-bytes (bytes 1 53))]) - (define res (decode (x:struct 'ptr (x:pointer #:lazy #t)))) + (define res (decode (x:dict 'ptr (x:pointer #:lazy #t)))) (check-true (promise? (hash-ref res 'ptr))) (check-equal? (force (hash-ref res 'ptr)) 53))) diff --git a/xenomorph/xenomorph/test/vector-test.rkt b/xenomorph/xenomorph/test/vector-test.rkt index 74e903a7..6a40b605 100644 --- a/xenomorph/xenomorph/test/vector-test.rkt +++ b/xenomorph/xenomorph/test/vector-test.rkt @@ -3,7 +3,7 @@ racket/class racket/vector "../vector.rkt" - "../struct.rkt" + "../dict.rkt" "../number.rkt" "../pointer.rkt" "../base.rkt" @@ -22,7 +22,7 @@ https://github.com/mbutterick/restructure/blob/master/test/Array.coffee (test-case "vector: decode nested" (parameterize ([current-input-port (open-input-bytes (bytes 1 2 3 4 5))]) - (check-equal? (decode (x:vector #:type (x:struct 'foo uint8) #:length 4)) + (check-equal? (decode (x:vector #:type (x:dict 'foo uint8) #:length 4)) (vector (mhasheq 'foo 1) (mhasheq 'foo 2) (mhasheq 'foo 3) diff --git a/xenomorph/xenomorph/test/versioned-struct-test.rkt b/xenomorph/xenomorph/test/versioned-dict-test.rkt similarity index 76% rename from xenomorph/xenomorph/test/versioned-struct-test.rkt rename to xenomorph/xenomorph/test/versioned-dict-test.rkt index f8dfb5b7..5a6e6225 100644 --- a/xenomorph/xenomorph/test/versioned-struct-test.rkt +++ b/xenomorph/xenomorph/test/versioned-dict-test.rkt @@ -6,9 +6,9 @@ "../number.rkt" "../string.rkt" "../pointer.rkt" - "../struct.rkt" + "../dict.rkt" "../base.rkt" - "../versioned-struct.rkt") + "../versioned-dict.rkt") #| approximates @@ -16,12 +16,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe |# (test-case - "versioned struct: decode should get version from number type" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: decode should get version from number type" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (parameterize ([current-input-port (open-input-bytes #"\x00\x05roxyb\x15")]) @@ -30,41 +30,41 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (check-equal? (decode vstruct) (mhasheq 'name "roxyb 🤘" 'age 21 x:version-key 1 'gender 0))))) (test-case - "versioned struct: decode should get version from number type, nested" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: decode should get version from number type, nested" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8 - 'nested (x:struct 'foo uint8)) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 'nested (x:dict 'foo uint8)) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8 - 'nested (x:struct 'foo uint8))))]) + 'nested (x:dict 'foo uint8))))]) (parameterize ([current-input-port (open-input-bytes #"\x00\x05roxyb\x15\x2a")]) (check-equal? (decode vstruct) (mhasheq 'name "roxyb" 'age 21 'nested (mhasheq 'foo 42) x:version-key 0))) (parameterize ([current-input-port (open-input-bytes (string->bytes/utf-8 "\x01\x0aroxyb 🤘\x15\x00\x2a"))]) (check-equal? (decode vstruct) (mhasheq 'name "roxyb 🤘" 'age 21 x:version-key 1 'gender 0 'nested (mhasheq 'foo 42)))))) (test-case - "versioned struct: decode should throw for unknown version" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: decode should throw for unknown version" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (parameterize ([current-input-port (open-input-bytes #"\x05\x05roxyb\x15")]) (check-exn exn:fail:contract? (λ () (decode vstruct)))))) (test-case - "versioned struct: decode should support common header block" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: decode should support common header block" + (let ([vstruct (x:versioned-dict uint8 (dictify 'header (dictify 'age uint8 'alive uint8) 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii)) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'gender uint8)))]) (parameterize ([current-input-port (open-input-bytes #"\x00\x15\x01\x05roxyb")]) (check-equal? (decode vstruct) (mhasheq 'name "roxyb" @@ -79,12 +79,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe 'gender 0))))) (test-case - "versioned struct: decode should support parent version key" - (let ([vstruct (x:versioned-struct x:version-key + "versioned dict: decode should support parent version key" + (let ([vstruct (x:versioned-dict x:version-key (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (parameterize ([current-input-port (open-input-bytes #"\x05roxyb\x15")]) @@ -95,12 +95,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (mhasheq 'name "roxyb 🤘" 'age 21 x:version-key 1 'gender 0))))) (test-case - "versioned struct: decode should support sub versioned structs" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: decode should support sub versioned dicts" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:versioned-struct uint8 + 1 (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string uint8)) 1 (dictify 'name (x:string uint8) @@ -116,13 +116,13 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (mhasheq 'name "ice cream" 'isDessert 1 x:version-key 1))))) (test-case - "versioned struct: decode should support process hook" - (let ([vstruct (x:versioned-struct #:post-decode (λ (val) (hash-set! val 'processed "true") val) + "versioned dict: decode should support process hook" + (let ([vstruct (x:versioned-dict #:post-decode (λ (val) (hash-set! val 'processed "true") val) uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (parameterize ([current-input-port (open-input-bytes #"\x00\x05roxyb\x15")]) @@ -130,12 +130,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (mhasheq 'name "roxyb" 'processed "true" 'age 21 x:version-key 0))))) (test-case - "versioned struct: size should compute the correct size" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: size should compute the correct size" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (check-equal? (size vstruct (mhasheq 'name "roxyb" @@ -147,35 +147,35 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe x:version-key 1)) 14))) (test-case - "versioned struct: size should throw for unknown version" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: size should throw for unknown version" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (check-exn exn:fail:contract? (λ () (size vstruct (mhasheq 'name "roxyb" 'age 21 x:version-key 5)))))) (test-case - "versioned struct: size should support common header block" - (let ([struct (x:versioned-struct uint8 + "versioned dict: size should support common header block" + (let ([struct (x:versioned-dict uint8 (dictify 'header (dictify 'age uint8 'alive uint8) 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii)) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'gender uint8)))]) (check-equal? (size struct (mhasheq 'name "roxyb" 'age 21 'alive 1 x:version-key 0)) 9) (check-equal? (size struct (mhasheq 'name "roxyb 🤘" 'gender 0 'age 21 'alive 1 x:version-key 1)) 15))) (test-case - "versioned struct: size should compute the correct size with pointers" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: size should compute the correct size with pointers" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'ptr (x:pointer #:offset-type uint8 #:type (x:string uint8)))))]) @@ -185,23 +185,23 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe 'ptr "hello")) 15))) (test-case - "versioned struct: size should throw if no value is given" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: size should throw if no value is given" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))]) (check-exn exn:fail:contract? (λ () (size vstruct))))) (test-case - "versioned struct: encode should encode objects to buffers" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: encode should encode objects to buffers" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))] [op (open-output-bytes)]) @@ -210,25 +210,25 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (check-equal? (get-output-bytes op) (string->bytes/utf-8 "\x00\x05roxyb\x15\x01\x0aroxyb 🤘\x15\x00")))) (test-case - "versioned struct: encode should throw for unknown version" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: encode should throw for unknown version" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))] [op (open-output-bytes)]) (check-exn exn:fail:contract? (λ () (encode vstruct op (mhasheq 'name "roxyb" 'age 21 x:version-key 5)))))) (test-case - "versioned struct: encode should support common header block" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: encode should support common header block" + (let ([vstruct (x:versioned-dict uint8 (dictify 'header (dictify 'age uint8 'alive uint8) 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii)) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'gender uint8)))] [op (open-output-bytes)]) (encode vstruct (mhasheq 'name "roxyb" 'age 21 'alive 1 x:version-key 0) op) @@ -236,12 +236,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (check-equal? (get-output-bytes op) (string->bytes/utf-8 "\x00\x15\x01\x05roxyb\x01\x15\x01\x0aroxyb 🤘\x00")))) (test-case - "versioned struct: encode should encode pointer data after structure" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: encode should encode pointer data after structure" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'ptr (x:pointer #:offset-type uint8 #:type (x:string uint8)))))] @@ -251,12 +251,12 @@ https://github.com/mbutterick/restructure/blob/master/test/VersionedStruct.coffe (check-equal? (get-output-bytes op) (string->bytes/utf-8 "\x01\x05roxyb\x15\x09\x05hello")))) #;(test-case - "versioned struct: encode should support preEncode hook" - (let ([vstruct (x:versioned-struct uint8 + "versioned dict: encode should support preEncode hook" + (let ([vstruct (x:versioned-dict uint8 (dictify 0 (dictify 'name (x:string #:length uint8 #:encoding 'ascii) 'age uint8) - 1 (x:struct 'name (x:string #:length uint8 #:encoding 'utf8) + 1 (x:dict 'name (x:string #:length uint8 #:encoding 'utf8) 'age uint8 'gender uint8)))] [op (open-output-bytes)]) diff --git a/xenomorph/xenomorph/versioned-struct.rkt b/xenomorph/xenomorph/versioned-dict.rkt similarity index 76% rename from xenomorph/xenomorph/versioned-struct.rkt rename to xenomorph/xenomorph/versioned-dict.rkt index 91b19bfe..22be1b55 100644 --- a/xenomorph/xenomorph/versioned-struct.rkt +++ b/xenomorph/xenomorph/versioned-dict.rkt @@ -1,5 +1,5 @@ #lang debug racket/base -(require "base.rkt" "struct.rkt" +(require "base.rkt" "dict.rkt" racket/dict racket/match racket/class @@ -11,25 +11,25 @@ approximates https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee |# -(define x:versioned-struct% - (class x:struct% +(define x:versioned-dict% + (class x:dict% (super-new) (init-field [(@type type)] [(@versions versions)]) (unless (for/or ([proc (list integer? procedure? xenomorphic-type? symbol?)]) (proc @type)) - (raise-argument-error 'x:versioned-struct "integer, procedure, symbol, or xenomorphic" @type)) - (unless (and (dict? @versions) (andmap (λ (v) (or (dict? v) (x:struct? v))) (dict-values @versions))) - (raise-argument-error 'x:versioned-struct "dict of dicts or structish" @versions)) + (raise-argument-error 'x:versioned-dict "integer, procedure, symbol, or xenomorphic" @type)) + (unless (and (dict? @versions) (andmap (λ (v) (or (dict? v) (x:dict? v))) (dict-values @versions))) + (raise-argument-error 'x:versioned-dict "dict of dicts or structish" @versions)) (define (select-field-set val) (define version-key (or (dict-ref val x:version-key #f) - (raise-argument-error 'x:versioned-struct-encode "value for version key" x:version-key))) + (raise-argument-error 'x:versioned-dict-encode "value for version key" x:version-key))) (define field-object (or (dict-ref @versions version-key #f) - (raise-argument-error 'x:versioned-struct-encode (format "valid field version: ~v" (dict-keys @versions)) version-key))) - (if (x:struct? field-object) (get-field fields field-object) field-object)) + (raise-argument-error 'x:versioned-dict-encode (format "valid field version: ~v" (dict-keys @versions)) version-key))) + (if (x:dict? field-object) (get-field fields field-object) field-object)) (define/override (x:decode port parent [length 0]) (define res (setup-private-fields port parent length)) @@ -38,7 +38,7 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee [(? 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)] + (raise-argument-error 'x:versioned-dict-decode "valid parent" parent)] [_ (send @type x:decode port parent)])) (dict-set! res x:version-key which-version) @@ -47,16 +47,16 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee [header-val (parse-fields port res header-val)]) (match (dict-ref @versions which-version #f) - [#false (raise-argument-error 'x:versioned-struct-decode + [#false (raise-argument-error 'x:versioned-dict-decode (format "valid field version: ~v" (dict-keys @versions)) which-version)] - [(? x:versioned-struct? vs) (send vs x:decode port parent)] + [(? x:versioned-dict? vs) (send vs x:decode port parent)] [field-object (parse-fields port res field-object)])) (define/override (pre-encode val) val) (define/override (x:encode field-data port [parent-arg #f]) (unless (dict? field-data) - (raise-argument-error 'x:versioned-struct-encode "dict" field-data)) + (raise-argument-error 'x:versioned-dict-encode "dict" field-data)) (define parent (mhasheq x:pointers-key null x:start-offset-key (pos port) x:parent-key parent-arg @@ -70,7 +70,7 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee (define fields (select-field-set field-data)) (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))) + (raise-argument-error 'x:versioned-dict-encode (format "hash that contains superset of xversioned-dict keys: ~a" (dict-keys fields)) (dict-keys field-data))) (for ([(key type) (in-dict fields)]) (send type x:encode (dict-ref field-data key) port parent)) (let loop ([i 0]) @@ -82,7 +82,7 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee (define/override (x:size [val-arg #f] [parent-arg #f] [include-pointers #t]) (unless val-arg - (raise-argument-error 'x:versioned-struct-size "value" val-arg)) + (raise-argument-error 'x:versioned-dict-size "value" val-arg)) (define val (pre-encode val-arg)) (define parent (mhasheq x:parent-key parent-arg x:val-key val @@ -101,15 +101,20 @@ https://github.com/mbutterick/restructure/blob/master/src/VersionedStruct.coffee (define pointer-size (if include-pointers (dict-ref parent x:pointer-size-key) 0)) (+ version-size header-size fields-size pointer-size)))) -(define (x:versioned-struct? x) (is-a? x x:versioned-struct%)) +(define (x:versioned-dict? x) (is-a? x x:versioned-dict%)) -(define (x:versioned-struct type +(define (x:versioned-dict type [versions (dictify)] #:pre-encode [pre-proc #f] #:post-decode [post-proc #f] - #:base-class [base-class x:versioned-struct%]) + #:base-class [base-class x:versioned-dict%]) (new (generate-subclass base-class pre-proc post-proc) [type type] [versions versions] [fields #f])) + +;; bw compat +(define x:versioned-struct% x:versioned-dict%) +(define x:versioned-struct? x:versioned-dict?) +(define x:versioned-struct x:versioned-dict) \ No newline at end of file