1004 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			1004 lines
		
	
	
		
			43 KiB
		
	
	
	
		
			C#
		
	
	
using System;
 | 
						|
using System.IO;
 | 
						|
using System.Text;
 | 
						|
using ProtoBuf.Meta;
 | 
						|
 | 
						|
namespace ProtoBuf
 | 
						|
{
 | 
						|
    /// <summary>
 | 
						|
    /// Represents an output stream for writing protobuf data.
 | 
						|
    /// 
 | 
						|
    /// Why is the API backwards (static methods with writer arguments)?
 | 
						|
    /// See: http://marcgravell.blogspot.com/2010/03/last-will-be-first-and-first-will-be.html
 | 
						|
    /// </summary>
 | 
						|
    public sealed class ProtoWriter : IDisposable
 | 
						|
    {
 | 
						|
        private Stream dest;
 | 
						|
        TypeModel model;
 | 
						|
        /// <summary>
 | 
						|
        /// Write an encapsulated sub-object, using the supplied unique key (reprasenting a type).
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="value">The object to write.</param>
 | 
						|
        /// <param name="key">The key that uniquely identifies the type within the model.</param>
 | 
						|
        /// <param name="writer">The destination.</param>
 | 
						|
        public static void WriteObject(object value, int key, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            if (writer.model == null)
 | 
						|
            {
 | 
						|
                throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
 | 
						|
            }
 | 
						|
 | 
						|
            SubItemToken token = StartSubItem(value, writer);
 | 
						|
            if (key >= 0)
 | 
						|
            {
 | 
						|
                writer.model.Serialize(key, value, writer);
 | 
						|
            }
 | 
						|
            else if (writer.model != null && writer.model.TrySerializeAuxiliaryType(writer, value.GetType(), DataFormat.Default, Serializer.ListItemTag, value, false, null))
 | 
						|
            {
 | 
						|
                // all ok
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                TypeModel.ThrowUnexpectedType(value.GetType());
 | 
						|
            }
 | 
						|
 | 
						|
            EndSubItem(token, writer);
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Write an encapsulated sub-object, using the supplied unique key (reprasenting a type) - but the
 | 
						|
        /// caller is asserting that this relationship is non-recursive; no recursion check will be
 | 
						|
        /// performed.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="value">The object to write.</param>
 | 
						|
        /// <param name="key">The key that uniquely identifies the type within the model.</param>
 | 
						|
        /// <param name="writer">The destination.</param>
 | 
						|
        public static void WriteRecursionSafeObject(object value, int key, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            if (writer.model == null)
 | 
						|
            {
 | 
						|
                throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
 | 
						|
            }
 | 
						|
            SubItemToken token = StartSubItem(null, writer);
 | 
						|
            writer.model.Serialize(key, value, writer);
 | 
						|
            EndSubItem(token, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        internal static void WriteObject(object value, int key, ProtoWriter writer, PrefixStyle style, int fieldNumber)
 | 
						|
        {
 | 
						|
            if (writer.model == null)
 | 
						|
            {
 | 
						|
                throw new InvalidOperationException("Cannot serialize sub-objects unless a model is provided");
 | 
						|
            }
 | 
						|
            if (writer.wireType != WireType.None) throw ProtoWriter.CreateException(writer);
 | 
						|
 | 
						|
            switch (style)
 | 
						|
            {
 | 
						|
                case PrefixStyle.Base128:
 | 
						|
                    writer.wireType = WireType.String;
 | 
						|
                    writer.fieldNumber = fieldNumber;
 | 
						|
                    if (fieldNumber > 0) WriteHeaderCore(fieldNumber, WireType.String, writer);
 | 
						|
                    break;
 | 
						|
                case PrefixStyle.Fixed32:
 | 
						|
                case PrefixStyle.Fixed32BigEndian:
 | 
						|
                    writer.fieldNumber = 0;
 | 
						|
                    writer.wireType = WireType.Fixed32;
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    throw new ArgumentOutOfRangeException("style");
 | 
						|
            }
 | 
						|
            SubItemToken token = StartSubItem(value, writer, true);
 | 
						|
            if (key < 0)
 | 
						|
            {
 | 
						|
                if (!writer.model.TrySerializeAuxiliaryType(writer, value.GetType(), DataFormat.Default, Serializer.ListItemTag, value, false, null))
 | 
						|
                {
 | 
						|
                    TypeModel.ThrowUnexpectedType(value.GetType());
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                writer.model.Serialize(key, value, writer);
 | 
						|
            }
 | 
						|
            EndSubItem(token, writer, style);
 | 
						|
        }
 | 
						|
 | 
						|
        internal int GetTypeKey(ref Type type)
 | 
						|
        {
 | 
						|
            return model.GetKey(ref type);
 | 
						|
        }
 | 
						|
 | 
						|
        private readonly NetObjectCache netCache = new NetObjectCache();
 | 
						|
        internal NetObjectCache NetCache => netCache;
 | 
						|
 | 
						|
        private int fieldNumber, flushLock;
 | 
						|
        WireType wireType;
 | 
						|
        internal WireType WireType { get { return wireType; } }
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a field-header, indicating the format of the next data we plan to write.
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteFieldHeader(int fieldNumber, WireType wireType, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            if (writer.wireType != WireType.None) throw new InvalidOperationException("Cannot write a " + wireType.ToString()
 | 
						|
                + " header until the " + writer.wireType.ToString() + " data has been written");
 | 
						|
            if (fieldNumber < 0) throw new ArgumentOutOfRangeException("fieldNumber");
 | 
						|
#if DEBUG
 | 
						|
            switch (wireType)
 | 
						|
            {   // validate requested header-type
 | 
						|
                case WireType.Fixed32:
 | 
						|
                case WireType.Fixed64:
 | 
						|
                case WireType.String:
 | 
						|
                case WireType.StartGroup:
 | 
						|
                case WireType.SignedVariant:
 | 
						|
                case WireType.Variant:
 | 
						|
                    break; // fine
 | 
						|
                case WireType.None:
 | 
						|
                case WireType.EndGroup:
 | 
						|
                default:
 | 
						|
                    throw new ArgumentException("Invalid wire-type: " + wireType.ToString(), "wireType");
 | 
						|
            }
 | 
						|
#endif
 | 
						|
            if (writer.packedFieldNumber == 0)
 | 
						|
            {
 | 
						|
                writer.fieldNumber = fieldNumber;
 | 
						|
                writer.wireType = wireType;
 | 
						|
                WriteHeaderCore(fieldNumber, wireType, writer);
 | 
						|
            }
 | 
						|
            else if (writer.packedFieldNumber == fieldNumber)
 | 
						|
            { // we'll set things up, but note we *don't* actually write the header here
 | 
						|
                switch (wireType)
 | 
						|
                {
 | 
						|
                    case WireType.Fixed32:
 | 
						|
                    case WireType.Fixed64:
 | 
						|
                    case WireType.Variant:
 | 
						|
                    case WireType.SignedVariant:
 | 
						|
                        break; // fine
 | 
						|
                    default:
 | 
						|
                        throw new InvalidOperationException("Wire-type cannot be encoded as packed: " + wireType.ToString());
 | 
						|
                }
 | 
						|
                writer.fieldNumber = fieldNumber;
 | 
						|
                writer.wireType = wireType;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                throw new InvalidOperationException("Field mismatch during packed encoding; expected " + writer.packedFieldNumber.ToString() + " but received " + fieldNumber.ToString());
 | 
						|
            }
 | 
						|
        }
 | 
						|
        internal static void WriteHeaderCore(int fieldNumber, WireType wireType, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            uint header = (((uint)fieldNumber) << 3)
 | 
						|
                | (((uint)wireType) & 7);
 | 
						|
            WriteUInt32Variant(header, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a byte-array to the stream; supported wire-types: String
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteBytes(byte[] data, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (data == null) throw new ArgumentNullException(nameof(data));
 | 
						|
            ProtoWriter.WriteBytes(data, 0, data.Length, writer);
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a byte-array to the stream; supported wire-types: String
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteBytes(byte[] data, int offset, int length, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (data == null) throw new ArgumentNullException(nameof(data));
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    if (length != 4) throw new ArgumentException(nameof(length));
 | 
						|
                    goto CopyFixedLength;  // ugly but effective
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    if (length != 8) throw new ArgumentException(nameof(length));
 | 
						|
                    goto CopyFixedLength;  // ugly but effective
 | 
						|
                case WireType.String:
 | 
						|
                    WriteUInt32Variant((uint)length, writer);
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    if (length == 0) return;
 | 
						|
                    if (writer.flushLock != 0 || length <= writer.ioBuffer.Length) // write to the buffer
 | 
						|
                    {
 | 
						|
                        goto CopyFixedLength; // ugly but effective
 | 
						|
                    }
 | 
						|
                    // writing data that is bigger than the buffer (and the buffer
 | 
						|
                    // isn't currently locked due to a sub-object needing the size backfilled)
 | 
						|
                    Flush(writer); // commit any existing data from the buffer
 | 
						|
                    // now just write directly to the underlying stream
 | 
						|
                    writer.dest.Write(data, offset, length);
 | 
						|
                    writer.position64 += length; // since we've flushed offset etc is 0, and remains
 | 
						|
                                                 // zero since we're writing directly to the stream
 | 
						|
                    return;
 | 
						|
            }
 | 
						|
            throw CreateException(writer);
 | 
						|
            CopyFixedLength: // no point duplicating this lots of times, and don't really want another stackframe
 | 
						|
            DemandSpace(length, writer);
 | 
						|
            Buffer.BlockCopy(data, offset, writer.ioBuffer, writer.ioIndex, length);
 | 
						|
            IncrementedAndReset(length, writer);
 | 
						|
        }
 | 
						|
        private static void CopyRawFromStream(Stream source, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            byte[] buffer = writer.ioBuffer;
 | 
						|
            int space = buffer.Length - writer.ioIndex, bytesRead = 1; // 1 here to spoof case where already full
 | 
						|
 | 
						|
            // try filling the buffer first   
 | 
						|
            while (space > 0 && (bytesRead = source.Read(buffer, writer.ioIndex, space)) > 0)
 | 
						|
            {
 | 
						|
                writer.ioIndex += bytesRead;
 | 
						|
                writer.position64 += bytesRead;
 | 
						|
                space -= bytesRead;
 | 
						|
            }
 | 
						|
            if (bytesRead <= 0) return; // all done using just the buffer; stream exhausted
 | 
						|
 | 
						|
            // at this point the stream still has data, but buffer is full; 
 | 
						|
            if (writer.flushLock == 0)
 | 
						|
            {
 | 
						|
                // flush the buffer and write to the underlying stream instead
 | 
						|
                Flush(writer);
 | 
						|
                while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
 | 
						|
                {
 | 
						|
                    writer.dest.Write(buffer, 0, bytesRead);
 | 
						|
                    writer.position64 += bytesRead;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                do
 | 
						|
                {
 | 
						|
                    // need more space; resize (double) as necessary,
 | 
						|
                    // requesting a reasonable minimum chunk each time
 | 
						|
                    // (128 is the minimum; there may actually be much
 | 
						|
                    // more space than this in the buffer)
 | 
						|
                    DemandSpace(128, writer);
 | 
						|
                    if ((bytesRead = source.Read(writer.ioBuffer, writer.ioIndex,
 | 
						|
                        writer.ioBuffer.Length - writer.ioIndex)) <= 0) break;
 | 
						|
                    writer.position64 += bytesRead;
 | 
						|
                    writer.ioIndex += bytesRead;
 | 
						|
                } while (true);
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        private static void IncrementedAndReset(int length, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            Helpers.DebugAssert(length >= 0);
 | 
						|
            writer.ioIndex += length;
 | 
						|
            writer.position64 += length;
 | 
						|
            writer.wireType = WireType.None;
 | 
						|
        }
 | 
						|
        int depth = 0;
 | 
						|
        const int RecursionCheckDepth = 25;
 | 
						|
        /// <summary>
 | 
						|
        /// Indicates the start of a nested record.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="instance">The instance to write.</param>
 | 
						|
        /// <param name="writer">The destination.</param>
 | 
						|
        /// <returns>A token representing the state of the stream; this token is given to EndSubItem.</returns>
 | 
						|
        public static SubItemToken StartSubItem(object instance, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            return StartSubItem(instance, writer, false);
 | 
						|
        }
 | 
						|
 | 
						|
        MutableList recursionStack;
 | 
						|
        private void CheckRecursionStackAndPush(object instance)
 | 
						|
        {
 | 
						|
            int hitLevel;
 | 
						|
            if (recursionStack == null) { recursionStack = new MutableList(); }
 | 
						|
            else if (instance != null && (hitLevel = recursionStack.IndexOfReference(instance)) >= 0)
 | 
						|
            {
 | 
						|
#if DEBUG
 | 
						|
                Helpers.DebugWriteLine("Stack:");
 | 
						|
                foreach (object obj in recursionStack)
 | 
						|
                {
 | 
						|
                    Helpers.DebugWriteLine(obj == null ? "<null>" : obj.ToString());
 | 
						|
                }
 | 
						|
                Helpers.DebugWriteLine(instance == null ? "<null>" : instance.ToString());
 | 
						|
#endif
 | 
						|
                throw new ProtoException("Possible recursion detected (offset: " + (recursionStack.Count - hitLevel).ToString() + " level(s)): " + instance.ToString());
 | 
						|
            }
 | 
						|
            recursionStack.Add(instance);
 | 
						|
        }
 | 
						|
        private void PopRecursionStack() { recursionStack.RemoveLast(); }
 | 
						|
 | 
						|
        private static SubItemToken StartSubItem(object instance, ProtoWriter writer, bool allowFixed)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            if (++writer.depth > RecursionCheckDepth)
 | 
						|
            {
 | 
						|
                writer.CheckRecursionStackAndPush(instance);
 | 
						|
            }
 | 
						|
            if (writer.packedFieldNumber != 0) throw new InvalidOperationException("Cannot begin a sub-item while performing packed encoding");
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.StartGroup:
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    return new SubItemToken((long)(-writer.fieldNumber));
 | 
						|
                case WireType.String:
 | 
						|
#if DEBUG
 | 
						|
                    if (writer.model != null && writer.model.ForwardsOnly)
 | 
						|
                    {
 | 
						|
                        throw new ProtoException("Should not be buffering data: " + instance ?? "(null)");
 | 
						|
                    }
 | 
						|
#endif
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    DemandSpace(32, writer); // make some space in anticipation...
 | 
						|
                    writer.flushLock++;
 | 
						|
                    writer.position64++;
 | 
						|
                    return new SubItemToken((long)(writer.ioIndex++)); // leave 1 space (optimistic) for length
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    {
 | 
						|
                        if (!allowFixed) throw CreateException(writer);
 | 
						|
                        DemandSpace(32, writer); // make some space in anticipation...
 | 
						|
                        writer.flushLock++;
 | 
						|
                        SubItemToken token = new SubItemToken((long)writer.ioIndex);
 | 
						|
                        ProtoWriter.IncrementedAndReset(4, writer); // leave 4 space (rigid) for length
 | 
						|
                        return token;
 | 
						|
                    }
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Indicates the end of a nested record.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="token">The token obtained from StartubItem.</param>
 | 
						|
        /// <param name="writer">The destination.</param>
 | 
						|
        public static void EndSubItem(SubItemToken token, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            EndSubItem(token, writer, PrefixStyle.Base128);
 | 
						|
        }
 | 
						|
        private static void EndSubItem(SubItemToken token, ProtoWriter writer, PrefixStyle style)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            if (writer.wireType != WireType.None) { throw CreateException(writer); }
 | 
						|
            int value = (int)token.value64;
 | 
						|
            if (writer.depth <= 0) throw CreateException(writer);
 | 
						|
            if (writer.depth-- > RecursionCheckDepth)
 | 
						|
            {
 | 
						|
                writer.PopRecursionStack();
 | 
						|
            }
 | 
						|
            writer.packedFieldNumber = 0; // ending the sub-item always wipes packed encoding
 | 
						|
            if (value < 0)
 | 
						|
            {   // group - very simple append
 | 
						|
                WriteHeaderCore(-value, WireType.EndGroup, writer);
 | 
						|
                writer.wireType = WireType.None;
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // so we're backfilling the length into an existing sequence
 | 
						|
            int len;
 | 
						|
            switch (style)
 | 
						|
            {
 | 
						|
                case PrefixStyle.Fixed32:
 | 
						|
                    len = (int)((writer.ioIndex - value) - 4);
 | 
						|
                    ProtoWriter.WriteInt32ToBuffer(len, writer.ioBuffer, value);
 | 
						|
                    break;
 | 
						|
                case PrefixStyle.Fixed32BigEndian:
 | 
						|
                    len = (int)((writer.ioIndex - value) - 4);
 | 
						|
                    byte[] buffer = writer.ioBuffer;
 | 
						|
                    ProtoWriter.WriteInt32ToBuffer(len, buffer, value);
 | 
						|
                    // and swap the byte order
 | 
						|
                    byte b = buffer[value];
 | 
						|
                    buffer[value] = buffer[value + 3];
 | 
						|
                    buffer[value + 3] = b;
 | 
						|
                    b = buffer[value + 1];
 | 
						|
                    buffer[value + 1] = buffer[value + 2];
 | 
						|
                    buffer[value + 2] = b;
 | 
						|
                    break;
 | 
						|
                case PrefixStyle.Base128:
 | 
						|
                    // string - complicated because we only reserved one byte;
 | 
						|
                    // if the prefix turns out to need more than this then
 | 
						|
                    // we need to shuffle the existing data
 | 
						|
                    len = (int)((writer.ioIndex - value) - 1);
 | 
						|
                    int offset = 0;
 | 
						|
                    uint tmp = (uint)len;
 | 
						|
                    while ((tmp >>= 7) != 0) offset++;
 | 
						|
                    if (offset == 0)
 | 
						|
                    {
 | 
						|
                        writer.ioBuffer[value] = (byte)(len & 0x7F);
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        DemandSpace(offset, writer);
 | 
						|
                        byte[] blob = writer.ioBuffer;
 | 
						|
                        Buffer.BlockCopy(blob, value + 1, blob, value + 1 + offset, len);
 | 
						|
                        tmp = (uint)len;
 | 
						|
                        do
 | 
						|
                        {
 | 
						|
                            blob[value++] = (byte)((tmp & 0x7F) | 0x80);
 | 
						|
                        } while ((tmp >>= 7) != 0);
 | 
						|
                        blob[value - 1] = (byte)(blob[value - 1] & ~0x80);
 | 
						|
                        writer.position64 += offset;
 | 
						|
                        writer.ioIndex += offset;
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                default:
 | 
						|
                    throw new ArgumentOutOfRangeException("style");
 | 
						|
            }
 | 
						|
            // and this object is no longer a blockage - also flush if sensible
 | 
						|
            const int ADVISORY_FLUSH_SIZE = 1024;
 | 
						|
            if (--writer.flushLock == 0 && writer.ioIndex >= ADVISORY_FLUSH_SIZE)
 | 
						|
            {
 | 
						|
                ProtoWriter.Flush(writer);
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Creates a new writer against a stream
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="dest">The destination stream</param>
 | 
						|
        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects</param>
 | 
						|
        /// <param name="context">Additional context about this serialization operation</param>
 | 
						|
        public static ProtoWriter Create(Stream dest, TypeModel model, SerializationContext context = null)
 | 
						|
#pragma warning disable CS0618
 | 
						|
            => new ProtoWriter(dest, model, context);
 | 
						|
#pragma warning restore CS0618
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Creates a new writer against a stream
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="dest">The destination stream</param>
 | 
						|
        /// <param name="model">The model to use for serialization; this can be null, but this will impair the ability to serialize sub-objects</param>
 | 
						|
        /// <param name="context">Additional context about this serialization operation</param>
 | 
						|
        [Obsolete("Please use ProtoWriter.Create; this API may be removed in a future version", error: false)]
 | 
						|
        public ProtoWriter(Stream dest, TypeModel model, SerializationContext context)
 | 
						|
        {
 | 
						|
            if (dest == null) throw new ArgumentNullException("dest");
 | 
						|
            if (!dest.CanWrite) throw new ArgumentException("Cannot write to stream", "dest");
 | 
						|
            //if (model == null) throw new ArgumentNullException("model");
 | 
						|
            this.dest = dest;
 | 
						|
            this.ioBuffer = BufferPool.GetBuffer();
 | 
						|
            this.model = model;
 | 
						|
            this.wireType = WireType.None;
 | 
						|
            if (context == null) { context = SerializationContext.Default; }
 | 
						|
            else { context.Freeze(); }
 | 
						|
            this.context = context;
 | 
						|
 | 
						|
        }
 | 
						|
 | 
						|
        private readonly SerializationContext context;
 | 
						|
        /// <summary>
 | 
						|
        /// Addition information about this serialization operation.
 | 
						|
        /// </summary>
 | 
						|
        public SerializationContext Context => context;
 | 
						|
 | 
						|
        void IDisposable.Dispose()
 | 
						|
        {
 | 
						|
            Dispose();
 | 
						|
        }
 | 
						|
 | 
						|
        private void Dispose()
 | 
						|
        {   // importantly, this does **not** own the stream, and does not dispose it
 | 
						|
            if (dest != null)
 | 
						|
            {
 | 
						|
                Flush(this);
 | 
						|
                dest = null;
 | 
						|
            }
 | 
						|
            model = null;
 | 
						|
            BufferPool.ReleaseBufferToPool(ref ioBuffer);
 | 
						|
        }
 | 
						|
 | 
						|
        private byte[] ioBuffer;
 | 
						|
        private int ioIndex;
 | 
						|
        // note that this is used by some of the unit tests and should not be removed
 | 
						|
        internal static long GetLongPosition(ProtoWriter writer) { return writer.position64; }
 | 
						|
        internal static int GetPosition(ProtoWriter writer) { return checked((int)writer.position64); }
 | 
						|
        private long position64;
 | 
						|
        private static void DemandSpace(int required, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            // check for enough space
 | 
						|
            if ((writer.ioBuffer.Length - writer.ioIndex) < required)
 | 
						|
            {
 | 
						|
                TryFlushOrResize(required, writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private static void TryFlushOrResize(int required, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer.flushLock == 0)
 | 
						|
            {
 | 
						|
                Flush(writer); // try emptying the buffer
 | 
						|
                if ((writer.ioBuffer.Length - writer.ioIndex) >= required) return;
 | 
						|
            }
 | 
						|
 | 
						|
            // either can't empty the buffer, or that didn't help; need more space
 | 
						|
            BufferPool.ResizeAndFlushLeft(ref writer.ioBuffer, required + writer.ioIndex, 0, writer.ioIndex);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Flushes data to the underlying stream, and releases any resources. The underlying stream is *not* disposed
 | 
						|
        /// by this operation.
 | 
						|
        /// </summary>
 | 
						|
        public void Close()
 | 
						|
        {
 | 
						|
            if (depth != 0 || flushLock != 0) throw new InvalidOperationException("Unable to close stream in an incomplete state");
 | 
						|
            Dispose();
 | 
						|
        }
 | 
						|
 | 
						|
        internal void CheckDepthFlushlock()
 | 
						|
        {
 | 
						|
            if (depth != 0 || flushLock != 0) throw new InvalidOperationException("The writer is in an incomplete state");
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Get the TypeModel associated with this writer
 | 
						|
        /// </summary>
 | 
						|
        public TypeModel Model => model;
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes any buffered data (if possible) to the underlying stream.
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="writer">The writer to flush</param>
 | 
						|
        /// <remarks>It is not always possible to fully flush, since some sequences
 | 
						|
        /// may require values to be back-filled into the byte-stream.</remarks>
 | 
						|
        internal static void Flush(ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer.flushLock == 0 && writer.ioIndex != 0)
 | 
						|
            {
 | 
						|
                writer.dest.Write(writer.ioBuffer, 0, writer.ioIndex);
 | 
						|
                writer.ioIndex = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes an unsigned 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        private static void WriteUInt32Variant(uint value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            DemandSpace(5, writer);
 | 
						|
            int count = 0;
 | 
						|
            do
 | 
						|
            {
 | 
						|
                writer.ioBuffer[writer.ioIndex++] = (byte)((value & 0x7F) | 0x80);
 | 
						|
                count++;
 | 
						|
            } while ((value >>= 7) != 0);
 | 
						|
            writer.ioBuffer[writer.ioIndex - 1] &= 0x7F;
 | 
						|
            writer.position64 += count;
 | 
						|
        }
 | 
						|
 | 
						|
#if COREFX
 | 
						|
        static readonly Encoding encoding = Encoding.UTF8;
 | 
						|
#else
 | 
						|
        static readonly UTF8Encoding encoding = new UTF8Encoding();
 | 
						|
#endif
 | 
						|
 | 
						|
        internal static uint Zig(int value)
 | 
						|
        {
 | 
						|
            return (uint)((value << 1) ^ (value >> 31));
 | 
						|
        }
 | 
						|
 | 
						|
        internal static ulong Zig(long value)
 | 
						|
        {
 | 
						|
            return (ulong)((value << 1) ^ (value >> 63));
 | 
						|
        }
 | 
						|
 | 
						|
        private static void WriteUInt64Variant(ulong value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            DemandSpace(10, writer);
 | 
						|
            int count = 0;
 | 
						|
            do
 | 
						|
            {
 | 
						|
                writer.ioBuffer[writer.ioIndex++] = (byte)((value & 0x7F) | 0x80);
 | 
						|
                count++;
 | 
						|
            } while ((value >>= 7) != 0);
 | 
						|
            writer.ioBuffer[writer.ioIndex - 1] &= 0x7F;
 | 
						|
            writer.position64 += count;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a string to the stream; supported wire-types: String
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteString(string value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            if (writer.wireType != WireType.String) throw CreateException(writer);
 | 
						|
            if (value == null) throw new ArgumentNullException("value"); // written header; now what?
 | 
						|
            int len = value.Length;
 | 
						|
            if (len == 0)
 | 
						|
            {
 | 
						|
                WriteUInt32Variant(0, writer);
 | 
						|
                writer.wireType = WireType.None;
 | 
						|
                return; // just a header
 | 
						|
            }
 | 
						|
            int predicted = encoding.GetByteCount(value);
 | 
						|
            WriteUInt32Variant((uint)predicted, writer);
 | 
						|
            DemandSpace(predicted, writer);
 | 
						|
            int actual = encoding.GetBytes(value, 0, value.Length, writer.ioBuffer, writer.ioIndex);
 | 
						|
            Helpers.DebugAssert(predicted == actual);
 | 
						|
            IncrementedAndReset(actual, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes an unsigned 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteUInt64(ulong value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    ProtoWriter.WriteInt64((long)value, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.Variant:
 | 
						|
                    WriteUInt64Variant(value, writer);
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    checked { ProtoWriter.WriteUInt32((uint)value, writer); }
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a signed 64-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteInt64(long value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            byte[] buffer;
 | 
						|
            int index;
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    DemandSpace(8, writer);
 | 
						|
                    buffer = writer.ioBuffer;
 | 
						|
                    index = writer.ioIndex;
 | 
						|
 | 
						|
#if NETCOREAPP2_1
 | 
						|
                    System.Buffers.Binary.BinaryPrimitives.WriteInt64LittleEndian(buffer.AsSpan(index, 8), value);
 | 
						|
#else
 | 
						|
                    buffer[index] = (byte)value;
 | 
						|
                    buffer[index + 1] = (byte)(value >> 8);
 | 
						|
                    buffer[index + 2] = (byte)(value >> 16);
 | 
						|
                    buffer[index + 3] = (byte)(value >> 24);
 | 
						|
                    buffer[index + 4] = (byte)(value >> 32);
 | 
						|
                    buffer[index + 5] = (byte)(value >> 40);
 | 
						|
                    buffer[index + 6] = (byte)(value >> 48);
 | 
						|
                    buffer[index + 7] = (byte)(value >> 56);
 | 
						|
#endif
 | 
						|
                    IncrementedAndReset(8, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.SignedVariant:
 | 
						|
                    WriteUInt64Variant(Zig(value), writer);
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    return;
 | 
						|
                case WireType.Variant:
 | 
						|
                    if (value >= 0)
 | 
						|
                    {
 | 
						|
                        WriteUInt64Variant((ulong)value, writer);
 | 
						|
                        writer.wireType = WireType.None;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        DemandSpace(10, writer);
 | 
						|
                        buffer = writer.ioBuffer;
 | 
						|
                        index = writer.ioIndex;
 | 
						|
                        buffer[index] = (byte)(value | 0x80);
 | 
						|
                        buffer[index + 1] = (byte)((int)(value >> 7) | 0x80);
 | 
						|
                        buffer[index + 2] = (byte)((int)(value >> 14) | 0x80);
 | 
						|
                        buffer[index + 3] = (byte)((int)(value >> 21) | 0x80);
 | 
						|
                        buffer[index + 4] = (byte)((int)(value >> 28) | 0x80);
 | 
						|
                        buffer[index + 5] = (byte)((int)(value >> 35) | 0x80);
 | 
						|
                        buffer[index + 6] = (byte)((int)(value >> 42) | 0x80);
 | 
						|
                        buffer[index + 7] = (byte)((int)(value >> 49) | 0x80);
 | 
						|
                        buffer[index + 8] = (byte)((int)(value >> 56) | 0x80);
 | 
						|
                        buffer[index + 9] = 0x01; // sign bit
 | 
						|
                        IncrementedAndReset(10, writer);
 | 
						|
                    }
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    checked { WriteInt32((int)value, writer); }
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteUInt32(uint value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    ProtoWriter.WriteInt32((int)value, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    ProtoWriter.WriteInt64((int)value, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.Variant:
 | 
						|
                    WriteUInt32Variant(value, writer);
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a signed 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteInt16(short value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            ProtoWriter.WriteInt32(value, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes an unsigned 16-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteUInt16(ushort value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            ProtoWriter.WriteUInt32(value, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes an unsigned 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteByte(byte value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            ProtoWriter.WriteUInt32(value, writer);
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a signed 8-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteSByte(sbyte value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            ProtoWriter.WriteInt32(value, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        private static void WriteInt32ToBuffer(int value, byte[] buffer, int index)
 | 
						|
        {
 | 
						|
#if NETCOREAPP2_1
 | 
						|
            System.Buffers.Binary.BinaryPrimitives.WriteInt32LittleEndian(buffer.AsSpan(index, 4), value);
 | 
						|
#else
 | 
						|
            buffer[index] = (byte)value;
 | 
						|
            buffer[index + 1] = (byte)(value >> 8);
 | 
						|
            buffer[index + 2] = (byte)(value >> 16);
 | 
						|
            buffer[index + 3] = (byte)(value >> 24);
 | 
						|
#endif
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a signed 32-bit integer to the stream; supported wire-types: Variant, Fixed32, Fixed64, SignedVariant
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteInt32(int value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            byte[] buffer;
 | 
						|
            int index;
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    DemandSpace(4, writer);
 | 
						|
                    WriteInt32ToBuffer(value, writer.ioBuffer, writer.ioIndex);
 | 
						|
                    IncrementedAndReset(4, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    DemandSpace(8, writer);
 | 
						|
                    buffer = writer.ioBuffer;
 | 
						|
                    index = writer.ioIndex;
 | 
						|
                    buffer[index] = (byte)value;
 | 
						|
                    buffer[index + 1] = (byte)(value >> 8);
 | 
						|
                    buffer[index + 2] = (byte)(value >> 16);
 | 
						|
                    buffer[index + 3] = (byte)(value >> 24);
 | 
						|
                    buffer[index + 4] = buffer[index + 5] =
 | 
						|
                        buffer[index + 6] = buffer[index + 7] = 0;
 | 
						|
                    IncrementedAndReset(8, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.SignedVariant:
 | 
						|
                    WriteUInt32Variant(Zig(value), writer);
 | 
						|
                    writer.wireType = WireType.None;
 | 
						|
                    return;
 | 
						|
                case WireType.Variant:
 | 
						|
                    if (value >= 0)
 | 
						|
                    {
 | 
						|
                        WriteUInt32Variant((uint)value, writer);
 | 
						|
                        writer.wireType = WireType.None;
 | 
						|
                    }
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                        DemandSpace(10, writer);
 | 
						|
                        buffer = writer.ioBuffer;
 | 
						|
                        index = writer.ioIndex;
 | 
						|
                        buffer[index] = (byte)(value | 0x80);
 | 
						|
                        buffer[index + 1] = (byte)((value >> 7) | 0x80);
 | 
						|
                        buffer[index + 2] = (byte)((value >> 14) | 0x80);
 | 
						|
                        buffer[index + 3] = (byte)((value >> 21) | 0x80);
 | 
						|
                        buffer[index + 4] = (byte)((value >> 28) | 0x80);
 | 
						|
                        buffer[index + 5] = buffer[index + 6] =
 | 
						|
                            buffer[index + 7] = buffer[index + 8] = (byte)0xFF;
 | 
						|
                        buffer[index + 9] = (byte)0x01;
 | 
						|
                        IncrementedAndReset(10, writer);
 | 
						|
                    }
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a double-precision number to the stream; supported wire-types: Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public
 | 
						|
#if !FEAT_SAFE
 | 
						|
            unsafe
 | 
						|
#endif
 | 
						|
 | 
						|
                static void WriteDouble(double value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed32:
 | 
						|
                    float f = (float)value;
 | 
						|
                    if (float.IsInfinity(f) && !double.IsInfinity(value))
 | 
						|
                    {
 | 
						|
                        throw new OverflowException();
 | 
						|
                    }
 | 
						|
                    ProtoWriter.WriteSingle(f, writer);
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed64:
 | 
						|
#if FEAT_SAFE
 | 
						|
                    ProtoWriter.WriteInt64(BitConverter.ToInt64(BitConverter.GetBytes(value), 0), writer);
 | 
						|
#else
 | 
						|
                    ProtoWriter.WriteInt64(*(long*)&value, writer);
 | 
						|
#endif
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a single-precision number to the stream; supported wire-types: Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public
 | 
						|
#if !FEAT_SAFE
 | 
						|
            unsafe
 | 
						|
#endif
 | 
						|
            static void WriteSingle(float value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            switch (writer.wireType)
 | 
						|
            {
 | 
						|
                case WireType.Fixed32:
 | 
						|
#if FEAT_SAFE
 | 
						|
                    ProtoWriter.WriteInt32(BitConverter.ToInt32(BitConverter.GetBytes(value), 0), writer);
 | 
						|
#else
 | 
						|
                    ProtoWriter.WriteInt32(*(int*)&value, writer);
 | 
						|
#endif
 | 
						|
                    return;
 | 
						|
                case WireType.Fixed64:
 | 
						|
                    ProtoWriter.WriteDouble((double)value, writer);
 | 
						|
                    return;
 | 
						|
                default:
 | 
						|
                    throw CreateException(writer);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Throws an exception indicating that the given enum cannot be mapped to a serialized value.
 | 
						|
        /// </summary>
 | 
						|
        public static void ThrowEnumException(ProtoWriter writer, object enumValue)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            string rhs = enumValue == null ? "<null>" : (enumValue.GetType().FullName + "." + enumValue.ToString());
 | 
						|
            throw new ProtoException("No wire-value is mapped to the enum " + rhs + " at position " + writer.position64.ToString());
 | 
						|
        }
 | 
						|
 | 
						|
        // general purpose serialization exception message
 | 
						|
        internal static Exception CreateException(ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException("writer");
 | 
						|
            return new ProtoException("Invalid serialization operation with wire-type " + writer.wireType.ToString() + " at position " + writer.position64.ToString());
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a boolean to the stream; supported wire-types: Variant, Fixed32, Fixed64
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteBoolean(bool value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            ProtoWriter.WriteUInt32(value ? (uint)1 : (uint)0, writer);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Copies any extension data stored for the instance to the underlying stream
 | 
						|
        /// </summary>
 | 
						|
        public static void AppendExtensionData(IExtensible instance, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (instance == null) throw new ArgumentNullException(nameof(instance));
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            // we expect the writer to be raw here; the extension data will have the
 | 
						|
            // header detail, so we'll copy it implicitly
 | 
						|
            if (writer.wireType != WireType.None) throw CreateException(writer);
 | 
						|
 | 
						|
            IExtension extn = instance.GetExtensionObject(false);
 | 
						|
            if (extn != null)
 | 
						|
            {
 | 
						|
                // unusually we *don't* want "using" here; the "finally" does that, with
 | 
						|
                // the extension object being responsible for disposal etc
 | 
						|
                Stream source = extn.BeginQuery();
 | 
						|
                try
 | 
						|
                {
 | 
						|
                    CopyRawFromStream(source, writer);
 | 
						|
                }
 | 
						|
                finally { extn.EndQuery(source); }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        private int packedFieldNumber;
 | 
						|
        /// <summary>
 | 
						|
        /// Used for packed encoding; indicates that the next field should be skipped rather than
 | 
						|
        /// a field header written. Note that the field number must match, else an exception is thrown
 | 
						|
        /// when the attempt is made to write the (incorrect) field. The wire-type is taken from the
 | 
						|
        /// subsequent call to WriteFieldHeader. Only primitive types can be packed.
 | 
						|
        /// </summary>
 | 
						|
        public static void SetPackedField(int fieldNumber, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (fieldNumber <= 0) throw new ArgumentOutOfRangeException(nameof(fieldNumber));
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            writer.packedFieldNumber = fieldNumber;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Used for packed encoding; explicitly reset the packed field marker; this is not required
 | 
						|
        /// if using StartSubItem/EndSubItem
 | 
						|
        /// </summary>
 | 
						|
        public static void ClearPackedField(int fieldNumber, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (fieldNumber != writer.packedFieldNumber)
 | 
						|
                throw new InvalidOperationException("Field mismatch during packed encoding; expected " + writer.packedFieldNumber.ToString() + " but received " + fieldNumber.ToString());
 | 
						|
            writer.packedFieldNumber = 0;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Used for packed encoding; writes the length prefix using fixed sizes rather than using
 | 
						|
        /// buffering. Only valid for fixed-32 and fixed-64 encoding.
 | 
						|
        /// </summary>
 | 
						|
        public static void WritePackedPrefix(int elementCount, WireType wireType, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer.WireType != WireType.String) throw new InvalidOperationException("Invalid wire-type: " + writer.WireType);
 | 
						|
            if (elementCount < 0) throw new ArgumentOutOfRangeException(nameof(elementCount));
 | 
						|
            ulong bytes;
 | 
						|
            switch (wireType)
 | 
						|
            {
 | 
						|
                // use long in case very large arrays are enabled
 | 
						|
                case WireType.Fixed32: bytes = ((ulong)elementCount) << 2; break; // x4
 | 
						|
                case WireType.Fixed64: bytes = ((ulong)elementCount) << 3; break; // x8
 | 
						|
                default:
 | 
						|
                    throw new ArgumentOutOfRangeException(nameof(wireType), "Invalid wire-type: " + wireType);
 | 
						|
            }
 | 
						|
            WriteUInt64Variant(bytes, writer);
 | 
						|
            writer.wireType = WireType.None;
 | 
						|
        }
 | 
						|
 | 
						|
        internal string SerializeType(Type type)
 | 
						|
        {
 | 
						|
            return TypeModel.SerializeType(model, type);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Specifies a known root object to use during reference-tracked serialization
 | 
						|
        /// </summary>
 | 
						|
        public void SetRootObject(object value)
 | 
						|
        {
 | 
						|
            NetCache.SetKeyedObject(NetObjectCache.Root, value);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Writes a Type to the stream, using the model's DynamicTypeFormatting if appropriate; supported wire-types: String
 | 
						|
        /// </summary>
 | 
						|
        public static void WriteType(Type value, ProtoWriter writer)
 | 
						|
        {
 | 
						|
            if (writer == null) throw new ArgumentNullException(nameof(writer));
 | 
						|
            WriteString(writer.SerializeType(value), writer);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |