diff --git a/design/IDL.md b/design/IDL.md index 01a354ac65e..c620ddbb482 100644 --- a/design/IDL.md +++ b/design/IDL.md @@ -665,9 +665,9 @@ actor server = { ### Serialisation -At runtime, every IDL value is serialised into a pair (M, T), where M ("memory") is a sequence of bytes and T ("table") a sequence of references. If T is empty, it can be omitted. By using references, (1) the wire representation of reference values (which may be complex and involve system meta data such as types) need not be exposed to client code, and (2) the system knows where the references are in the serialised data, such that it can rewrite/map/filter/adjust them as it sees fit. +At runtime, every IDL value is serialised into a pair (M, R), where M ("memory") is a sequence of bytes and R ("references") a sequence of references. If R is empty, it can be omitted. By using references, (1) the wire representation of reference values (which may be complex and involve system meta data such as types) need not be exposed to client code, and (2) the system knows where the references are in the serialised data, such that it can rewrite/map/filter/adjust them as it sees fit. -Accordingly, serialisation is defined by two mapping functions, `M` and `T`, producing the respective parts. They are defined independently, but both definitions are indexed by IDL types. +Accordingly, serialisation is defined by two mapping functions, `M` and `R`, producing the respective parts. They are defined independently, but both definitions are indexed by IDL types. `M` maps an IDL value to a byte sequence described in terms of natural storage types (`i` for N = 8, 16, 32, 64`, `f` for `N = 32, 64`). @@ -696,8 +696,10 @@ M(v^N : vec ) = leb128(N) M(v : )^N M(kv^N : struct{^K}) = leb128(K') M(kv : ^K)^N where K' is the number of fields produced M((k,v) : variant{*}) = i32(k) M(v : ) iff k : in * -M((k,v) : ^*) = i32(k) leb128(|F(v : )|) F(v : ) iff k : in * and F(v : ) =/= . -M((k,v) : ^*) = . otherwise +M((k,v) : ^*) = i32(k) leb128(|F(v : )|) leb128(|RF(v : )|) F(v : ) + iff k : in * and (|F(v : )| + |RF(v : )|) > 0 +M((k,v) : ^*) = . + otherwise F(null : opt ) = . F(v : opt ) = M(v : ) @@ -705,6 +707,33 @@ F(v : ) = M(v : ) otherwise M(r : service ) = leb128(T(r)) M(r : func ) = leb128(T(r)) + + +R(n : nat) = . +R(_ : int) = . +R(_ : nat) = . +R(_ : int) = . +R(_ : float) = . +R(_ : bool) = . +R(_ : text) = . +R(_ : null) = . +R(_ : unavailable) = . + +R(null : opt ) = . +R(?v : opt ) = R(v : ) +R(v^N : vec ) = R(v : )^N +R(kv^N : struct{^K}) = R(kv : ^K)^N +R((k,v) : variant{*}) = R(v : ) iff k : in * + +R((k,v) : ^*) = R(v : ) iff k : in * and F(v : ) =/= . +R((k,v) : ^*) = . otherwise + +RF(null : opt ) = . +RF(v : opt ) = R(v : ) +RF(v : ) = R(v : ) otherwise + +M(r : service ) = r +M(r : func ) = r ``` Notes: @@ -713,8 +742,6 @@ Notes: * Every record field explicitly includes the size of its payload data. This is to allow skipping an unknown field upon deserialisation, see below. -* The M-representation of references is the respective index into T, denoted by `T(r)` above. A serialiser is allowed (but not required to) merge multiple occurrences of the same reference in T. - * It is unspecified how references *r* are represented, neither internally nor externally. When binding to Wasm, their internal representation is expected to be based on Wasm reference types, i.e., `anyref` or subtypes thereof. It is up to the system how to represent or translate the reference table on the wire. * Serialisation is a function, i.e., it deterministically produces a unique output. However, this output depends on the type, so two binary representations (or hashes thereof) are only comparable when the serialisation side type is known and was the same for both. @@ -722,13 +749,13 @@ Notes: ### Deserialisation -Deserialisation is the parallel application of the inverse functions of `M` and `T` defined above, with the following relaxation: +Deserialisation is the parallel application of the inverse functions of `M` and `R` defined above, with the following relaxation: * A record representation may include *additional* fields not occurring in the static type, or which serialisation would omit (`null`, `unavailable`); they are simply ignored, the deserialiser can skip over the body using the field size. ### Parameters -`P` defines the parameter mapping. Essentially, a parameter list is serialised into the pair (M,T) as if it was a single closed record, preceded by the string "DIDL" as a magic number: +`P` defines the parameter mapping. Essentially, a parameter list is serialised into the pair (M,R) as if it was a single closed record, preceded by the string "DIDL" as a magic number: ``` P(kv* : ,*) = i8('D') i8('I') i8('D') i8('L') M(kv* : ;*)