#
Chapter 39
`serialize.ss`: Serializing Data

To load: `(require (lib "serialize.ss"))`

`(`define-serializable-struct` `id `(`field-id ``···`)` [inspector-expr]`)` SYNTAX

`(`define-serializable-struct` `(id super-id) `(`field-id ``···`)` [inspector-expr]`)` SYNTAX

Like `define-struct`, but instances of the structure type are serializable with `serialize`. This form is allowed only at the top level or in a module's top level (so that deserialization information can be found later).

In addition to the bindings generated by `define-struct`, `define-serializable-struct` binds `deserialize-info:`id`-v0` to deserialization information. Furthermore, in a module context, it automatically `provide`s this binding.

Naturally, `define-serializable-struct` enables the construction of structure instances from places where `make-`id`` is not accessible, since deserialization must construct instances. Furthermore, `define-serializable-struct` provides limited access to field mutation, but only for instances generated through the deserialization information bound to `deserialize-info:`id`-v0`. See `make-deserialize-info` for more information.

The `-v0` suffix on the deserialization enables future versioning on the structure type through `define-serializable-struct/version`.

When `super-id` is supplied, compile-time information bound to `super-id` must include all of the supertype's field accessors. If any field mutator is missing, the structure type will be treated as immutable for the purposes of marshaling (so cycles involving only instances of the structure type cannot be handled by the deserializer).

Example:

    (define-serializable-struct point (x y))
    (deserialize (serialize (make-point 1 2))) ; => (make-point 1 2)

`(`define-serializable-struct/version` `id vers-num `(`field-id ``···`)` `((``other-vers-num` `make-proc-expr` `cycle-make-proc-expr``))` [inspector-expr]`)` SYNTAX

`(`define-serializable-struct/version` `(id super-id) vers-num `(`field-id ``···`)` `((``other-vers-num` `make-proc-expr` `cycle-make-proc-expr``))` [inspector-expr]`)` SYNTAX

Like `define-serializable-struct`, but the generated deserializer binding is `deserialize-info:`id`-`vers-num``. In addition, `deserialize-info:`id`-`other-vers-num`` is bound for each `other-vers-num`.

Each `make-proc-expr` should produce a procedure, and the procedure should accept as many argument as fields in the corresponding version of the structure type, and it produce an instance of `id`. Each `graph-make-proc-expr` should produce a procedure of no arguments; this procedure should return two values: an instance `x` of `id` (typically with `#f` for all fields) and a procedure that accepts another instance of `id` and copies its field values into `x`.

Example:

    (define-serializable-struct point (x y))
    (define ps (serialize (make-point 1 2)))
    (deserialize ps) ; => (make-point 1 2)

    (define x (make-point 1 10))
    (set-point-x! x x)
    (define xs (serialize x))
    (deserialize xs) ; => x0, where x0 is (make-point x0 10)

    (define-serializable-struct/versions point 1 (x y z)
                                         ([0
                                           ;; Constructor for simple v0 instances:
                                           (lambda (x y) (make-point x y 0))
                                           ;; Constructor for v0 instance in a cycle:
                                           (lambda ()
                                             (let ([p0 (make-point #f #f 0)])
                                              (values
                                                p0
                                                (lambda (p)
                                                  (set-point-x! p0 (point-x p))
                                                  (set-point-y! p0 (point-y p))))))]))
    (deserialize (serialize (make-point 4 5 6))) ; => (make-point 4 5 6)
    (deserialize ps) ; => (make-point 1 2 0)
    (deserialize xs) ; => x1, where x1 is (make-point x1 10 0)

`(serialize``` `v``)` PROCEDURE

Returns a value that encapsulates the value `v`. This value includes only readable values, so it can be written to a stream with `write`, later read from a stream using `read`, and then converted to a value like the original using `deserialize`. Serialization followed by deserialization produces a value with the same graph structure and mutability as the original value, but the serialized value is a plain tree (i.e., no sharing).

The following kinds of values are serializable:

- structures created through `define-serializable-struct` or `define-serializable-struct/version`, or more generally structures with the `prop:serializable` property (see `prop:serializable` for more information);

- instances of classes defined with `define-serializable-class` or `define-serializable-class` (see section 4.7);

- booleans, numbers, characters, symbols, strings, byte strings, paths, void, and the empty list;

- pairs, vectors, boxes, and hash tables; and

- `date` and `arity-at-least` structures.

Of course, serialization succeeds for a compound value, such as a pair, only if all content of the value is serializable. If a value given to `serialize` is not completely serializable, the `exn:fail:contract` exception is raised.

See `deserialize` for information on the format of serialized data.

`(deserialize``` `v``)` PROCEDURE

Given a value `v` that was produced by `serialize`, produces a value like the one given to `serialize`, including the same graph structure and mutability.

A serialized representation `v` is a list of six elements:

- A non-negative exact integer `s-count` that represents the number of distinct structure types represented in the serialized data.

- A list `s-types` of length `s-count`, where each element represents a structure types. Each structure type is encoded as a pair. The ``car`` of the pair is `#f` for a structure whose deserialization information is defined at the top level, otherwise it is a quoted module path for a module that exports the structure's deserialization information. The ``cdr`` of the pair is the name of a binding (at the top level or exported from a module) for deserialization information. These two are used with either `namespace-variable-binding` or `dynamic-require` to obtain deserialization information. See `make-deserialization-info` for more information on the binding's value.

- A non-negative exact integer, `g-count` that represents the number of graph points contained in the following list.

- A list `graph` of length `g-count`, where each element represents a serialized value to be referenced during the construction of other serialized values. Each list element is either a box or not:

  - A box represents a value that is part of a cycle, and for deserialization, it must be allocated with `#f` for each of its fields. The content of the box indicates the shape of the value:

    - a non-negative exact integer `i` for an instance of a structure type that is represented by the `i`th element of the `s-types` list;

    - `'c` for a pair;

    - `'b` for a box;

    - a pair whose ``car`` is `'v` and whose ``cdr`` is a non-negative exact integer `s` for a vector of length `s`; or

    - a list whose first element is `'h` and whose remaining elements are flags for `make-hash-table` for a hash table.

    - `'date` for a `date` structure;

    - `'arity-at-least` for an `arity-at-least` structure;

    The `#f`-filled value will be updated with content specified by the fifth element of the serialization list `v`.

  - A non-box represents a serial value to be constructed immediately, and it is one of the following:

    - a boolean, number, character, symbol, or empty list, representing itself.

    - a string, representing an immutable string.

    - a byte string, representing an immutable byte string.

    - a pair whose ``car`` is `'?` and whose ``cdr`` is a non-negative exact integer `i`; it represents the value constructed for the `i`th element of `graph`, where `i` is less than the position of this element within `graph`.

    - a pair whose ``car`` is a number `i`; it represents an instance of a structure type that is described by the `i`th element of the `s-types` list. The ``cdr`` of the pair is a list of serials representing arguments to be provided to the structure type's deserializer.

    - a pair whose ``car`` is `'void`, representing void.

    - a pair whose ``car`` is `'u` and whose ``cdr`` is either a byte string or character string; it represents a mutable byte or character string.

    - a pair whose ``car`` is `'c` and whose ``cdr`` is a pair of serials; it represents an immutable pair.

    - a pair whose ``car`` is `'c!` and whose ``cdr`` is a pair of serials; it represents a mutable pair.

    - a pair whose ``car`` is `'v` and whose ``cdr`` is a list of serials; it represents an immutable vector.

    - a pair whose ``car`` is `'v!` and whose ``cdr`` is a list of serials; it represents a mutable vector.

    - a pair whose ``car`` is `'b` and whose ``cdr`` is a serial; it represents an immutable box.

    - a pair whose ``car`` is `'b!` and whose ``cdr`` is a serial; it represents a mutable box.

    - a pair whose ``car`` is `'h`, whose ``cadr`` is either `'!` or `'-` (mutable or immutable, respectively), whose ``caddr`` is a list of symbols to be used as flags for `make-hash-table`, and whose ``cdddr`` is a list of pairs, where the ``car`` of each pair is a serial for a hash-table key and the ``cdr`` is a serial for the corresponding value.

    - a pair whose ``car`` is `'date` and whose ``cdr`` is a list of serials; it represents a `date` structure.

    - a pair whose ``car`` is `'arity-at-least` and whose ``cdr`` is a serial; it represents an `arity-at-least` structure.

- A list of pairs, where the ``car`` of each pair is a non-negative exact integer `i` and the ``cdr`` is a serial (as defined in the previous bullet). Each element represents an update to an `i`th element of `graphs` that was specified as a box, and the serial describes how to construct a new value with the same shape as specified by the box. The content of this new value must be transferred into the value created for the box in `graph`.

- A final serial (as defined in the two bullets back) representing the result of `deserialize`.

The result of `deserialize` shares no mutable values with the argument to `deserialize`.

If a value provided to `serialize` is a simple tree (i.e., no sharing), then the fourth and fifth elements in the serialized representation will be empty.

`(make-deserialize-info``` `make-proc cycle-make-proc``)` PROCEDURE

Produces a deserialization information record to be used by `deserialize`. This information is normally tied to a particular structure because the structure has a `prop:serializable` property value that points to a top-level variable or module-exported variable that is bound to deserialization information.

The `make-proc` procedure should accept as many argument as the structure's serializer put into a vector; normally, this is the number of fields in the structure. It should return an instance of the structure.

The `cycle-make-proc` procedure should accept no arguments, and it should return two values: a structure instance `x` (with dummy field values) and an update procedure. The update procedure takes another structure instance generated by the `make-proc`, and it transfers the field values of this instance into `x`.

`prop:serializeable` PROPERTY

This property identifies structures and structure types that are serializable. The property value should be constructed with `make-serialize-info`.

`(make-serialize-info``` `to-vector-proc deserialize-id can-cycle? dir-path``)` PROCEDURE

Produces a value to be associated with a structure type through the `prop:serializable` property. This value is used by `serialize`.

The `to-vector-proc` procedure should accept a structure instance and produce a vector for the instance's content.

The `deserialize-id` value indicates a binding for deserialize information, to either a module export or a top-level definition. The `deserialize-id` value can be an identifier syntax object, a symbol, or a pair:

- If `deserialize-id` is an identifier, and if `(`identifier-binding` deserialize-id)` produces a list, then the third element is used for the exporting module, otherwise the top-level is assumed. In either case, ``syntax-e`` is used to obtain the name of an exported identifier or top-level definition.

- If `deserialize-id` is a symbol, it indicates a top-level variable that is named by the symbol.

- If `deserialize-id` is a pair, the ``car`` must be a symbol to name an exported identifier, and the ``cdr`` must be either a symbol or a module path index to specify the exporting module.

See `make-deserialize-info` and `deserialize` for more information.

The `can-cycle?` argument should be false if instances should not be serialized in such a way that deserialization requires creating a structure instance with dummy field values and then updating the instance later.

The `dir-path` argument should be a directory path that is used to resolve a module reference for the binding of `deserialize-id`. This directory path is used as a last resort when `deserialize-id` indicates a module that was loaded through a relative path with respect to the top level. Usually, it should be `(or (`current-load-relative-directory`) (`current-directory`))`.

`(serializable?``` `v``)` PROCEDURE

Returns `#t` if `v` appears to be serializable, without checking the content of compound values, and `#f` otherwise. See `serialize` for an enumeration of serializable values.