private static void ProcessExtraData(SerializationContext read, int fieldTag, WireType wireType, SerializationContext write)
        {
            int len;

            switch (wireType)
            {
            case WireType.Variant:
                len = read.ReadRawVariant();
                write.WriteBlock(read.Workspace, 0, len);
                break;

            case WireType.Fixed32:
                read.ReadBlock(4);
                write.WriteBlock(read.Workspace, 0, 4);
                break;

            case WireType.Fixed64:
                read.ReadBlock(8);
                write.WriteBlock(read.Workspace, 0, 8);
                break;

            case WireType.String:
                len = read.DecodeInt32();
                write.EncodeInt32(len);
                read.WriteTo(write, len);
                break;

            case WireType.StartGroup:
                read.StartGroup(fieldTag);
                uint prefix;
                while (read.TryReadFieldPrefix(out prefix))
                {
                    write.EncodeUInt32(prefix);
                    Serializer.ParseFieldToken(prefix, out wireType, out fieldTag);
                    if (wireType == WireType.EndGroup)
                    {
                        read.EndGroup(fieldTag);
                        break;
                    }
                    ProcessExtraData(read, fieldTag, wireType, write);
                }
                break;

            case WireType.EndGroup:
                throw new ProtoException("End-group not expected at this location");

            default:
                throw new ProtoException("Unknown wire-type " + wireType);
            }
        }
        internal int WriteLengthPrefixed <TValue>(TValue value, uint underEstimatedLength, ILengthProperty <TValue> property)
        {
            Flush(); // commit to the stream before monkeying with the buffers...

            MemoryStream ms = stream as MemoryStream;

            if (ms != null)
            {
                // we'll write to out current stream, optimising
                // for the case when the length-prefix is 1-byte;
                // if not we'll have to BlockCopy
                int  startIndex        = (int)ms.Position;
                uint guessLength       = underEstimatedLength,
                     guessPrefixLength = (uint)this.EncodeUInt32(guessLength),
                     actualLength      = (uint)property.Serialize(value, this);

                if (guessLength == actualLength)
                { // good guess! nothing to do...
                    return((int)(guessPrefixLength + actualLength));
                }

                uint actualPrefixLength = (uint)SerializationContext.GetLength(actualLength);

                Flush(); // commit to the stream before we start messing with it...

                if (actualPrefixLength < guessPrefixLength)
                {
                    throw new ProtoException("Internal error; the serializer over-estimated the length. Sorry, but this shouldn't have happened.");
                }
                else if (actualPrefixLength > guessPrefixLength)
                {
                    // our guess of the length turned out to be bad; we need to
                    // fix things...

                    // extend the buffer to ensure we have space
                    for (uint i = actualPrefixLength - guessPrefixLength; i > 0; i--)
                    {
                        ms.WriteByte(0);
                        position++;
                    }

                    // move the data
                    // (note; we MUST call GetBuffer *after* extending it,
                    // otherwise there the buffer might change if we extend
                    // over a boundary)
                    byte[] buffer = ms.GetBuffer();
                    Buffer.BlockCopy(buffer, (int)(startIndex + guessPrefixLength),
                                     buffer, (int)(startIndex + actualPrefixLength), (int)actualLength);
                }

                // back-fill the actual length into the buffer
                SerializationContext.EncodeUInt32(actualLength, ms.GetBuffer(), startIndex);
                return((int)(actualPrefixLength + actualLength));
            }
            else
            {
                // create a temporary stream and write the final result
                using (ms = new MemoryStream())
                {
                    SerializationContext ctx = new SerializationContext(ms, this);
                    int len = property.Serialize(value, ctx);
                    ctx.Flush();
                    this.ReadFrom(ctx);

                    int    preambleLen = this.EncodeInt32(len);
                    byte[] buffer      = ms.GetBuffer();
                    this.WriteBlock(buffer, 0, len);
                    return(preambleLen + len);
                }
            }
        }
Example #3
0
 protected int WritePrefix(SerializationContext context)
 {
     return(prefix == 0 ? 0 : context.EncodeUInt32(prefix));
 }
 protected int WritePrefix(SerializationContext context)
 {
     return prefix == 0 ? 0 : context.EncodeUInt32(prefix);
 }
        internal static void Deserialize <TCreation>(ref T instance, SerializationContext context)
            where TCreation : class, T
        {
            uint prefix = 0;

            if (context == null)
            {
                throw new ArgumentNullException("context");
            }
#if !CF
            try
            {
#endif
            if (instance != null)
            {
                Callback(CallbackType.BeforeDeserialization, instance);
            }

            context.Push();
            int propCount = readProps.Length;
            //context.CheckSpace();
            IExtensible extensible = instance as IExtensible;
            IExtension extn        = null;
            Property <T> prop      = propCount == 0 ? null : readProps[0];

            int lastIndex   = prop == null ? -1 : 0;
            uint lastPrefix = prop == null ? uint.MaxValue : prop.FieldPrefix;

            while (context.TryReadFieldPrefix(out prefix))
            {
                // scan for the correct property
                bool foundTag = false;
                if (prefix == lastPrefix)
                {
                    foundTag = true;
                }
                else if (prefix > lastPrefix)
                {
                    for (int i = lastIndex + 1; i < propCount; i++)
                    {
                        if (readProps[i].FieldPrefix == prefix)
                        {
                            prop       = readProps[i];
                            lastIndex  = i;
                            lastPrefix = prefix;
                            foundTag   = true;
                            break;
                        }
                        if (readProps[i].FieldPrefix > prefix)
                        {
                            break;                                        // too far
                        }
                    }
                }
                else
                {
                    for (int i = lastIndex - 1; i >= 0; i--)
                    {
                        if (readProps[i].FieldPrefix == prefix)
                        {
                            prop       = readProps[i];
                            lastIndex  = i;
                            lastPrefix = prefix;
                            foundTag   = true;
                            break;
                        }
                        if (readProps[i].FieldPrefix < prefix)
                        {
                            break;                                        // too far
                        }
                    }
                }

                if (!foundTag)
                {
                    // check for subclass creation
                    foreach (KeyValuePair <Type, Property <T, T> > subclass in subclasses)
                    {
                        // deserialize the nested data
                        if (prefix == subclass.Value.FieldPrefix)
                        {
                            foundTag = true;
                            instance = subclass.Value.DeserializeImpl(instance, context);
                            break;
                        }
                    }
                    if (foundTag)
                    {
                        continue;               // nothing more to do for this...
                    }
                }

                // not a sub-class, but *some* data there, so create an object
                if (instance == null)
                {
                    instance = ObjectFactory <TCreation> .Create();

                    Callback(CallbackType.ObjectCreation, instance);
                    extensible = instance as IExtensible;
                }
                if (foundTag)
                {
                    // found it by seeking; deserialize and continue

                    // ReSharper disable PossibleNullReferenceException
                    try
                    {
                        prop.Deserialize(instance, context);
                    }
                    catch (UnexpectedDataException ex)
                    {
                        if (extensible != null)
                        {
                            if (extn == null)
                            {
                                extn = extensible.GetExtensionObject(true);
                            }
                            ex.Serialize(extn);
                        }
                        // DON'T re-throw; we've handled this
                    }
                    // ReSharper restore PossibleNullReferenceException
                    continue;
                }

                WireType wireType;
                int      fieldTag;
                Serializer.ParseFieldToken(prefix, out wireType, out fieldTag);
                if (wireType == WireType.EndGroup)
                {
                    context.EndGroup(fieldTag);
                    break;     // this ends the entity, so stop the loop
                }

                // so we couldn't find it...
                if (extensible != null)
                {
                    if (extn == null)
                    {
                        extn = extensible.GetExtensionObject(true);
                    }
                    Stream extraStream = extn.BeginAppend();
                    try {
                        SerializationContext extraData = new SerializationContext(extraStream, null);

                        // copy the data into the output stream
                        // ReSharper disable PossibleNullReferenceException
                        extraData.EncodeUInt32(prefix);
                        // ReSharper restore PossibleNullReferenceException
                        ProcessExtraData(context, fieldTag, wireType, extraData);
                        extraData.Flush();
                        extn.EndAppend(extraStream, true);
                    } catch {
                        extn.EndAppend(extraStream, false);
                        throw;
                    }
                }
                else
                {
                    Debug.WriteLine("Dropping:" + fieldTag);
                    // unexpected fields for an inextensible object; discard the data
                    Serializer.SkipData(context, fieldTag, wireType);
                }
            }


            // final chance to create an instance - this only gets invoked for empty
            // messages (otherwise instance should already be non-null)
            if (instance == null)
            {
                instance = ObjectFactory <T> .Create();

                Callback(CallbackType.ObjectCreation, instance);
            }
            context.Pop();
            Callback(CallbackType.AfterDeserialization, instance);
#if !CF
        }

        catch (Exception ex)
        {
            const string ErrorDataKey = "protoSource";
            if (!ex.Data.Contains(ErrorDataKey))
            {
                ex.Data.Add(ErrorDataKey, string.Format("tag={0}; wire-type={1}; offset={2}; depth={3}; type={4}",
                                                        (int)(prefix >> 3), (WireType)(prefix & 7),
                                                        context.Position, context.Depth, typeof(T).FullName));
            }
            throw;
        }
#endif
        }