Simple Example of Class Encoding
On this page:
Sample Class Definitions
We have separately discussed the primary components of the class encoding: slices, references, and type IDs. To make the preceding discussions more concrete, consider the following class definitions:
class Base { int baseInt; string baseString; } class Derived extends Base { bool derivedBool; string derivedString; double derivedDouble; }
Suppose the sender marshals two instances of Derived
(for example, as two in-parameters in the same request) with these member values:
First instance:
Member | Type | Value | Marshaled size (in bytes) |
---|---|---|---|
|
|
| 4 |
|
|
| 6 |
|
|
| 1 |
|
|
| 7 |
|
|
| 8 |
Second instance:
Member | Type | Value | Marshaled size (in bytes) |
---|---|---|---|
|
|
| 4 |
|
|
| 5 |
|
|
| 1 |
|
|
| 6 |
|
|
| 8 |
We describe how to marshal these instances using versions 1.0 and 1.1 of the encoding in separate sections below.
Class Encoding version 1.0
The sender arbitrarily assigns a non-zero identity to each instance. Typically, the sender will simply consecutively number the instances starting at 1
. For this example, assume that the two instances have the identities 1
and 2
. The marshaled representation for the two instances (assuming that they are marshaled immediately following each other) is shown below:
Marshaled value | Size in bytes | Type | Byte offset |
---|---|---|---|
| 4 |
| 0 |
| 1 |
| 4 |
| 10 |
| 5 |
| 4 |
| 15 |
| 1 |
| 19 |
| 7 |
| 20 |
| 8 |
| 27 |
| 1 |
| 35 |
| 7 |
| 36 |
| 4 |
| 43 |
| 4 |
| 47 |
| 6 |
| 51 |
| 1 |
| 57 |
| 14 |
| 58 |
| 4 |
| 72 |
| 1 |
| 76 |
| 4 |
| 77 |
| 1 |
| 81 |
| 1 |
| 82 |
| 4 |
| 83 |
| 1 |
| 87 |
| 6 |
| 88 |
| 8 |
| 94 |
| 1 |
| 102 |
| 1 |
| 103 |
| 4 |
| 104 |
| 4 |
| 108 |
| 5 |
| 112 |
| 1 |
| 117 |
| 1 |
| 118 |
| 4 |
| 119 |
| 1 |
| 123 |
Note that, because classes (like exceptions) are sent as a sequence of slices, the receiver of a class can slice off any derived parts of a class it does not understand. Also note that (as shown in the above table) each class instance contains three slices. The third slice is for the type ::Ice::Object
, which is the base type of all classes. The class type ID ::Ice::Object
has the number 3
in this example because it is the third distinct type ID that is marshaled by the sender. (See entries at byte offsets 58 and 118 in the above table.) All class instances have this final slice of type ::Ice::Object
.
Marshaling a separate slice for ::Ice::Object
dates back to Ice versions 1.3 and earlier. In those versions, classes carried a facet map that was marshaled as if it were defined as follows:
module Ice { class Object; dictionary<string, Object> FacetMap; class Object { FacetMap facets; // No longer exists } }
As of Ice version 1.4, this facet map is always empty, that is, the count of entries for the dictionary that is marshaled in the ::Ice::Object
slice is always zero. If a receiver receives a class instance with a non-empty facet map, it must throw a MarshalException
.
Note that if a class has no data members, a type ID and slice for that class is still marshaled. The byte count of the slice will be 4 in this case, indicating that the slice contains no data.
Class Encoding version 1.1
A leading size value of 1
marks the beginning of an instance, followed by one or more slices.
Class Encoding in the Sliced Format
The marshaled representation for the two instances (assuming that they are marshaled immediately following each other) in the sliced format is shown below:
Marshaled value | Size in bytes | Type | Byte offset |
---|---|---|---|
| 1 |
| 0 |
17 (slice flags: string type ID, size is present) | 1 |
| 1 |
| 10 |
| 2 |
| 4 |
| 12 |
| 1 |
| 16 |
| 7 |
| 17 |
| 8 |
| 24 |
49 (slice flags: string type ID, size is present, last slice) | 1 |
| 32 |
| 7 |
| 33 |
| 4 |
| 40 |
| 4 |
| 44 |
| 6 |
| 48 |
| 1 |
| 54 |
18 (slice flags: index type ID, size is present) | 1 |
| 55 |
| 1 |
| 56 |
| 4 |
| 57 |
| 1 |
| 61 |
| 6 |
| 62 |
| 8 |
| 68 |
50 (slice flags: index type ID, size is present, last slice) | 1 |
| 76 |
| 1 |
| 77 |
| 4 |
| 78 |
| 4 |
| 82 |
| 5 |
| 86 |
The sliced format allows the receiver of a class to slice off any derived parts of a class it does not understand, as in version 1.0 of the encoding. Although the sliced format provides equivalent functionality to that of version 1.0, it is significantly more efficient, requiring only 91 bytes to encode our example compared to the 124 bytes required by version 1.0. We could reduce the encoded size even further, while still retaining the ability to slice off unknown types, by using compact type IDs.
Note that if a class has no data members, a type ID and slice for that class is still marshaled. The byte count of the slice will be 4 in this case, indicating that the slice contains no data.
Class Encoding in the Compact Format
The marshaled representation for the two instances (assuming that they are marshaled immediately following each other) in the compact format is shown below:
Marshaled value | Size in bytes | Type | Byte offset |
---|---|---|---|
| 1 |
| 0 |
| 1 |
| 1 |
| 10 |
| 2 |
| 1 |
| 12 |
| 7 |
| 13 |
| 8 |
| 20 |
| 1 |
| 28 |
| 4 |
| 29 |
| 6 |
| 33 |
| 1 |
| 39 |
| 1 |
| 40 |
| 1 |
| 41 |
| 1 |
| 42 |
| 6 |
| 43 |
| 8 |
| 49 |
| 1 |
| 57 |
| 4 |
| 58 |
| 5 |
| 62 |
In an effort to conserve bandwidth, the compact format omits certain details that would allow a receiver to slice off derived parts of a class, such as the slice size and the type IDs for base classes. The result is an encoding that requires only 67 bytes for the two sample instances.
Note that if a class has no data members, a type ID and slice for that class is still marshaled. The byte count of the slice will be 4 in this case, indicating that the slice contains no data.
Class Encoding in the Compact Format with Compact Type IDs
Compact type IDs can be used regardless of the sender's chosen format. For the sake of example, we will use compact type IDs together with the compact format to produce the smallest encoding possible. The Slice definitions below reflect the addition of the compact type IDs:
class Base(10) { int baseInt; string baseString; } class Derived(11) extends Base { bool derivedBool; string derivedString; double derivedDouble; }
We assign the compact type ID 10
to Base
and 11
to Derived
. Note however that assigning a compact type ID to Base
does not affect the size of the encoded data in our example because the compact format omits type IDs altogether for base types.
The marshaled representation for the two instances (assuming that they are marshaled immediately following each other) in the compact format is shown below:
Marshaled value | Size in bytes | Type | Byte offset |
---|---|---|---|
| 1 |
| 0 |
| 1 |
| 1 |
| 1 |
| 2 |
| 1 |
| 3 |
| 7 |
| 4 |
| 8 |
| 11 |
| 1 |
| 19 |
| 4 |
| 20 |
| 6 |
| 24 |
| 1 |
| 30 |
| 1 |
| 31 |
| 1 |
| 32 |
| 1 |
| 33 |
| 6 |
| 34 |
| 8 |
| 40 |
| 1 |
| 48 |
| 4 |
| 49 |
| 5 |
| 53 |
Substituting a compact type ID for its string equivalent reduces the encoded size for the two instances by another nine bytes to 58, less than half the size of version 1.0.