| Type | byte | array | struct | vector | table | option | union |
|---|---|---|---|---|---|---|---|
| Size | Fixed | Fixed | Fixed | Dynamic | Dynamic | Dynamic | Dynamic |
| Type | Header | Body |
|--------+--------------------------------------------------+-----------------------------------|
| array | | item-0 | item-1 | ... | item-N |
| struct | | field-0 | field-1 | ... | field-N |
| fixvec | items-count | item-0 | item-1 | ... | item-N |
| dynvec | full-size | offset-0 | offset-1 | ... | offset-N | item-0 | item-1 | ... | item-N |
| table | full-size | offset-0 | offset-1 | ... | offset-N | filed-0 | field-1 | ... | field-N |
| option | | item or none (zero bytes) |
| union | item-type-id | item |
- All items in Header are 32 bit unsigned integers in little-endian.
The byte is a byte.
00 is a byte.
The array is a fixed-size type: it has a fixed-size inner type and a fixed length.
The size of an array is the size of inner type times the length.
Serializing an array only need to serialize all items in it.
There's no overhead to serialize an array, which stores all items consecutively, without extra space between two adjacent items.
If we define array Byte3 [byte; 3];, and we want to store three bytes: first is 01, the second is 02 and the last is 03, then the serialized bytes will be 01 02 03.
If we define array Uint32 [byte; 4]; , and we want to store a 32 bit unsigned integer 0x01020304 into it in little-endian, then the serialized bytes will be 04 03 02 01.
If we define array TwoUint32 [Uint32; 2];, and we want to store two 32 bit unsigned integers in little-endian: first is 0x01020304 and second is 0xabcde, then the serialized bytes will be 04 03 02 01 de bc 0a 00.
The struct is a fixed-size type: all fields in struct are fixed-size and it has a fixed quantity of fields.
The size of a struct is the sum of all fields' size.
Serializing a struct only need to serialize all fields in it.
Fields in a struct are stored in the order they are declared.
There's no overhead to serialize a struct, which stores all fields consecutively, without extra space between two adjacent items.
If we define struct OnlyAByte { f1: byte }, and we want to store a byte ab, then the serialized bytes will be ab.
If we define struct ByteAndUint32 { f1: byte, f2: Uint32 }, and we want to store a byte ab and a 32 bit unsigned integer 0x010203 in little-endian, then the serialized bytes will be ab 03 02 01 00.
There two kinds of vectors: fixed vector fixvec and dynamic vector dynvec.
Whether a vector is fixed or dynamic depends on the type of its inner item: if the inner item is fixed-size, then it's a fixvec; otherwise, it's a dynvec.
Both of fixvec and dynvec are dynamic-size types.
There are two steps of serializing a fixvec:
- Serialize the length as a 32 bit unsigned integer in little-endian.
- Serialize all items in it.
If we define vector Bytes <byte>;:
- the serialized bytes of an empty bytes is
00 00 00 00(the length of any empty fixed vector is0). - the serialized bytes of
0x12is01 00 00 00, 12. - the serialized bytes of
0x1234567890abcdefis08 00 00 00, 12 34 56 78 90 ab cd ef.
If we define vector Uint32Vec <Uint32>;:
- the serialized bytes of an empty
Uint32Vecis00 00 00 00. - the serialized bytes of
0x123is01 00 00 00, 23 01 00 00. - the serialized bytes of
[0x123, 0x456, 0x7890, 0xa, 0xbc, 0xdef]is# there are 6 items 06 00 00 00 # six items 23 01 00 00, 56 04 00 00, 90 78 00 00, 0a 00 00 00, bc 00 00 00, ef 0d 00 00
There are three steps of serializing a dynvec:
- Serialize the full size in bytes as a 32 bit unsigned integer in little-endian.
- Serialize all offset of items as 32 bit unsigned integer in little-endian.
- Serialize all items in it.
If we define vector BytesVec <Bytes>;:
- the serialized bytes of an empty
BytesVecis04 00 00 00(the full size of an empty dynamic vector is 4 bytes). - the serialized bytes of
[0x1234]is# the full size is 14 bytes 0e 00 00 00 # one offset 08 00 00 00 # one item 02 00 00 00 12 34 - the serialized bytes of
[0x1234, 0x, 0x567, 0x89, 0xabcdef]is# the full size is 52 (0x34) bytes 34 00 00 00 # five offsets (20 bytes in total) 18 00 00 00, 1e 00 00 00, 22 00 00 00, 28 00 00 00, 2d 00 00 00 # five items (28 bytes in total) 02 00 00 00, 12 34 00 00 00 00, 02 00 00 00, 05 67 01 00 00 00, 89 03 00 00 00, ab cd ef
The table is a dynamic-size type. It can be considered as a dynvec but the length is fixed.
The serializing steps are same as dynvec:
- Serialize the full size in bytes as a 32 bit unsigned integer in little-endian.
- Serialize all offset of fields as 32 bit unsigned integer in little-endian.
- Serialize all fields in it in the order they are declared.
If we define table MixedType { f1: Bytes, f2: byte, f3: Uint32, f4: Byte3, f5: Bytes }
- the serialized bytes of a
MixedType { f1: 0x, f2: 0xab, f3: 0x123, f4: 0x456789, f5: 0xabcdef }is# the full size is 43 (0x2b) bytes 2b 00 00 00 # five offsets (20 bytes in total) 18 00 00 00, 1c 00 00 00, 1d 00 00 00, 21 00 00 00, 24 00 00 00 # five items (19 bytes in total) 00 00 00 00 ab 23 01 00 00 45 67 89 03 00 00 00, ab cd ef
The option is a dynamic-size type.
Serializing an option depends on whether it is empty or not:
- if it's empty, there is zero bytes (the size is
0). - if it's not empty, just serialize the inner item (the size is same as the inner item's size).
If we define option BytesVecOpt (BytesVec);
- the serialized bytes of
Noneis(empty). - the serialized bytes of
Some([])is04 00 00 00. - the serialized bytes of
Some([0x])is# the full size of BytesVec is 12 bytes 0c 00 00 00 # the offset of Bytes 08 00 00 00 # the length of Bytes 00 00 00 00
The union is a dynamic-size type.
Serializing a union has two steps:
- Serialize a item type id in bytes as a 32 bit unsigned integer in little-endian. The item type id is the index of the inner items, and it's starting at 0.
- Serialize the inner item.
If we define union HybridBytes { Byte3, Bytes, BytesVec, BytesVecOpt }
- the serialized bytes of
Byte3 (0x123456)is00 00 00 00, 12 34 56 - the serialized bytes of
Bytes (0x)is01 00 00 00, 00 00 00 00 - the serialized bytes of
Bytes (0x123)is01 00 00 00, 02 00 00 00, 01 23 - the serialized bytes of
BytesVec ([])is02 00 00 00, 04 00 00 00 - the serialized bytes of
BytesVec ([0x])is02 00 00 00, 0c 00 00 00, 08 00 00 00, 00 00 00 00 - the serialized bytes of
BytesVec ([0x123])is02 00 00 00, 0e 00 00 00, 08 00 00 00, 02 00 00 00, 01 23 - the serialized bytes of
BytesVec ([0x123, 0x456])is# Item Type Id 02 00 00 00 # the full size of BytesVec is 24 bytes 18 00 00 00 # two offsets of BytesVec (8 bytes in total) 0c 00 00 00, 12 00 00 00, # two Bytes (12 bytes in total) 02 00 00 00, 01 23 02 00 00 00, 04 56 - the serialized bytes of
BytesVecOpt (None)is03 00 00 00 - the serialized bytes of
BytesVecOpt (Some(([])))is03 00 00 00, 04 00 00 00 - the serialized bytes of
BytesVecOpt (Some(([0x])))is03 00 00 00, 0c 00 00 00, 08 00 00 00, 00 00 00 00 - the serialized bytes of
BytesVecOpt (Some(([0x123])))is03 00 00 00, 0e 00 00 00, 08 00 00 00, 02 00 00 00, 01 23 - the serialized bytes of
BytesVecOpt (Some(([0x123, 0x456])))is# Item Type Id 03 00 00 00 # the full size of BytesVec is 24 bytes 18 00 00 00 # two offsets of BytesVec (8 bytes in total) 0c 00 00 00, 12 00 00 00, # two Bytes (12 bytes in total) 02 00 00 00, 01 23 02 00 00 00, 04 56