The serialization framework
To improve performance we cache request to Global DCC and their replies. To
store them we need to serialize them, this is largely handled by
GlobalDcc.Native.Serialization in the DCC repository. The actual serialization
is done by GlobalDcc.Native.Serialization.DccWriter
.
How are objects serialized
Serializing an object is done by converting it to a set of bytes. This is done in the following way.
Serializing a primitive type
When serializing a primitive type (bool
, byte
, sbyte
, short
, ushort
,
int
, uint
, long
, ulong
, IntPtr
, UIntPtr
, char
, double
, Single
)
we copy the bytes it consists of. These are hopefully always
little endian (we will have serious
problems if we change between little and big endian).
Example: In hex the int 305,419,896 is 0x12345678. When serialized it becomes { 0x78, 0x56, 0x34, 0x12 } or if displayed in SQL Server Management Studio 0x78564312, because little endian means the bytes get switched.
Serializing a non-primitive type
Strings get serialized by using the default encoding.
Other complex types gets serialized by serializing each property and concatenate
them (the order is usually given by
DccModel.Internal.Serialization.DccDataAttribute
) to one byte array. We then
take the length of this array and concatenate the serialized length with the
byte array.
Deserializing a non-primitive type
When deserializing an object it is normally necessary to know the type of the
object, since the type is not a part of the serialized object. However we store
the TraceReply
and DistanceReply
objects in much the same way, so these we
deserialize without knowing if it is one or the other, and when deserialized we
check if the object contains trace data (in which case it is of type
TraceReply
).
Example - Getting a trace from cache
Say GlobalDcc receives a TraceRequest
from (48.96567, 4.345064) to (49.939624,
2.976233), with some circumstances and a roadnet. It looks the circumstance and
roadnet up in the cache and sees they correspond to the id 105, and it uses the
default road net version, since that is the version in use for that road net.
- First GlobalDcc figures out what
KeyStorage.Data
is for this request. To do this it converts the degrees to microdegrees, converts the corresponding int to bytes, and concatenates them (the first latitude gets converted this wayDecimal
48.96567 ->int
48,965,670 -> { 0x26, 0x28, 0xeb, 0x02 }), then it converts the circumstance id to bytes and append them to the end. The result is: { 0x26, 0x28, 0xeb, 0x02, 0xe8, 0x4c, 0x42, 0x00, 0xa8, 0x04, 0xfa, 0x02, 0xe9, 0x69, 0x2d, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }. - To get the corresponding Id for the cache GlobalDcc base 64 encodes the bytes found in the previous step, it then concatenates the road net version ("default"), a vertical dash, and the base 64 encoded string. The result is "default|JijrAuhMQgCoBPoC6WktAGkAAAAAAAAA".
- GlobalDcc tries to look up an entry in the cache for the above Id, and finds it has value: "0x0C00000000000000000000000000000010000000ECB00200B9250000BED21400E100000000000000".
- To read the cached entry GlobalDcc reads the first 4 bytes ({ 0x0c, 0x00,
0x00, 0x00 }) and converts them to the int 0xc = 12. This means that it
should read 12 bytes to get the first property of a
CacheEntry
which isAttributes
(the property order is not defined byDccDataAttribute
here, but is explicitly done in the deserialization). These 12 bytes are all 0, so the attributes are zeroed out. - GlobalDcc then reads the next 4 bytes to get the length of the next data
block ({ 0x10, 0x00, 0x00, 0x00 } -> 0x10 = 16), and reads theese bytes
which corresponds to the 4 properties of
CacheAnswer
. The first propertyLength
of cache answer is the first 4 bytes ({ 0xec, 0xb0, 0x02, 0x00 } -> 0x2b0ec = 176,364), the second propertyDuration
is the next 4 ({ 0xb9, 0x25, 0x00, 0x00 } -> 0x25b9 = 9,657), etc. - GlobalDcc then reads the next 4 bytes to get the length of the next data
block { 0x00, 0x00, 0x00, 0x00 } -> 0. So the length of this block is 0
bytes, which is good since there is no more bytes. This means that the final
property of this
CacheEntry
contains no data. This property isTraceData
, and since there was no data here this is a distance reply not aTraceReply
. - Since this is not a
TraceReply
it is not usefull for the receivedTraceRequest
, so GlobalDcc has to do a trace calculation. The result of this calculation gets saved in the cache, and overwrites the existing one (it is not a problem that we delete theDistanceReply
, because theTraceReply
contains all the data from theDistanceReply
plus trace data).