Skip to main content

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 way Decimal 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 is Attributes (the property order is not defined by DccDataAttribute 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 property Length of cache answer is the first 4 bytes ({ 0xec, 0xb0, 0x02, 0x00 } -> 0x2b0ec = 176,364), the second property Duration 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 is TraceData, and since there was no data here this is a distance reply not a TraceReply.
  • Since this is not a TraceReply it is not usefull for the received TraceRequest, 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 the DistanceReply, because the TraceReply contains all the data from the DistanceReply plus trace data).