Data Encoding for Exceptions
10 min read
10 min read
10 min read
10 min read
10 min read
10 min read
10 min read
10 min read
10 min read
Exception Encoding Version 1.0
An exception is marshaled as shown below:
Marshaling format for exceptions.
Every exception instance is preceded by a single byte that indicates whether the exception uses class fields: the byte value is 1 if any of the exception fields are classes (or if any of the exception fields, recursively, contain class fields) and 0, otherwise.
Following the header byte, the exception is marshaled as a sequence of pairs: the first field of each pair is the type ID for an exception slice, and the second field of the pair is a slice containing the marshaled fields of that slice. The sequence of pairs is marshaled in derived-to-base order, with the most-derived slice first, and ending with the least-derived slice. Within each slice, fields are marshaled as for structures: in the order in which they are defined in the Slice definition.
Following the sequence of pairs, any class instances that are used by the fields of the exception are marshaled. This final part is optional: it is present only if the header byte is 1.
To illustrate the marshaling, consider the following exception hierarchy:
exception Base{ int baseInt; string baseString;}
exception Derived extends Base{ bool derivedBool; string derivedString; double derivedDouble;}Assume that the exception fields are initialized to the values shown below:
| Field | Type | Value | Marshaled size (in bytes) |
|---|---|---|---|
baseInt | int | 99 | 4 |
baseString | string | "Hello" | 6 |
derivedBool | bool | true | 1 |
derivedString | string | "World!" | 7 |
derivedDouble | double | 3.14 | 8 |
Field values of an exception of typeDerived.
From the above table, we can see that the total size of the fields of Base is 10 bytes, and the total size of the fields of Derived is 16 bytes. None of the exception fields are classes. An instance of this exception has the on-the-wire representation shown in the next table. (The size, type, and byte offset of the marshaled representation is indicated for each component.)
| Marshaled value | Size in bytes | Type | Byte offset |
|---|---|---|---|
0 (no class fields) | 1 | bool | 0 |
"::Derived" (type ID) | 10 | string | 1 |
20 (byte count for slice) | 4 | int | 11 |
1 (derivedBool) | 1 | bool | 15 |
"World!" (derivedString) | 7 | string | 16 |
3.14 (derivedDouble) | 8 | double | 23 |
"::Base" (type ID) | 7 | string | 31 |
14 (byte count for slice) | 4 | int | 38 |
99 (baseInt) | 4 | int | 42 |
"Hello" (baseString) | 6 | string | 46 |
Marshaled representation of the exception.
Note that the size of each string is one larger than the actual string length. This is because each string is preceded by a count of its number of bytes, as directed by the encoding for strings.
If an exception contains class fields, these fields are marshaled following the exception slices as described in the class encoding.
Exception Encoding Version 1.1
An exception is marshaled as a collection of slices whose order matches the inheritance hierarchy, with the most-derived type appearing first. The selected encoding format affects the content of each slice. The final slice, representing the least-derived type, has its last slice bit set to true.
An exception in the compact format is marshaled as follows:
Compact format for exceptions.
The leading byte of each slice is a set of bit flags that specifies the features of the slice. Each slice includes its type ID, encoded as a string.
The sliced format adds a slice size to every slice, along with an optional indirection table:
Sliced format for exceptions.
To illustrate the marshaling, consider the following exception hierarchy:
exception Base{ int baseInt; string baseString;}
exception Derived extends Base{ bool derivedBool; string derivedString; double derivedDouble;}Assume that the exception fields are initialized to the values shown below:
| Field | Type | Value | Marshaled size (in bytes) |
|---|---|---|---|
baseInt | int | 99 | 4 |
baseString | string | "Hello" | 6 |
derivedBool | bool | true | 1 |
derivedString | string | "World!" | 7 |
derivedDouble | double | 3.14 | 8 |
Field values of an exception of typeDerived.
From the above table, we can see that the total size of the fields of Base is 10 bytes, and the total size of the fields of Derived is 16 bytes. None of the exception fields are classes. An instance of this exception using the sliced format has the on-the-wire representation shown in the next table. (The size, type, and byte offset of the marshaled representation is indicated for each component.)
| Marshaled value | Size in bytes | Type | Byte offset |
|---|---|---|---|
16 (flags: slice size is present) | 1 | byte | 0 |
"::Derived" (type ID) | 10 | string | 1 |
20 (byte count for slice) | 4 | int | 11 |
1 (derivedBool) | 1 | bool | 15 |
"World!" (derivedString) | 7 | string | 16 |
3.14 (derivedDouble) | 8 | double | 23 |
48 (flags: slice size is present, last slice) | 1 | byte | 31 |
"::Base" (type ID) | 7 | string | 32 |
14 (byte count for slice) | 4 | int | 39 |
99 (baseInt) | 4 | int | 43 |
"Hello" (baseString) | 6 | string | 47 |
Marshaled representation of the exception using the sliced format.
Note that the size of each string is one larger than the actual string length. This is because each string is preceded by a count of its number of bytes, as directed by the encoding for strings.
A receiver that does not know the type ID of the first slice uses the slice size to skip to the next slice, and repeats until it reaches a type ID its Slice loader knows: it then unmarshals the exception as this type. For example, a receiver that knows Base but not Derived reads the slice size 20, skips 20-4 bytes to the flags of the ::Base slice at byte offset 31, and unmarshals the exception as Base. When it reaches the last slice without finding a type ID it knows, it throws MarshalException.
Repeating this exercise using the compact format produces the following encoding:
| Marshaled value | Size in bytes | Type | Byte offset |
|---|---|---|---|
0 (flags: none) | 1 | byte | 0 |
"::Derived" (type ID) | 10 | string | 1 |
1 (derivedBool) | 1 | bool | 11 |
"World!" (derivedString) | 7 | string | 12 |
3.14 (derivedDouble) | 8 | double | 19 |
32 (flags: last slice) | 1 | byte | 27 |
"::Base" (type ID) | 7 | string | 28 |
99 (baseInt) | 4 | int | 35 |
"Hello" (baseString) | 6 | string | 39 |
Marshaled representation of the exception using the compact format.
With the compact format, a receiver decodes the exception only if it knows the most-derived type: without slice sizes, it cannot skip a slice it does not know how to decode, so it throws MarshalException.