diff --git a/Libs/Google.Protobuf/ByteArray.cs b/Libs/Google.Protobuf/ByteArray.cs
new file mode 100644
index 0000000..b945ee1
--- /dev/null
+++ b/Libs/Google.Protobuf/ByteArray.cs
@@ -0,0 +1,79 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Provides a utility routine to copy small arrays much more quickly than Buffer.BlockCopy
+ ///
+ internal static class ByteArray
+ {
+ ///
+ /// The threshold above which you should use Buffer.BlockCopy rather than ByteArray.Copy
+ ///
+ private const int CopyThreshold = 12;
+
+ ///
+ /// Determines which copy routine to use based on the number of bytes to be copied.
+ ///
+ internal static void Copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int count)
+ {
+ if (count > CopyThreshold)
+ {
+ Buffer.BlockCopy(src, srcOffset, dst, dstOffset, count);
+ }
+ else
+ {
+ int stop = srcOffset + count;
+ for (int i = srcOffset; i < stop; i++)
+ {
+ dst[dstOffset++] = src[i];
+ }
+ }
+ }
+
+ ///
+ /// Reverses the order of bytes in the array
+ ///
+ internal static void Reverse(byte[] bytes)
+ {
+ for (int first = 0, last = bytes.Length - 1; first < last; first++, last--)
+ {
+ byte temp = bytes[first];
+ bytes[first] = bytes[last];
+ bytes[last] = temp;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/ByteString.cs b/Libs/Google.Protobuf/ByteString.cs
new file mode 100644
index 0000000..1d2ccbe
--- /dev/null
+++ b/Libs/Google.Protobuf/ByteString.cs
@@ -0,0 +1,434 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Text;
+#if !NET35
+using System.Threading;
+using System.Threading.Tasks;
+#endif
+#if NET35
+using LC.Google.Protobuf.Compatibility;
+#endif
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Immutable array of bytes.
+ ///
+ [SecuritySafeCritical]
+ public sealed class ByteString : IEnumerable, IEquatable
+ {
+ private static readonly ByteString empty = new ByteString(new byte[0]);
+
+ private readonly ReadOnlyMemory bytes;
+
+ ///
+ /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.
+ ///
+ internal static ByteString AttachBytes(ReadOnlyMemory bytes)
+ {
+ return new ByteString(bytes);
+ }
+
+ ///
+ /// Internal use only. Ensure that the provided memory is not mutated and belongs to this instance.
+ /// This method encapsulates converting array to memory. Reduces need for SecuritySafeCritical
+ /// in .NET Framework.
+ ///
+ internal static ByteString AttachBytes(byte[] bytes)
+ {
+ return AttachBytes(bytes.AsMemory());
+ }
+
+ ///
+ /// Constructs a new ByteString from the given memory. The memory is
+ /// *not* copied, and must not be modified after this constructor is called.
+ ///
+ private ByteString(ReadOnlyMemory bytes)
+ {
+ this.bytes = bytes;
+ }
+
+ ///
+ /// Returns an empty ByteString.
+ ///
+ public static ByteString Empty
+ {
+ get { return empty; }
+ }
+
+ ///
+ /// Returns the length of this ByteString in bytes.
+ ///
+ public int Length
+ {
+ get { return bytes.Length; }
+ }
+
+ ///
+ /// Returns true if this byte string is empty, false otherwise.
+ ///
+ public bool IsEmpty
+ {
+ get { return Length == 0; }
+ }
+
+ ///
+ /// Provides read-only access to the data of this .
+ /// No data is copied so this is the most efficient way of accessing.
+ ///
+ public ReadOnlySpan Span
+ {
+ get { return bytes.Span; }
+ }
+
+ ///
+ /// Provides read-only access to the data of this .
+ /// No data is copied so this is the most efficient way of accessing.
+ ///
+ public ReadOnlyMemory Memory
+ {
+ get { return bytes; }
+ }
+
+ ///
+ /// Converts this into a byte array.
+ ///
+ /// The data is copied - changes to the returned array will not be reflected in this ByteString.
+ /// A byte array with the same data as this ByteString.
+ public byte[] ToByteArray()
+ {
+ return bytes.ToArray();
+ }
+
+ ///
+ /// Converts this into a standard base64 representation.
+ ///
+ /// A base64 representation of this ByteString.
+ public string ToBase64()
+ {
+ if (MemoryMarshal.TryGetArray(bytes, out ArraySegment segment))
+ {
+ // Fast path. ByteString was created with an array, so pass the underlying array.
+ return Convert.ToBase64String(segment.Array, segment.Offset, segment.Count);
+ }
+ else
+ {
+ // Slow path. BytesString is not an array. Convert memory and pass result to ToBase64String.
+ return Convert.ToBase64String(bytes.ToArray());
+ }
+ }
+
+ ///
+ /// Constructs a from the Base64 Encoded String.
+ ///
+ public static ByteString FromBase64(string bytes)
+ {
+ // By handling the empty string explicitly, we not only optimize but we fix a
+ // problem on CF 2.0. See issue 61 for details.
+ return bytes == "" ? Empty : new ByteString(Convert.FromBase64String(bytes));
+ }
+
+ ///
+ /// Constructs a from data in the given stream, synchronously.
+ ///
+ /// If successful, will be read completely, from the position
+ /// at the start of the call.
+ /// The stream to copy into a ByteString.
+ /// A ByteString with content read from the given stream.
+ public static ByteString FromStream(Stream stream)
+ {
+ ProtoPreconditions.CheckNotNull(stream, nameof(stream));
+ int capacity = stream.CanSeek ? checked((int) (stream.Length - stream.Position)) : 0;
+ var memoryStream = new MemoryStream(capacity);
+ stream.CopyTo(memoryStream);
+#if NETSTANDARD1_1 || NETSTANDARD2_0
+ byte[] bytes = memoryStream.ToArray();
+#else
+ // Avoid an extra copy if we can.
+ byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
+#endif
+ return AttachBytes(bytes);
+ }
+
+#if !NET35
+ ///
+ /// Constructs a from data in the given stream, asynchronously.
+ ///
+ /// If successful, will be read completely, from the position
+ /// at the start of the call.
+ /// The stream to copy into a ByteString.
+ /// The cancellation token to use when reading from the stream, if any.
+ /// A ByteString with content read from the given stream.
+ public static Task FromStreamAsync(Stream stream, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ ProtoPreconditions.CheckNotNull(stream, nameof(stream));
+ return ByteStringAsync.FromStreamAsyncCore(stream, cancellationToken);
+ }
+#endif
+
+ ///
+ /// Constructs a from the given array. The contents
+ /// are copied, so further modifications to the array will not
+ /// be reflected in the returned ByteString.
+ /// This method can also be invoked in ByteString.CopyFrom(0xaa, 0xbb, ...) form
+ /// which is primarily useful for testing.
+ ///
+ public static ByteString CopyFrom(params byte[] bytes)
+ {
+ return new ByteString((byte[]) bytes.Clone());
+ }
+
+ ///
+ /// Constructs a from a portion of a byte array.
+ ///
+ public static ByteString CopyFrom(byte[] bytes, int offset, int count)
+ {
+ byte[] portion = new byte[count];
+ ByteArray.Copy(bytes, offset, portion, 0, count);
+ return new ByteString(portion);
+ }
+
+ ///
+ /// Constructs a from a read only span. The contents
+ /// are copied, so further modifications to the span will not
+ /// be reflected in the returned .
+ ///
+ public static ByteString CopyFrom(ReadOnlySpan bytes)
+ {
+ return new ByteString(bytes.ToArray());
+ }
+
+ ///
+ /// Creates a new by encoding the specified text with
+ /// the given encoding.
+ ///
+ public static ByteString CopyFrom(string text, Encoding encoding)
+ {
+ return new ByteString(encoding.GetBytes(text));
+ }
+
+ ///
+ /// Creates a new by encoding the specified text in UTF-8.
+ ///
+ public static ByteString CopyFromUtf8(string text)
+ {
+ return CopyFrom(text, Encoding.UTF8);
+ }
+
+ ///
+ /// Returns the byte at the given index.
+ ///
+ public byte this[int index]
+ {
+ get { return bytes.Span[index]; }
+ }
+
+ ///
+ /// Converts this into a string by applying the given encoding.
+ ///
+ ///
+ /// This method should only be used to convert binary data which was the result of encoding
+ /// text with the given encoding.
+ ///
+ /// The encoding to use to decode the binary data into text.
+ /// The result of decoding the binary data with the given decoding.
+ public string ToString(Encoding encoding)
+ {
+ if (MemoryMarshal.TryGetArray(bytes, out ArraySegment segment))
+ {
+ // Fast path. ByteString was created with an array.
+ return encoding.GetString(segment.Array, segment.Offset, segment.Count);
+ }
+ else
+ {
+ // Slow path. BytesString is not an array. Convert memory and pass result to GetString.
+ // TODO: Consider using GetString overload that takes a pointer.
+ byte[] array = bytes.ToArray();
+ return encoding.GetString(array, 0, array.Length);
+ }
+ }
+
+ ///
+ /// Converts this into a string by applying the UTF-8 encoding.
+ ///
+ ///
+ /// This method should only be used to convert binary data which was the result of encoding
+ /// text with UTF-8.
+ ///
+ /// The result of decoding the binary data with the given decoding.
+ public string ToStringUtf8()
+ {
+ return ToString(Encoding.UTF8);
+ }
+
+ ///
+ /// Returns an iterator over the bytes in this .
+ ///
+ /// An iterator over the bytes in this object.
+ [SecuritySafeCritical]
+ public IEnumerator GetEnumerator()
+ {
+ return MemoryMarshal.ToEnumerable(bytes).GetEnumerator();
+ }
+
+ ///
+ /// Returns an iterator over the bytes in this .
+ ///
+ /// An iterator over the bytes in this object.
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Creates a CodedInputStream from this ByteString's data.
+ ///
+ public CodedInputStream CreateCodedInput()
+ {
+ // We trust CodedInputStream not to reveal the provided byte array or modify it
+ if (MemoryMarshal.TryGetArray(bytes, out ArraySegment segment) && segment.Count == bytes.Length)
+ {
+ // Fast path. ByteString was created with a complete array.
+ return new CodedInputStream(segment.Array);
+ }
+ else
+ {
+ // Slow path. BytesString is not an array, or is a slice of an array.
+ // Convert memory and pass result to WriteRawBytes.
+ return new CodedInputStream(bytes.ToArray());
+ }
+ }
+
+ ///
+ /// Compares two byte strings for equality.
+ ///
+ /// The first byte string to compare.
+ /// The second byte string to compare.
+ /// true if the byte strings are equal; false otherwise.
+ public static bool operator ==(ByteString lhs, ByteString rhs)
+ {
+ if (ReferenceEquals(lhs, rhs))
+ {
+ return true;
+ }
+ if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
+ {
+ return false;
+ }
+
+ return lhs.bytes.Span.SequenceEqual(rhs.bytes.Span);
+ }
+
+ ///
+ /// Compares two byte strings for inequality.
+ ///
+ /// The first byte string to compare.
+ /// The second byte string to compare.
+ /// false if the byte strings are equal; true otherwise.
+ public static bool operator !=(ByteString lhs, ByteString rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ ///
+ /// Compares this byte string with another object.
+ ///
+ /// The object to compare this with.
+ /// true if refers to an equal ; false otherwise.
+ [SecuritySafeCritical]
+ public override bool Equals(object obj)
+ {
+ return this == (obj as ByteString);
+ }
+
+ ///
+ /// Returns a hash code for this object. Two equal byte strings
+ /// will return the same hash code.
+ ///
+ /// A hash code for this object.
+ [SecuritySafeCritical]
+ public override int GetHashCode()
+ {
+ ReadOnlySpan b = bytes.Span;
+
+ int ret = 23;
+ for (int i = 0; i < b.Length; i++)
+ {
+ ret = (ret * 31) + b[i];
+ }
+ return ret;
+ }
+
+ ///
+ /// Compares this byte string with another.
+ ///
+ /// The to compare this with.
+ /// true if refers to an equal byte string; false otherwise.
+ public bool Equals(ByteString other)
+ {
+ return this == other;
+ }
+
+ ///
+ /// Copies the entire byte array to the destination array provided at the offset specified.
+ ///
+ public void CopyTo(byte[] array, int position)
+ {
+ bytes.CopyTo(array.AsMemory(position));
+ }
+
+ ///
+ /// Writes the entire byte array to the provided stream
+ ///
+ public void WriteTo(Stream outputStream)
+ {
+ if (MemoryMarshal.TryGetArray(bytes, out ArraySegment segment))
+ {
+ // Fast path. ByteString was created with an array, so pass the underlying array.
+ outputStream.Write(segment.Array, segment.Offset, segment.Count);
+ }
+ else
+ {
+ // Slow path. BytesString is not an array. Convert memory and pass result to WriteRawBytes.
+ var array = bytes.ToArray();
+ outputStream.Write(array, 0, array.Length);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/ByteStringAsync.cs b/Libs/Google.Protobuf/ByteStringAsync.cs
new file mode 100644
index 0000000..68c4d7f
--- /dev/null
+++ b/Libs/Google.Protobuf/ByteStringAsync.cs
@@ -0,0 +1,64 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// SecuritySafeCritical attribute can not be placed on types with async methods.
+ /// This class has ByteString's async methods so it can be marked with SecuritySafeCritical.
+ ///
+ internal static class ByteStringAsync
+ {
+#if !NET35
+ internal static async Task FromStreamAsyncCore(Stream stream, CancellationToken cancellationToken)
+ {
+ int capacity = stream.CanSeek ? checked((int)(stream.Length - stream.Position)) : 0;
+ var memoryStream = new MemoryStream(capacity);
+ // We have to specify the buffer size here, as there's no overload accepting the cancellation token
+ // alone. But it's documented to use 81920 by default if not specified.
+ await stream.CopyToAsync(memoryStream, 81920, cancellationToken);
+#if NETSTANDARD1_1
+ byte[] bytes = memoryStream.ToArray();
+#else
+ // Avoid an extra copy if we can.
+ byte[] bytes = memoryStream.Length == memoryStream.Capacity ? memoryStream.GetBuffer() : memoryStream.ToArray();
+#endif
+ return ByteString.AttachBytes(bytes);
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/CodedInputStream.cs b/Libs/Google.Protobuf/CodedInputStream.cs
new file mode 100644
index 0000000..3660c19
--- /dev/null
+++ b/Libs/Google.Protobuf/CodedInputStream.cs
@@ -0,0 +1,699 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using LC.Google.Protobuf.Collections;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Reads and decodes protocol message fields.
+ ///
+ ///
+ ///
+ /// This class is generally used by generated code to read appropriate
+ /// primitives from the stream. It effectively encapsulates the lowest
+ /// levels of protocol buffer format.
+ ///
+ ///
+ /// Repeated fields and map fields are not handled by this class; use
+ /// and to serialize such fields.
+ ///
+ ///
+ [SecuritySafeCritical]
+ public sealed class CodedInputStream : IDisposable
+ {
+ ///
+ /// Whether to leave the underlying stream open when disposing of this stream.
+ /// This is always true when there's no stream.
+ ///
+ private readonly bool leaveOpen;
+
+ ///
+ /// Buffer of data read from the stream or provided at construction time.
+ ///
+ private readonly byte[] buffer;
+
+ ///
+ /// The stream to read further input from, or null if the byte array buffer was provided
+ /// directly on construction, with no further data available.
+ ///
+ private readonly Stream input;
+
+ ///
+ /// The parser state is kept separately so that other parse implementations can reuse the same
+ /// parsing primitives.
+ ///
+ private ParserInternalState state;
+
+ internal const int DefaultRecursionLimit = 100;
+ internal const int DefaultSizeLimit = Int32.MaxValue;
+ internal const int BufferSize = 4096;
+
+ #region Construction
+ // Note that the checks are performed such that we don't end up checking obviously-valid things
+ // like non-null references for arrays we've just created.
+
+ ///
+ /// Creates a new CodedInputStream reading data from the given byte array.
+ ///
+ public CodedInputStream(byte[] buffer) : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), 0, buffer.Length, true)
+ {
+ }
+
+ ///
+ /// Creates a new that reads from the given byte array slice.
+ ///
+ public CodedInputStream(byte[] buffer, int offset, int length)
+ : this(null, ProtoPreconditions.CheckNotNull(buffer, "buffer"), offset, offset + length, true)
+ {
+ if (offset < 0 || offset > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException("offset", "Offset must be within the buffer");
+ }
+ if (length < 0 || offset + length > buffer.Length)
+ {
+ throw new ArgumentOutOfRangeException("length", "Length must be non-negative and within the buffer");
+ }
+ }
+
+ ///
+ /// Creates a new reading data from the given stream, which will be disposed
+ /// when the returned object is disposed.
+ ///
+ /// The stream to read from.
+ public CodedInputStream(Stream input) : this(input, false)
+ {
+ }
+
+ ///
+ /// Creates a new reading data from the given stream.
+ ///
+ /// The stream to read from.
+ /// true to leave open when the returned
+ /// is disposed; false to dispose of the given stream when the
+ /// returned object is disposed.
+ public CodedInputStream(Stream input, bool leaveOpen)
+ : this(ProtoPreconditions.CheckNotNull(input, "input"), new byte[BufferSize], 0, 0, leaveOpen)
+ {
+ }
+
+ ///
+ /// Creates a new CodedInputStream reading data from the given
+ /// stream and buffer, using the default limits.
+ ///
+ internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, bool leaveOpen)
+ {
+ this.input = input;
+ this.buffer = buffer;
+ this.state.bufferPos = bufferPos;
+ this.state.bufferSize = bufferSize;
+ this.state.sizeLimit = DefaultSizeLimit;
+ this.state.recursionLimit = DefaultRecursionLimit;
+ SegmentedBufferHelper.Initialize(this, out this.state.segmentedBufferHelper);
+ this.leaveOpen = leaveOpen;
+
+ this.state.currentLimit = int.MaxValue;
+ }
+
+ ///
+ /// Creates a new CodedInputStream reading data from the given
+ /// stream and buffer, using the specified limits.
+ ///
+ ///
+ /// This chains to the version with the default limits instead of vice versa to avoid
+ /// having to check that the default values are valid every time.
+ ///
+ internal CodedInputStream(Stream input, byte[] buffer, int bufferPos, int bufferSize, int sizeLimit, int recursionLimit, bool leaveOpen)
+ : this(input, buffer, bufferPos, bufferSize, leaveOpen)
+ {
+ if (sizeLimit <= 0)
+ {
+ throw new ArgumentOutOfRangeException("sizeLimit", "Size limit must be positive");
+ }
+ if (recursionLimit <= 0)
+ {
+ throw new ArgumentOutOfRangeException("recursionLimit!", "Recursion limit must be positive");
+ }
+ this.state.sizeLimit = sizeLimit;
+ this.state.recursionLimit = recursionLimit;
+ }
+ #endregion
+
+ ///
+ /// Creates a with the specified size and recursion limits, reading
+ /// from an input stream.
+ ///
+ ///
+ /// This method exists separately from the constructor to reduce the number of constructor overloads.
+ /// It is likely to be used considerably less frequently than the constructors, as the default limits
+ /// are suitable for most use cases.
+ ///
+ /// The input stream to read from
+ /// The total limit of data to read from the stream.
+ /// The maximum recursion depth to allow while reading.
+ /// A CodedInputStream reading from with the specified size
+ /// and recursion limits.
+ public static CodedInputStream CreateWithLimits(Stream input, int sizeLimit, int recursionLimit)
+ {
+ // Note: we may want an overload accepting leaveOpen
+ return new CodedInputStream(input, new byte[BufferSize], 0, 0, sizeLimit, recursionLimit, false);
+ }
+
+ ///
+ /// Returns the current position in the input stream, or the position in the input buffer
+ ///
+ public long Position
+ {
+ get
+ {
+ if (input != null)
+ {
+ return input.Position - ((state.bufferSize + state.bufferSizeAfterLimit) - state.bufferPos);
+ }
+ return state.bufferPos;
+ }
+ }
+
+ ///
+ /// Returns the last tag read, or 0 if no tags have been read or we've read beyond
+ /// the end of the stream.
+ ///
+ internal uint LastTag { get { return state.lastTag; } }
+
+ ///
+ /// Returns the size limit for this stream.
+ ///
+ ///
+ /// This limit is applied when reading from the underlying stream, as a sanity check. It is
+ /// not applied when reading from a byte array data source without an underlying stream.
+ /// The default value is Int32.MaxValue.
+ ///
+ ///
+ /// The size limit.
+ ///
+ public int SizeLimit { get { return state.sizeLimit; } }
+
+ ///
+ /// Returns the recursion limit for this stream. This limit is applied whilst reading messages,
+ /// to avoid maliciously-recursive data.
+ ///
+ ///
+ /// The default limit is 100.
+ ///
+ ///
+ /// The recursion limit for this stream.
+ ///
+ public int RecursionLimit { get { return state.recursionLimit; } }
+
+ ///
+ /// Internal-only property; when set to true, unknown fields will be discarded while parsing.
+ ///
+ internal bool DiscardUnknownFields
+ {
+ get { return state.DiscardUnknownFields; }
+ set { state.DiscardUnknownFields = value; }
+ }
+
+ ///
+ /// Internal-only property; provides extension identifiers to compatible messages while parsing.
+ ///
+ internal ExtensionRegistry ExtensionRegistry
+ {
+ get { return state.ExtensionRegistry; }
+ set { state.ExtensionRegistry = value; }
+ }
+
+ internal byte[] InternalBuffer => buffer;
+
+ internal Stream InternalInputStream => input;
+
+ internal ref ParserInternalState InternalState => ref state;
+
+ ///
+ /// Disposes of this instance, potentially closing any underlying stream.
+ ///
+ ///
+ /// As there is no flushing to perform here, disposing of a which
+ /// was constructed with the leaveOpen option parameter set to true (or one which
+ /// was constructed to read from a byte array) has no effect.
+ ///
+ public void Dispose()
+ {
+ if (!leaveOpen)
+ {
+ input.Dispose();
+ }
+ }
+
+ #region Validation
+ ///
+ /// Verifies that the last call to ReadTag() returned tag 0 - in other words,
+ /// we've reached the end of the stream when we expected to.
+ ///
+ /// The
+ /// tag read was not the one specified
+ internal void CheckReadEndOfStreamTag()
+ {
+ ParsingPrimitivesMessages.CheckReadEndOfStreamTag(ref state);
+ }
+ #endregion
+
+ #region Reading of tags etc
+
+ ///
+ /// Peeks at the next field tag. This is like calling , but the
+ /// tag is not consumed. (So a subsequent call to will return the
+ /// same value.)
+ ///
+ public uint PeekTag()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.PeekTag(ref span, ref state);
+ }
+
+ ///
+ /// Reads a field tag, returning the tag of 0 for "end of stream".
+ ///
+ ///
+ /// If this method returns 0, it doesn't necessarily mean the end of all
+ /// the data in this CodedInputStream; it may be the end of the logical stream
+ /// for an embedded message, for example.
+ ///
+ /// The next field tag, or 0 for end of stream. (0 is never a valid tag.)
+ public uint ReadTag()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseTag(ref span, ref state);
+ }
+
+ ///
+ /// Skips the data for the field with the tag we've just read.
+ /// This should be called directly after , when
+ /// the caller wishes to skip an unknown field.
+ ///
+ ///
+ /// This method throws if the last-read tag was an end-group tag.
+ /// If a caller wishes to skip a group, they should skip the whole group, by calling this method after reading the
+ /// start-group tag. This behavior allows callers to call this method on any field they don't understand, correctly
+ /// resulting in an error if an end-group tag has not been paired with an earlier start-group tag.
+ ///
+ /// The last tag was an end-group tag
+ /// The last read operation read to the end of the logical stream
+ public void SkipLastField()
+ {
+ var span = new ReadOnlySpan(buffer);
+ ParsingPrimitivesMessages.SkipLastField(ref span, ref state);
+ }
+
+ ///
+ /// Skip a group.
+ ///
+ internal void SkipGroup(uint startGroupTag)
+ {
+ var span = new ReadOnlySpan(buffer);
+ ParsingPrimitivesMessages.SkipGroup(ref span, ref state, startGroupTag);
+ }
+
+ ///
+ /// Reads a double field from the stream.
+ ///
+ public double ReadDouble()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseDouble(ref span, ref state);
+ }
+
+ ///
+ /// Reads a float field from the stream.
+ ///
+ public float ReadFloat()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseFloat(ref span, ref state);
+ }
+
+ ///
+ /// Reads a uint64 field from the stream.
+ ///
+ public ulong ReadUInt64()
+ {
+ return ReadRawVarint64();
+ }
+
+ ///
+ /// Reads an int64 field from the stream.
+ ///
+ public long ReadInt64()
+ {
+ return (long) ReadRawVarint64();
+ }
+
+ ///
+ /// Reads an int32 field from the stream.
+ ///
+ public int ReadInt32()
+ {
+ return (int) ReadRawVarint32();
+ }
+
+ ///
+ /// Reads a fixed64 field from the stream.
+ ///
+ public ulong ReadFixed64()
+ {
+ return ReadRawLittleEndian64();
+ }
+
+ ///
+ /// Reads a fixed32 field from the stream.
+ ///
+ public uint ReadFixed32()
+ {
+ return ReadRawLittleEndian32();
+ }
+
+ ///
+ /// Reads a bool field from the stream.
+ ///
+ public bool ReadBool()
+ {
+ return ReadRawVarint64() != 0;
+ }
+
+ ///
+ /// Reads a string field from the stream.
+ ///
+ public string ReadString()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ReadString(ref span, ref state);
+ }
+
+ ///
+ /// Reads an embedded message field value from the stream.
+ ///
+ public void ReadMessage(IMessage builder)
+ {
+ // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalMergeFrom method),
+ // what we're doing here works fine, but could be more efficient.
+ // What happends is that we first initialize a ParseContext from the current coded input stream only to parse the length of the message, at which point
+ // we will need to switch back again to CodedInputStream-based parsing (which involves copying and storing the state) to be able to
+ // invoke the legacy MergeFrom(CodedInputStream) method.
+ // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+ var span = new ReadOnlySpan(buffer);
+ ParseContext.Initialize(ref span, ref state, out ParseContext ctx);
+ try
+ {
+ ParsingPrimitivesMessages.ReadMessage(ref ctx, builder);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+
+ ///
+ /// Reads an embedded group field from the stream.
+ ///
+ public void ReadGroup(IMessage builder)
+ {
+ ParseContext.Initialize(this, out ParseContext ctx);
+ try
+ {
+ ParsingPrimitivesMessages.ReadGroup(ref ctx, builder);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+
+ ///
+ /// Reads a bytes field value from the stream.
+ ///
+ public ByteString ReadBytes()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ReadBytes(ref span, ref state);
+ }
+
+ ///
+ /// Reads a uint32 field value from the stream.
+ ///
+ public uint ReadUInt32()
+ {
+ return ReadRawVarint32();
+ }
+
+ ///
+ /// Reads an enum field value from the stream.
+ ///
+ public int ReadEnum()
+ {
+ // Currently just a pass-through, but it's nice to separate it logically from WriteInt32.
+ return (int) ReadRawVarint32();
+ }
+
+ ///
+ /// Reads an sfixed32 field value from the stream.
+ ///
+ public int ReadSFixed32()
+ {
+ return (int) ReadRawLittleEndian32();
+ }
+
+ ///
+ /// Reads an sfixed64 field value from the stream.
+ ///
+ public long ReadSFixed64()
+ {
+ return (long) ReadRawLittleEndian64();
+ }
+
+ ///
+ /// Reads an sint32 field value from the stream.
+ ///
+ public int ReadSInt32()
+ {
+ return ParsingPrimitives.DecodeZigZag32(ReadRawVarint32());
+ }
+
+ ///
+ /// Reads an sint64 field value from the stream.
+ ///
+ public long ReadSInt64()
+ {
+ return ParsingPrimitives.DecodeZigZag64(ReadRawVarint64());
+ }
+
+ ///
+ /// Reads a length for length-delimited data.
+ ///
+ ///
+ /// This is internally just reading a varint, but this method exists
+ /// to make the calling code clearer.
+ ///
+ public int ReadLength()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseLength(ref span, ref state);
+ }
+
+ ///
+ /// Peeks at the next tag in the stream. If it matches ,
+ /// the tag is consumed and the method returns true; otherwise, the
+ /// stream is left in the original position and the method returns false.
+ ///
+ public bool MaybeConsumeTag(uint tag)
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.MaybeConsumeTag(ref span, ref state, tag);
+ }
+
+#endregion
+
+ #region Underlying reading primitives
+
+ ///
+ /// Reads a raw Varint from the stream. If larger than 32 bits, discard the upper bits.
+ /// This method is optimised for the case where we've got lots of data in the buffer.
+ /// That means we can check the size just once, then just read directly from the buffer
+ /// without constant rechecking of the buffer length.
+ ///
+ internal uint ReadRawVarint32()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseRawVarint32(ref span, ref state);
+ }
+
+ ///
+ /// Reads a varint from the input one byte at a time, so that it does not
+ /// read any bytes after the end of the varint. If you simply wrapped the
+ /// stream in a CodedInputStream and used ReadRawVarint32(Stream)
+ /// then you would probably end up reading past the end of the varint since
+ /// CodedInputStream buffers its input.
+ ///
+ ///
+ ///
+ internal static uint ReadRawVarint32(Stream input)
+ {
+ return ParsingPrimitives.ReadRawVarint32(input);
+ }
+
+ ///
+ /// Reads a raw varint from the stream.
+ ///
+ internal ulong ReadRawVarint64()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseRawVarint64(ref span, ref state);
+ }
+
+ ///
+ /// Reads a 32-bit little-endian integer from the stream.
+ ///
+ internal uint ReadRawLittleEndian32()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseRawLittleEndian32(ref span, ref state);
+ }
+
+ ///
+ /// Reads a 64-bit little-endian integer from the stream.
+ ///
+ internal ulong ReadRawLittleEndian64()
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ParseRawLittleEndian64(ref span, ref state);
+ }
+ #endregion
+
+ #region Internal reading and buffer management
+
+ ///
+ /// Sets currentLimit to (current position) + byteLimit. This is called
+ /// when descending into a length-delimited embedded message. The previous
+ /// limit is returned.
+ ///
+ /// The old limit.
+ internal int PushLimit(int byteLimit)
+ {
+ return SegmentedBufferHelper.PushLimit(ref state, byteLimit);
+ }
+
+ ///
+ /// Discards the current limit, returning the previous limit.
+ ///
+ internal void PopLimit(int oldLimit)
+ {
+ SegmentedBufferHelper.PopLimit(ref state, oldLimit);
+ }
+
+ ///
+ /// Returns whether or not all the data before the limit has been read.
+ ///
+ ///
+ internal bool ReachedLimit
+ {
+ get
+ {
+ return SegmentedBufferHelper.IsReachedLimit(ref state);
+ }
+ }
+
+ ///
+ /// Returns true if the stream has reached the end of the input. This is the
+ /// case if either the end of the underlying input source has been reached or
+ /// the stream has reached a limit created using PushLimit.
+ ///
+ public bool IsAtEnd
+ {
+ get
+ {
+ var span = new ReadOnlySpan(buffer);
+ return SegmentedBufferHelper.IsAtEnd(ref span, ref state);
+ }
+ }
+
+ ///
+ /// Called when buffer is empty to read more bytes from the
+ /// input. If is true, RefillBuffer() guarantees that
+ /// either there will be at least one byte in the buffer when it returns
+ /// or it will throw an exception. If is false,
+ /// RefillBuffer() returns false if no more bytes were available.
+ ///
+ ///
+ ///
+ private bool RefillBuffer(bool mustSucceed)
+ {
+ var span = new ReadOnlySpan(buffer);
+ return state.segmentedBufferHelper.RefillBuffer(ref span, ref state, mustSucceed);
+ }
+
+ ///
+ /// Reads a fixed size of bytes from the input.
+ ///
+ ///
+ /// the end of the stream or the current limit was reached
+ ///
+ internal byte[] ReadRawBytes(int size)
+ {
+ var span = new ReadOnlySpan(buffer);
+ return ParsingPrimitives.ReadRawBytes(ref span, ref state, size);
+ }
+
+ ///
+ /// Reads a top-level message or a nested message after the limits for this message have been pushed.
+ /// (parser will proceed until the end of the current limit)
+ /// NOTE: this method needs to be public because it's invoked by the generated code - e.g. msg.MergeFrom(CodedInputStream input) method
+ ///
+ public void ReadRawMessage(IMessage message)
+ {
+ ParseContext.Initialize(this, out ParseContext ctx);
+ try
+ {
+ ParsingPrimitivesMessages.ReadRawMessage(ref ctx, message);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+#endregion
+ }
+}
diff --git a/Libs/Google.Protobuf/CodedOutputStream.ComputeSize.cs b/Libs/Google.Protobuf/CodedOutputStream.ComputeSize.cs
new file mode 100644
index 0000000..f6992be
--- /dev/null
+++ b/Libs/Google.Protobuf/CodedOutputStream.ComputeSize.cs
@@ -0,0 +1,308 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace LC.Google.Protobuf
+{
+ // This part of CodedOutputStream provides all the static entry points that are used
+ // by generated code and internally to compute the size of messages prior to being
+ // written to an instance of CodedOutputStream.
+ public sealed partial class CodedOutputStream
+ {
+ private const int LittleEndian64Size = 8;
+ private const int LittleEndian32Size = 4;
+
+ internal const int DoubleSize = LittleEndian64Size;
+ internal const int FloatSize = LittleEndian32Size;
+ internal const int BoolSize = 1;
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// double field, including the tag.
+ ///
+ public static int ComputeDoubleSize(double value)
+ {
+ return DoubleSize;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// float field, including the tag.
+ ///
+ public static int ComputeFloatSize(float value)
+ {
+ return FloatSize;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// uint64 field, including the tag.
+ ///
+ public static int ComputeUInt64Size(ulong value)
+ {
+ return ComputeRawVarint64Size(value);
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// int64 field, including the tag.
+ ///
+ public static int ComputeInt64Size(long value)
+ {
+ return ComputeRawVarint64Size((ulong) value);
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// int32 field, including the tag.
+ ///
+ public static int ComputeInt32Size(int value)
+ {
+ if (value >= 0)
+ {
+ return ComputeRawVarint32Size((uint) value);
+ }
+ else
+ {
+ // Must sign-extend.
+ return 10;
+ }
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// fixed64 field, including the tag.
+ ///
+ public static int ComputeFixed64Size(ulong value)
+ {
+ return LittleEndian64Size;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// fixed32 field, including the tag.
+ ///
+ public static int ComputeFixed32Size(uint value)
+ {
+ return LittleEndian32Size;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// bool field, including the tag.
+ ///
+ public static int ComputeBoolSize(bool value)
+ {
+ return BoolSize;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// string field, including the tag.
+ ///
+ public static int ComputeStringSize(String value)
+ {
+ int byteArraySize = WritingPrimitives.Utf8Encoding.GetByteCount(value);
+ return ComputeLengthSize(byteArraySize) + byteArraySize;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// group field, including the tag.
+ ///
+ public static int ComputeGroupSize(IMessage value)
+ {
+ return value.CalculateSize();
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// embedded message field, including the tag.
+ ///
+ public static int ComputeMessageSize(IMessage value)
+ {
+ int size = value.CalculateSize();
+ return ComputeLengthSize(size) + size;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// bytes field, including the tag.
+ ///
+ public static int ComputeBytesSize(ByteString value)
+ {
+ return ComputeLengthSize(value.Length) + value.Length;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// uint32 field, including the tag.
+ ///
+ public static int ComputeUInt32Size(uint value)
+ {
+ return ComputeRawVarint32Size(value);
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a
+ /// enum field, including the tag. The caller is responsible for
+ /// converting the enum value to its numeric value.
+ ///
+ public static int ComputeEnumSize(int value)
+ {
+ // Currently just a pass-through, but it's nice to separate it logically.
+ return ComputeInt32Size(value);
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// sfixed32 field, including the tag.
+ ///
+ public static int ComputeSFixed32Size(int value)
+ {
+ return LittleEndian32Size;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// sfixed64 field, including the tag.
+ ///
+ public static int ComputeSFixed64Size(long value)
+ {
+ return LittleEndian64Size;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// sint32 field, including the tag.
+ ///
+ public static int ComputeSInt32Size(int value)
+ {
+ return ComputeRawVarint32Size(WritingPrimitives.EncodeZigZag32(value));
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode an
+ /// sint64 field, including the tag.
+ ///
+ public static int ComputeSInt64Size(long value)
+ {
+ return ComputeRawVarint64Size(WritingPrimitives.EncodeZigZag64(value));
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a length,
+ /// as written by .
+ ///
+ public static int ComputeLengthSize(int length)
+ {
+ return ComputeRawVarint32Size((uint) length);
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a varint.
+ ///
+ public static int ComputeRawVarint32Size(uint value)
+ {
+ if ((value & (0xffffffff << 7)) == 0)
+ {
+ return 1;
+ }
+ if ((value & (0xffffffff << 14)) == 0)
+ {
+ return 2;
+ }
+ if ((value & (0xffffffff << 21)) == 0)
+ {
+ return 3;
+ }
+ if ((value & (0xffffffff << 28)) == 0)
+ {
+ return 4;
+ }
+ return 5;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a varint.
+ ///
+ public static int ComputeRawVarint64Size(ulong value)
+ {
+ if ((value & (0xffffffffffffffffL << 7)) == 0)
+ {
+ return 1;
+ }
+ if ((value & (0xffffffffffffffffL << 14)) == 0)
+ {
+ return 2;
+ }
+ if ((value & (0xffffffffffffffffL << 21)) == 0)
+ {
+ return 3;
+ }
+ if ((value & (0xffffffffffffffffL << 28)) == 0)
+ {
+ return 4;
+ }
+ if ((value & (0xffffffffffffffffL << 35)) == 0)
+ {
+ return 5;
+ }
+ if ((value & (0xffffffffffffffffL << 42)) == 0)
+ {
+ return 6;
+ }
+ if ((value & (0xffffffffffffffffL << 49)) == 0)
+ {
+ return 7;
+ }
+ if ((value & (0xffffffffffffffffL << 56)) == 0)
+ {
+ return 8;
+ }
+ if ((value & (0xffffffffffffffffL << 63)) == 0)
+ {
+ return 9;
+ }
+ return 10;
+ }
+
+ ///
+ /// Computes the number of bytes that would be needed to encode a tag.
+ ///
+ public static int ComputeTagSize(int fieldNumber)
+ {
+ return ComputeRawVarint32Size(WireFormat.MakeTag(fieldNumber, 0));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/CodedOutputStream.cs b/Libs/Google.Protobuf/CodedOutputStream.cs
new file mode 100644
index 0000000..e717806
--- /dev/null
+++ b/Libs/Google.Protobuf/CodedOutputStream.cs
@@ -0,0 +1,607 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using LC.Google.Protobuf.Collections;
+using System;
+using System.IO;
+using System.Security;
+using System.Text;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Encodes and writes protocol message fields.
+ ///
+ ///
+ ///
+ /// This class is generally used by generated code to write appropriate
+ /// primitives to the stream. It effectively encapsulates the lowest
+ /// levels of protocol buffer format. Unlike some other implementations,
+ /// this does not include combined "write tag and value" methods. Generated
+ /// code knows the exact byte representations of the tags they're going to write,
+ /// so there's no need to re-encode them each time. Manually-written code calling
+ /// this class should just call one of the WriteTag overloads before each value.
+ ///
+ ///
+ /// Repeated fields and map fields are not handled by this class; use RepeatedField<T>
+ /// and MapField<TKey, TValue> to serialize such fields.
+ ///
+ ///
+ [SecuritySafeCritical]
+ public sealed partial class CodedOutputStream : IDisposable
+ {
+ ///
+ /// The buffer size used by CreateInstance(Stream).
+ ///
+ public static readonly int DefaultBufferSize = 4096;
+
+ private readonly bool leaveOpen;
+ private readonly byte[] buffer;
+ private WriterInternalState state;
+
+ private readonly Stream output;
+
+ #region Construction
+ ///
+ /// Creates a new CodedOutputStream that writes directly to the given
+ /// byte array. If more bytes are written than fit in the array,
+ /// OutOfSpaceException will be thrown.
+ ///
+ public CodedOutputStream(byte[] flatArray) : this(flatArray, 0, flatArray.Length)
+ {
+ }
+
+ ///
+ /// Creates a new CodedOutputStream that writes directly to the given
+ /// byte array slice. If more bytes are written than fit in the array,
+ /// OutOfSpaceException will be thrown.
+ ///
+ private CodedOutputStream(byte[] buffer, int offset, int length)
+ {
+ this.output = null;
+ this.buffer = ProtoPreconditions.CheckNotNull(buffer, nameof(buffer));
+ this.state.position = offset;
+ this.state.limit = offset + length;
+ WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
+ leaveOpen = true; // Simple way of avoiding trying to dispose of a null reference
+ }
+
+ private CodedOutputStream(Stream output, byte[] buffer, bool leaveOpen)
+ {
+ this.output = ProtoPreconditions.CheckNotNull(output, nameof(output));
+ this.buffer = buffer;
+ this.state.position = 0;
+ this.state.limit = buffer.Length;
+ WriteBufferHelper.Initialize(this, out this.state.writeBufferHelper);
+ this.leaveOpen = leaveOpen;
+ }
+
+ ///
+ /// Creates a new which write to the given stream, and disposes of that
+ /// stream when the returned CodedOutputStream is disposed.
+ ///
+ /// The stream to write to. It will be disposed when the returned CodedOutputStream is disposed.
+ public CodedOutputStream(Stream output) : this(output, DefaultBufferSize, false)
+ {
+ }
+
+ ///
+ /// Creates a new CodedOutputStream which write to the given stream and uses
+ /// the specified buffer size.
+ ///
+ /// The stream to write to. It will be disposed when the returned CodedOutputStream is disposed.
+ /// The size of buffer to use internally.
+ public CodedOutputStream(Stream output, int bufferSize) : this(output, new byte[bufferSize], false)
+ {
+ }
+
+ ///
+ /// Creates a new CodedOutputStream which write to the given stream.
+ ///
+ /// The stream to write to.
+ /// If true, is left open when the returned CodedOutputStream is disposed;
+ /// if false, the provided stream is disposed as well.
+ public CodedOutputStream(Stream output, bool leaveOpen) : this(output, DefaultBufferSize, leaveOpen)
+ {
+ }
+
+ ///
+ /// Creates a new CodedOutputStream which write to the given stream and uses
+ /// the specified buffer size.
+ ///
+ /// The stream to write to.
+ /// The size of buffer to use internally.
+ /// If true, is left open when the returned CodedOutputStream is disposed;
+ /// if false, the provided stream is disposed as well.
+ public CodedOutputStream(Stream output, int bufferSize, bool leaveOpen) : this(output, new byte[bufferSize], leaveOpen)
+ {
+ }
+ #endregion
+
+ ///
+ /// Returns the current position in the stream, or the position in the output buffer
+ ///
+ public long Position
+ {
+ get
+ {
+ if (output != null)
+ {
+ return output.Position + state.position;
+ }
+ return state.position;
+ }
+ }
+
+ #region Writing of values (not including tags)
+
+ ///
+ /// Writes a double field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteDouble(double value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteDouble(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a float field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteFloat(float value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteFloat(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a uint64 field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteUInt64(ulong value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteUInt64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an int64 field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteInt64(long value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteInt64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an int32 field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteInt32(int value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteInt32(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a fixed64 field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteFixed64(ulong value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteFixed64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a fixed32 field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteFixed32(uint value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteFixed32(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a bool field value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteBool(bool value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteBool(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a string field value, without a tag, to the stream.
+ /// The data is length-prefixed.
+ ///
+ /// The value to write
+ public void WriteString(string value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteString(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a message, without a tag, to the stream.
+ /// The data is length-prefixed.
+ ///
+ /// The value to write
+ public void WriteMessage(IMessage value)
+ {
+ // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+ // what we're doing here works fine, but could be more efficient.
+ // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+ var span = new Span(buffer);
+ WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+ try
+ {
+ WritingPrimitivesMessages.WriteMessage(ref ctx, value);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+
+ ///
+ /// Writes a message, without a tag, to the stream.
+ /// Only the message data is written, without a length-delimiter.
+ ///
+ /// The value to write
+ public void WriteRawMessage(IMessage value)
+ {
+ // TODO(jtattermusch): if the message doesn't implement IBufferMessage (and thus does not provide the InternalWriteTo method),
+ // what we're doing here works fine, but could be more efficient.
+ // For now, this inefficiency is fine, considering this is only a backward-compatibility scenario (and regenerating the code fixes it).
+ var span = new Span(buffer);
+ WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+ try
+ {
+ WritingPrimitivesMessages.WriteRawMessage(ref ctx, value);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+
+ ///
+ /// Writes a group, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteGroup(IMessage value)
+ {
+ var span = new Span(buffer);
+ WriteContext.Initialize(ref span, ref state, out WriteContext ctx);
+ try
+ {
+ WritingPrimitivesMessages.WriteGroup(ref ctx, value);
+ }
+ finally
+ {
+ ctx.CopyStateTo(this);
+ }
+ }
+
+ ///
+ /// Write a byte string, without a tag, to the stream.
+ /// The data is length-prefixed.
+ ///
+ /// The value to write
+ public void WriteBytes(ByteString value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteBytes(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a uint32 value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteUInt32(uint value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteUInt32(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an enum value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteEnum(int value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteEnum(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an sfixed32 value, without a tag, to the stream.
+ ///
+ /// The value to write.
+ public void WriteSFixed32(int value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteSFixed32(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an sfixed64 value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteSFixed64(long value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteSFixed64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an sint32 value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteSInt32(int value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteSInt32(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes an sint64 value, without a tag, to the stream.
+ ///
+ /// The value to write
+ public void WriteSInt64(long value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteSInt64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes a length (in bytes) for length-delimited data.
+ ///
+ ///
+ /// This method simply writes a rawint, but exists for clarity in calling code.
+ ///
+ /// Length value, in bytes.
+ public void WriteLength(int length)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteLength(ref span, ref state, length);
+ }
+
+ #endregion
+
+ #region Raw tag writing
+ ///
+ /// Encodes and writes a tag.
+ ///
+ /// The number of the field to write the tag for
+ /// The wire format type of the tag to write
+ public void WriteTag(int fieldNumber, WireFormat.WireType type)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteTag(ref span, ref state, fieldNumber, type);
+ }
+
+ ///
+ /// Writes an already-encoded tag.
+ ///
+ /// The encoded tag
+ public void WriteTag(uint tag)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteTag(ref span, ref state, tag);
+ }
+
+ ///
+ /// Writes the given single-byte tag directly to the stream.
+ ///
+ /// The encoded tag
+ public void WriteRawTag(byte b1)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawTag(ref span, ref state, b1);
+ }
+
+ ///
+ /// Writes the given two-byte tag directly to the stream.
+ ///
+ /// The first byte of the encoded tag
+ /// The second byte of the encoded tag
+ public void WriteRawTag(byte b1, byte b2)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2);
+ }
+
+ ///
+ /// Writes the given three-byte tag directly to the stream.
+ ///
+ /// The first byte of the encoded tag
+ /// The second byte of the encoded tag
+ /// The third byte of the encoded tag
+ public void WriteRawTag(byte b1, byte b2, byte b3)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3);
+ }
+
+ ///
+ /// Writes the given four-byte tag directly to the stream.
+ ///
+ /// The first byte of the encoded tag
+ /// The second byte of the encoded tag
+ /// The third byte of the encoded tag
+ /// The fourth byte of the encoded tag
+ public void WriteRawTag(byte b1, byte b2, byte b3, byte b4)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4);
+ }
+
+ ///
+ /// Writes the given five-byte tag directly to the stream.
+ ///
+ /// The first byte of the encoded tag
+ /// The second byte of the encoded tag
+ /// The third byte of the encoded tag
+ /// The fourth byte of the encoded tag
+ /// The fifth byte of the encoded tag
+ public void WriteRawTag(byte b1, byte b2, byte b3, byte b4, byte b5)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawTag(ref span, ref state, b1, b2, b3, b4, b5);
+ }
+ #endregion
+
+ #region Underlying writing primitives
+
+ ///
+ /// Writes a 32 bit value as a varint. The fast route is taken when
+ /// there's enough buffer space left to whizz through without checking
+ /// for each byte; otherwise, we resort to calling WriteRawByte each time.
+ ///
+ internal void WriteRawVarint32(uint value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawVarint32(ref span, ref state, value);
+ }
+
+ internal void WriteRawVarint64(ulong value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawVarint64(ref span, ref state, value);
+ }
+
+ internal void WriteRawLittleEndian32(uint value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawLittleEndian32(ref span, ref state, value);
+ }
+
+ internal void WriteRawLittleEndian64(ulong value)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawLittleEndian64(ref span, ref state, value);
+ }
+
+ ///
+ /// Writes out an array of bytes.
+ ///
+ internal void WriteRawBytes(byte[] value)
+ {
+ WriteRawBytes(value, 0, value.Length);
+ }
+
+ ///
+ /// Writes out part of an array of bytes.
+ ///
+ internal void WriteRawBytes(byte[] value, int offset, int length)
+ {
+ var span = new Span(buffer);
+ WritingPrimitives.WriteRawBytes(ref span, ref state, value, offset, length);
+ }
+
+ #endregion
+
+ ///
+ /// Indicates that a CodedOutputStream wrapping a flat byte array
+ /// ran out of space.
+ ///
+ public sealed class OutOfSpaceException : IOException
+ {
+ internal OutOfSpaceException()
+ : base("CodedOutputStream was writing to a flat byte array and ran out of space.")
+ {
+ }
+ }
+
+ ///
+ /// Flushes any buffered data and optionally closes the underlying stream, if any.
+ ///
+ ///
+ ///
+ /// By default, any underlying stream is closed by this method. To configure this behaviour,
+ /// use a constructor overload with a leaveOpen parameter. If this instance does not
+ /// have an underlying stream, this method does nothing.
+ ///
+ ///
+ /// For the sake of efficiency, calling this method does not prevent future write calls - but
+ /// if a later write ends up writing to a stream which has been disposed, that is likely to
+ /// fail. It is recommend that you not call any other methods after this.
+ ///
+ ///
+ public void Dispose()
+ {
+ Flush();
+ if (!leaveOpen)
+ {
+ output.Dispose();
+ }
+ }
+
+ ///
+ /// Flushes any buffered data to the underlying stream (if there is one).
+ ///
+ public void Flush()
+ {
+ var span = new Span(buffer);
+ WriteBufferHelper.Flush(ref span, ref state);
+ }
+
+ ///
+ /// Verifies that SpaceLeft returns zero. It's common to create a byte array
+ /// that is exactly big enough to hold a message, then write to it with
+ /// a CodedOutputStream. Calling CheckNoSpaceLeft after writing verifies that
+ /// the message was actually as big as expected, which can help finding bugs.
+ ///
+ public void CheckNoSpaceLeft()
+ {
+ WriteBufferHelper.CheckNoSpaceLeft(ref state);
+ }
+
+ ///
+ /// If writing to a flat array, returns the space left in the array. Otherwise,
+ /// throws an InvalidOperationException.
+ ///
+ public int SpaceLeft => WriteBufferHelper.GetSpaceLeft(ref state);
+
+ internal byte[] InternalBuffer => buffer;
+
+ internal Stream InternalOutputStream => output;
+
+ internal ref WriterInternalState InternalState => ref state;
+ }
+}
diff --git a/Libs/Google.Protobuf/Collections/Lists.cs b/Libs/Google.Protobuf/Collections/Lists.cs
new file mode 100644
index 0000000..5790294
--- /dev/null
+++ b/Libs/Google.Protobuf/Collections/Lists.cs
@@ -0,0 +1,89 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+
+namespace LC.Google.Protobuf.Collections
+{
+ ///
+ /// Utility to compare if two Lists are the same, and the hash code
+ /// of a List.
+ ///
+ public static class Lists
+ {
+ ///
+ /// Checks if two lists are equal.
+ ///
+ public static bool Equals(List left, List right)
+ {
+ if (left == right)
+ {
+ return true;
+ }
+ if (left == null || right == null)
+ {
+ return false;
+ }
+ if (left.Count != right.Count)
+ {
+ return false;
+ }
+ IEqualityComparer comparer = EqualityComparer.Default;
+ for (int i = 0; i < left.Count; i++)
+ {
+ if (!comparer.Equals(left[i], right[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Gets the list's hash code.
+ ///
+ public static int GetHashCode(List list)
+ {
+ if (list == null)
+ {
+ return 0;
+ }
+ int hash = 31;
+ foreach (T element in list)
+ {
+ hash = hash * 29 + element.GetHashCode();
+ }
+ return hash;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/Collections/MapField.cs b/Libs/Google.Protobuf/Collections/MapField.cs
new file mode 100644
index 0000000..07ada24
--- /dev/null
+++ b/Libs/Google.Protobuf/Collections/MapField.cs
@@ -0,0 +1,762 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using LC.Google.Protobuf.Compatibility;
+using LC.Google.Protobuf.Reflection;
+using System;
+using System.Buffers;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Security;
+
+namespace LC.Google.Protobuf.Collections
+{
+ ///
+ /// Representation of a map field in a Protocol Buffer message.
+ ///
+ /// Key type in the map. Must be a type supported by Protocol Buffer map keys.
+ /// Value type in the map. Must be a type supported by Protocol Buffers.
+ ///
+ ///
+ /// For string keys, the equality comparison is provided by .
+ ///
+ ///
+ /// Null values are not permitted in the map, either for wrapper types or regular messages.
+ /// If a map is deserialized from a data stream and the value is missing from an entry, a default value
+ /// is created instead. For primitive types, that is the regular default value (0, the empty string and so
+ /// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
+ /// encoded value for the field.
+ ///
+ ///
+ /// This implementation does not generally prohibit the use of key/value types which are not
+ /// supported by Protocol Buffers (e.g. using a key type of byte
) but nor does it guarantee
+ /// that all operations will work in such cases.
+ ///
+ ///
+ /// The order in which entries are returned when iterating over this object is undefined, and may change
+ /// in future versions.
+ ///
+ ///
+ public sealed class MapField : IDeepCloneable>, IDictionary, IEquatable>, IDictionary
+#if !NET35
+ , IReadOnlyDictionary
+#endif
+ {
+ private static readonly EqualityComparer ValueEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer();
+ private static readonly EqualityComparer KeyEqualityComparer = ProtobufEqualityComparers.GetEqualityComparer();
+
+ // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
+ private readonly Dictionary>> map =
+ new Dictionary>>(KeyEqualityComparer);
+ private readonly LinkedList> list = new LinkedList>();
+
+ ///
+ /// Creates a deep clone of this object.
+ ///
+ ///
+ /// A deep clone of this object.
+ ///
+ public MapField Clone()
+ {
+ var clone = new MapField();
+ // Keys are never cloneable. Values might be.
+ if (typeof(IDeepCloneable).IsAssignableFrom(typeof(TValue)))
+ {
+ foreach (var pair in list)
+ {
+ clone.Add(pair.Key, ((IDeepCloneable)pair.Value).Clone());
+ }
+ }
+ else
+ {
+ // Nothing is cloneable, so we don't need to worry.
+ clone.Add(this);
+ }
+ return clone;
+ }
+
+ ///
+ /// Adds the specified key/value pair to the map.
+ ///
+ ///
+ /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.
+ ///
+ /// The key to add
+ /// The value to add.
+ /// The given key already exists in map.
+ public void Add(TKey key, TValue value)
+ {
+ // Validation of arguments happens in ContainsKey and the indexer
+ if (ContainsKey(key))
+ {
+ throw new ArgumentException("Key already exists in map", nameof(key));
+ }
+ this[key] = value;
+ }
+
+ ///
+ /// Determines whether the specified key is present in the map.
+ ///
+ /// The key to check.
+ /// true if the map contains the given key; false otherwise.
+ public bool ContainsKey(TKey key)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+ return map.ContainsKey(key);
+ }
+
+ private bool ContainsValue(TValue value) =>
+ list.Any(pair => ValueEqualityComparer.Equals(pair.Value, value));
+
+ ///
+ /// Removes the entry identified by the given key from the map.
+ ///
+ /// The key indicating the entry to remove from the map.
+ /// true if the map contained the given key before the entry was removed; false otherwise.
+ public bool Remove(TKey key)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+ LinkedListNode> node;
+ if (map.TryGetValue(key, out node))
+ {
+ map.Remove(key);
+ node.List.Remove(node);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the value associated with the specified key.
+ ///
+ /// The key whose value to get.
+ /// When this method returns, the value associated with the specified key, if the key is found;
+ /// otherwise, the default value for the type of the parameter.
+ /// This parameter is passed uninitialized.
+ /// true if the map contains an element with the specified key; otherwise, false.
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ LinkedListNode> node;
+ if (map.TryGetValue(key, out node))
+ {
+ value = node.Value.Value;
+ return true;
+ }
+ else
+ {
+ value = default(TValue);
+ return false;
+ }
+ }
+
+ ///
+ /// Gets or sets the value associated with the specified key.
+ ///
+ /// The key of the value to get or set.
+ /// The property is retrieved and key does not exist in the collection.
+ /// The value associated with the specified key. If the specified key is not found,
+ /// a get operation throws a , and a set operation creates a new element with the specified key.
+ public TValue this[TKey key]
+ {
+ get
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+ TValue value;
+ if (TryGetValue(key, out value))
+ {
+ return value;
+ }
+ throw new KeyNotFoundException();
+ }
+ set
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(key, nameof(key));
+ // value == null check here is redundant, but avoids boxing.
+ if (value == null)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+ }
+ LinkedListNode> node;
+ var pair = new KeyValuePair(key, value);
+ if (map.TryGetValue(key, out node))
+ {
+ node.Value = pair;
+ }
+ else
+ {
+ node = list.AddLast(pair);
+ map[key] = node;
+ }
+ }
+ }
+
+ ///
+ /// Gets a collection containing the keys in the map.
+ ///
+ public ICollection Keys { get { return new MapView(this, pair => pair.Key, ContainsKey); } }
+
+ ///
+ /// Gets a collection containing the values in the map.
+ ///
+ public ICollection Values { get { return new MapView(this, pair => pair.Value, ContainsValue); } }
+
+ ///
+ /// Adds the specified entries to the map. The keys and values are not automatically cloned.
+ ///
+ /// The entries to add to the map.
+ public void Add(IDictionary entries)
+ {
+ ProtoPreconditions.CheckNotNull(entries, nameof(entries));
+ foreach (var pair in entries)
+ {
+ Add(pair.Key, pair.Value);
+ }
+ }
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ ///
+ /// An enumerator that can be used to iterate through the collection.
+ ///
+ public IEnumerator> GetEnumerator()
+ {
+ return list.GetEnumerator();
+ }
+
+ ///
+ /// Returns an enumerator that iterates through a collection.
+ ///
+ ///
+ /// An object that can be used to iterate through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Adds the specified item to the map.
+ ///
+ /// The item to add to the map.
+ void ICollection>.Add(KeyValuePair item)
+ {
+ Add(item.Key, item.Value);
+ }
+
+ ///
+ /// Removes all items from the map.
+ ///
+ public void Clear()
+ {
+ list.Clear();
+ map.Clear();
+ }
+
+ ///
+ /// Determines whether map contains an entry equivalent to the given key/value pair.
+ ///
+ /// The key/value pair to find.
+ ///
+ bool ICollection>.Contains(KeyValuePair item)
+ {
+ TValue value;
+ return TryGetValue(item.Key, out value) && ValueEqualityComparer.Equals(item.Value, value);
+ }
+
+ ///
+ /// Copies the key/value pairs in this map to an array.
+ ///
+ /// The array to copy the entries into.
+ /// The index of the array at which to start copying values.
+ void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ list.CopyTo(array, arrayIndex);
+ }
+
+ ///
+ /// Removes the specified key/value pair from the map.
+ ///
+ /// Both the key and the value must be found for the entry to be removed.
+ /// The key/value pair to remove.
+ /// true if the key/value pair was found and removed; false otherwise.
+ bool ICollection>.Remove(KeyValuePair item)
+ {
+ if (item.Key == null)
+ {
+ throw new ArgumentException("Key is null", nameof(item));
+ }
+ LinkedListNode> node;
+ if (map.TryGetValue(item.Key, out node) &&
+ EqualityComparer.Default.Equals(item.Value, node.Value.Value))
+ {
+ map.Remove(item.Key);
+ node.List.Remove(node);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the number of elements contained in the map.
+ ///
+ public int Count { get { return list.Count; } }
+
+ ///
+ /// Gets a value indicating whether the map is read-only.
+ ///
+ public bool IsReadOnly { get { return false; } }
+
+ ///
+ /// Determines whether the specified , is equal to this instance.
+ ///
+ /// The to compare with this instance.
+ ///
+ /// true if the specified is equal to this instance; otherwise, false.
+ ///
+ public override bool Equals(object other)
+ {
+ return Equals(other as MapField);
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ var keyComparer = KeyEqualityComparer;
+ var valueComparer = ValueEqualityComparer;
+ int hash = 0;
+ foreach (var pair in list)
+ {
+ hash ^= keyComparer.GetHashCode(pair.Key) * 31 + valueComparer.GetHashCode(pair.Value);
+ }
+ return hash;
+ }
+
+ ///
+ /// Compares this map with another for equality.
+ ///
+ ///
+ /// The order of the key/value pairs in the maps is not deemed significant in this comparison.
+ ///
+ /// The map to compare this with.
+ /// true if refers to an equal map; false otherwise.
+ public bool Equals(MapField other)
+ {
+ if (other == null)
+ {
+ return false;
+ }
+ if (other == this)
+ {
+ return true;
+ }
+ if (other.Count != this.Count)
+ {
+ return false;
+ }
+ var valueComparer = ValueEqualityComparer;
+ foreach (var pair in this)
+ {
+ TValue value;
+ if (!other.TryGetValue(pair.Key, out value))
+ {
+ return false;
+ }
+ if (!valueComparer.Equals(value, pair.Value))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Adds entries to the map from the given stream.
+ ///
+ ///
+ /// It is assumed that the stream is initially positioned after the tag specified by the codec.
+ /// This method will continue reading entries from the stream until the end is reached, or
+ /// a different tag is encountered.
+ ///
+ /// Stream to read from
+ /// Codec describing how the key/value pairs are encoded
+ public void AddEntriesFrom(CodedInputStream input, Codec codec)
+ {
+ ParseContext.Initialize(input, out ParseContext ctx);
+ try
+ {
+ AddEntriesFrom(ref ctx, codec);
+ }
+ finally
+ {
+ ctx.CopyStateTo(input);
+ }
+ }
+
+ ///
+ /// Adds entries to the map from the given parse context.
+ ///
+ ///
+ /// It is assumed that the input is initially positioned after the tag specified by the codec.
+ /// This method will continue reading entries from the input until the end is reached, or
+ /// a different tag is encountered.
+ ///
+ /// Input to read from
+ /// Codec describing how the key/value pairs are encoded
+ [SecuritySafeCritical]
+ public void AddEntriesFrom(ref ParseContext ctx, Codec codec)
+ {
+ do
+ {
+ KeyValuePair entry = ParsingPrimitivesMessages.ReadMapEntry(ref ctx, codec);
+ this[entry.Key] = entry.Value;
+ } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, codec.MapTag));
+ }
+
+ ///
+ /// Writes the contents of this map to the given coded output stream, using the specified codec
+ /// to encode each entry.
+ ///
+ /// The output stream to write to.
+ /// The codec to use for each entry.
+ public void WriteTo(CodedOutputStream output, Codec codec)
+ {
+ WriteContext.Initialize(output, out WriteContext ctx);
+ try
+ {
+ WriteTo(ref ctx, codec);
+ }
+ finally
+ {
+ ctx.CopyStateTo(output);
+ }
+ }
+
+ ///
+ /// Writes the contents of this map to the given write context, using the specified codec
+ /// to encode each entry.
+ ///
+ /// The write context to write to.
+ /// The codec to use for each entry.
+ [SecuritySafeCritical]
+ public void WriteTo(ref WriteContext ctx, Codec codec)
+ {
+ foreach (var entry in list)
+ {
+ ctx.WriteTag(codec.MapTag);
+
+ WritingPrimitives.WriteLength(ref ctx.buffer, ref ctx.state, CalculateEntrySize(codec, entry));
+ codec.KeyCodec.WriteTagAndValue(ref ctx, entry.Key);
+ codec.ValueCodec.WriteTagAndValue(ref ctx, entry.Value);
+ }
+ }
+
+ ///
+ /// Calculates the size of this map based on the given entry codec.
+ ///
+ /// The codec to use to encode each entry.
+ ///
+ public int CalculateSize(Codec codec)
+ {
+ if (Count == 0)
+ {
+ return 0;
+ }
+ int size = 0;
+ foreach (var entry in list)
+ {
+ int entrySize = CalculateEntrySize(codec, entry);
+
+ size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
+ size += CodedOutputStream.ComputeLengthSize(entrySize) + entrySize;
+ }
+ return size;
+ }
+
+ private static int CalculateEntrySize(Codec codec, KeyValuePair entry)
+ {
+ return codec.KeyCodec.CalculateSizeWithTag(entry.Key) + codec.ValueCodec.CalculateSizeWithTag(entry.Value);
+ }
+
+ ///
+ /// Returns a string representation of this repeated field, in the same
+ /// way as it would be represented by the default JSON formatter.
+ ///
+ public override string ToString()
+ {
+ var writer = new StringWriter();
+ JsonFormatter.Default.WriteDictionary(writer, this);
+ return writer.ToString();
+ }
+
+ #region IDictionary explicit interface implementation
+ void IDictionary.Add(object key, object value)
+ {
+ Add((TKey)key, (TValue)value);
+ }
+
+ bool IDictionary.Contains(object key)
+ {
+ if (!(key is TKey))
+ {
+ return false;
+ }
+ return ContainsKey((TKey)key);
+ }
+
+ IDictionaryEnumerator IDictionary.GetEnumerator()
+ {
+ return new DictionaryEnumerator(GetEnumerator());
+ }
+
+ void IDictionary.Remove(object key)
+ {
+ ProtoPreconditions.CheckNotNull(key, nameof(key));
+ if (!(key is TKey))
+ {
+ return;
+ }
+ Remove((TKey)key);
+ }
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ // This is ugly and slow as heck, but with any luck it will never be used anyway.
+ ICollection temp = this.Select(pair => new DictionaryEntry(pair.Key, pair.Value)).ToList();
+ temp.CopyTo(array, index);
+ }
+
+ bool IDictionary.IsFixedSize { get { return false; } }
+
+ ICollection IDictionary.Keys { get { return (ICollection)Keys; } }
+
+ ICollection IDictionary.Values { get { return (ICollection)Values; } }
+
+ bool ICollection.IsSynchronized { get { return false; } }
+
+ object ICollection.SyncRoot { get { return this; } }
+
+ object IDictionary.this[object key]
+ {
+ get
+ {
+ ProtoPreconditions.CheckNotNull(key, nameof(key));
+ if (!(key is TKey))
+ {
+ return null;
+ }
+ TValue value;
+ TryGetValue((TKey)key, out value);
+ return value;
+ }
+
+ set
+ {
+ this[(TKey)key] = (TValue)value;
+ }
+ }
+ #endregion
+
+ #region IReadOnlyDictionary explicit interface implementation
+#if !NET35
+ IEnumerable IReadOnlyDictionary.Keys => Keys;
+
+ IEnumerable IReadOnlyDictionary.Values => Values;
+#endif
+ #endregion
+
+ private class DictionaryEnumerator : IDictionaryEnumerator
+ {
+ private readonly IEnumerator> enumerator;
+
+ internal DictionaryEnumerator(IEnumerator> enumerator)
+ {
+ this.enumerator = enumerator;
+ }
+
+ public bool MoveNext()
+ {
+ return enumerator.MoveNext();
+ }
+
+ public void Reset()
+ {
+ enumerator.Reset();
+ }
+
+ public object Current { get { return Entry; } }
+ public DictionaryEntry Entry { get { return new DictionaryEntry(Key, Value); } }
+ public object Key { get { return enumerator.Current.Key; } }
+ public object Value { get { return enumerator.Current.Value; } }
+ }
+
+ ///
+ /// A codec for a specific map field. This contains all the information required to encode and
+ /// decode the nested messages.
+ ///
+ public sealed class Codec
+ {
+ private readonly FieldCodec keyCodec;
+ private readonly FieldCodec valueCodec;
+ private readonly uint mapTag;
+
+ ///
+ /// Creates a new entry codec based on a separate key codec and value codec,
+ /// and the tag to use for each map entry.
+ ///
+ /// The key codec.
+ /// The value codec.
+ /// The map tag to use to introduce each map entry.
+ public Codec(FieldCodec keyCodec, FieldCodec valueCodec, uint mapTag)
+ {
+ this.keyCodec = keyCodec;
+ this.valueCodec = valueCodec;
+ this.mapTag = mapTag;
+ }
+
+ ///
+ /// The key codec.
+ ///
+ internal FieldCodec KeyCodec => keyCodec;
+
+ ///
+ /// The value codec.
+ ///
+ internal FieldCodec ValueCodec => valueCodec;
+
+ ///
+ /// The tag used in the enclosing message to indicate map entries.
+ ///
+ internal uint MapTag => mapTag;
+ }
+
+ private class MapView : ICollection, ICollection
+ {
+ private readonly MapField parent;
+ private readonly Func, T> projection;
+ private readonly Func containsCheck;
+
+ internal MapView(
+ MapField parent,
+ Func, T> projection,
+ Func containsCheck)
+ {
+ this.parent = parent;
+ this.projection = projection;
+ this.containsCheck = containsCheck;
+ }
+
+ public int Count { get { return parent.Count; } }
+
+ public bool IsReadOnly { get { return true; } }
+
+ public bool IsSynchronized { get { return false; } }
+
+ public object SyncRoot { get { return parent; } }
+
+ public void Add(T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ public void Clear()
+ {
+ throw new NotSupportedException();
+ }
+
+ public bool Contains(T item)
+ {
+ return containsCheck(item);
+ }
+
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ if (arrayIndex < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+ }
+ if (arrayIndex + Count > array.Length)
+ {
+ throw new ArgumentException("Not enough space in the array", nameof(array));
+ }
+ foreach (var item in this)
+ {
+ array[arrayIndex++] = item;
+ }
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ return parent.list.Select(projection).GetEnumerator();
+ }
+
+ public bool Remove(T item)
+ {
+ throw new NotSupportedException();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ public void CopyTo(Array array, int index)
+ {
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ if (index + Count > array.Length)
+ {
+ throw new ArgumentException("Not enough space in the array", nameof(array));
+ }
+ foreach (var item in this)
+ {
+ array.SetValue(item, index++);
+ }
+ }
+ }
+ }
+}
diff --git a/Libs/Google.Protobuf/Collections/ProtobufEqualityComparers.cs b/Libs/Google.Protobuf/Collections/ProtobufEqualityComparers.cs
new file mode 100644
index 0000000..830b0f5
--- /dev/null
+++ b/Libs/Google.Protobuf/Collections/ProtobufEqualityComparers.cs
@@ -0,0 +1,130 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+namespace LC.Google.Protobuf.Collections
+{
+ ///
+ /// Provides a central place to implement equality comparisons, primarily for bitwise float/double equality.
+ ///
+ public static class ProtobufEqualityComparers
+ {
+ ///
+ /// Returns an equality comparer for suitable for Protobuf equality comparisons.
+ /// This is usually just the default equality comparer for the type, but floating point numbers are compared
+ /// bitwise.
+ ///
+ /// The type of equality comparer to return.
+ /// The equality comparer.
+ public static EqualityComparer GetEqualityComparer()
+ {
+ return typeof(T) == typeof(double) ? (EqualityComparer) (object) BitwiseDoubleEqualityComparer
+ : typeof(T) == typeof(float) ? (EqualityComparer) (object) BitwiseSingleEqualityComparer
+ : typeof(T) == typeof(double?) ? (EqualityComparer) (object) BitwiseNullableDoubleEqualityComparer
+ : typeof(T) == typeof(float?) ? (EqualityComparer) (object) BitwiseNullableSingleEqualityComparer
+ : EqualityComparer.Default;
+ }
+
+ ///
+ /// Returns an equality comparer suitable for comparing 64-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ ///
+ public static EqualityComparer BitwiseDoubleEqualityComparer { get; } = new BitwiseDoubleEqualityComparerImpl();
+
+ ///
+ /// Returns an equality comparer suitable for comparing 32-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ ///
+ public static EqualityComparer BitwiseSingleEqualityComparer { get; } = new BitwiseSingleEqualityComparerImpl();
+
+ ///
+ /// Returns an equality comparer suitable for comparing nullable 64-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ ///
+ public static EqualityComparer BitwiseNullableDoubleEqualityComparer { get; } = new BitwiseNullableDoubleEqualityComparerImpl();
+
+ ///
+ /// Returns an equality comparer suitable for comparing nullable 32-bit floating point values, by bitwise comparison.
+ /// (NaN values are considered equal, but only when they have the same representation.)
+ ///
+ public static EqualityComparer BitwiseNullableSingleEqualityComparer { get; } = new BitwiseNullableSingleEqualityComparerImpl();
+
+ private class BitwiseDoubleEqualityComparerImpl : EqualityComparer
+ {
+ public override bool Equals(double x, double y) =>
+ BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y);
+
+ public override int GetHashCode(double obj) =>
+ BitConverter.DoubleToInt64Bits(obj).GetHashCode();
+ }
+
+ private class BitwiseSingleEqualityComparerImpl : EqualityComparer
+ {
+ // Just promote values to double and use BitConverter.DoubleToInt64Bits,
+ // as there's no BitConverter.SingleToInt32Bits, unfortunately.
+
+ public override bool Equals(float x, float y) =>
+ BitConverter.DoubleToInt64Bits(x) == BitConverter.DoubleToInt64Bits(y);
+
+ public override int GetHashCode(float obj) =>
+ BitConverter.DoubleToInt64Bits(obj).GetHashCode();
+ }
+
+ private class BitwiseNullableDoubleEqualityComparerImpl : EqualityComparer
+ {
+ public override bool Equals(double? x, double? y) =>
+ x == null && y == null ? true
+ : x == null || y == null ? false
+ : BitwiseDoubleEqualityComparer.Equals(x.Value, y.Value);
+
+ // The hash code for null is just a constant which is at least *unlikely* to be used
+ // elsewhere. (Compared with 0, say.)
+ public override int GetHashCode(double? obj) =>
+ obj == null ? 293864 : BitwiseDoubleEqualityComparer.GetHashCode(obj.Value);
+ }
+
+ private class BitwiseNullableSingleEqualityComparerImpl : EqualityComparer
+ {
+ public override bool Equals(float? x, float? y) =>
+ x == null && y == null ? true
+ : x == null || y == null ? false
+ : BitwiseSingleEqualityComparer.Equals(x.Value, y.Value);
+
+ // The hash code for null is just a constant which is at least *unlikely* to be used
+ // elsewhere. (Compared with 0, say.)
+ public override int GetHashCode(float? obj) =>
+ obj == null ? 293864 : BitwiseSingleEqualityComparer.GetHashCode(obj.Value);
+ }
+ }
+}
diff --git a/Libs/Google.Protobuf/Collections/ReadOnlyDictionary.cs b/Libs/Google.Protobuf/Collections/ReadOnlyDictionary.cs
new file mode 100644
index 0000000..e589b89
--- /dev/null
+++ b/Libs/Google.Protobuf/Collections/ReadOnlyDictionary.cs
@@ -0,0 +1,147 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace LC.Google.Protobuf.Collections
+{
+ ///
+ /// Read-only wrapper around another dictionary.
+ ///
+ internal sealed class ReadOnlyDictionary : IDictionary
+ {
+ private readonly IDictionary wrapped;
+
+ public ReadOnlyDictionary(IDictionary wrapped)
+ {
+ this.wrapped = wrapped;
+ }
+
+ public void Add(TKey key, TValue value)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool ContainsKey(TKey key)
+ {
+ return wrapped.ContainsKey(key);
+ }
+
+ public ICollection Keys
+ {
+ get { return wrapped.Keys; }
+ }
+
+ public bool Remove(TKey key)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool TryGetValue(TKey key, out TValue value)
+ {
+ return wrapped.TryGetValue(key, out value);
+ }
+
+ public ICollection Values
+ {
+ get { return wrapped.Values; }
+ }
+
+ public TValue this[TKey key]
+ {
+ get { return wrapped[key]; }
+ set { throw new InvalidOperationException(); }
+ }
+
+ public void Add(KeyValuePair item)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public void Clear()
+ {
+ throw new InvalidOperationException();
+ }
+
+ public bool Contains(KeyValuePair item)
+ {
+ return wrapped.Contains(item);
+ }
+
+ public void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ wrapped.CopyTo(array, arrayIndex);
+ }
+
+ public int Count
+ {
+ get { return wrapped.Count; }
+ }
+
+ public bool IsReadOnly
+ {
+ get { return true; }
+ }
+
+ public bool Remove(KeyValuePair item)
+ {
+ throw new InvalidOperationException();
+ }
+
+ public IEnumerator> GetEnumerator()
+ {
+ return wrapped.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return ((IEnumerable) wrapped).GetEnumerator();
+ }
+
+ public override bool Equals(object obj)
+ {
+ return wrapped.Equals(obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return wrapped.GetHashCode();
+ }
+
+ public override string ToString()
+ {
+ return wrapped.ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Libs/Google.Protobuf/Collections/RepeatedField.cs b/Libs/Google.Protobuf/Collections/RepeatedField.cs
new file mode 100644
index 0000000..30e2e57
--- /dev/null
+++ b/Libs/Google.Protobuf/Collections/RepeatedField.cs
@@ -0,0 +1,698 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Security;
+using System.Threading;
+
+namespace LC.Google.Protobuf.Collections
+{
+ ///
+ /// The contents of a repeated field: essentially, a collection with some extra
+ /// restrictions (no null values) and capabilities (deep cloning).
+ ///
+ ///
+ /// This implementation does not generally prohibit the use of types which are not
+ /// supported by Protocol Buffers but nor does it guarantee that all operations will work in such cases.
+ ///
+ /// The element type of the repeated field.
+ public sealed class RepeatedField : IList, IList, IDeepCloneable>, IEquatable>
+#if !NET35
+ , IReadOnlyList
+#endif
+ {
+ private static readonly EqualityComparer EqualityComparer = ProtobufEqualityComparers.GetEqualityComparer();
+ private static readonly T[] EmptyArray = new T[0];
+ private const int MinArraySize = 8;
+
+ private T[] array = EmptyArray;
+ private int count = 0;
+
+ ///
+ /// Creates a deep clone of this repeated field.
+ ///
+ ///
+ /// If the field type is
+ /// a message type, each element is also cloned; otherwise, it is
+ /// assumed that the field type is primitive (including string and
+ /// bytes, both of which are immutable) and so a simple copy is
+ /// equivalent to a deep clone.
+ ///
+ /// A deep clone of this repeated field.
+ public RepeatedField Clone()
+ {
+ RepeatedField clone = new RepeatedField();
+ if (array != EmptyArray)
+ {
+ clone.array = (T[])array.Clone();
+ IDeepCloneable[] cloneableArray = clone.array as IDeepCloneable[];
+ if (cloneableArray != null)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ clone.array[i] = cloneableArray[i].Clone();
+ }
+ }
+ }
+ clone.count = count;
+ return clone;
+ }
+
+ ///
+ /// Adds the entries from the given input stream, decoding them with the specified codec.
+ ///
+ /// The input stream to read from.
+ /// The codec to use in order to read each entry.
+ public void AddEntriesFrom(CodedInputStream input, FieldCodec codec)
+ {
+ ParseContext.Initialize(input, out ParseContext ctx);
+ try
+ {
+ AddEntriesFrom(ref ctx, codec);
+ }
+ finally
+ {
+ ctx.CopyStateTo(input);
+ }
+ }
+
+ ///
+ /// Adds the entries from the given parse context, decoding them with the specified codec.
+ ///
+ /// The input to read from.
+ /// The codec to use in order to read each entry.
+ [SecuritySafeCritical]
+ public void AddEntriesFrom(ref ParseContext ctx, FieldCodec codec)
+ {
+ // TODO: Inline some of the Add code, so we can avoid checking the size on every
+ // iteration.
+ uint tag = ctx.state.lastTag;
+ var reader = codec.ValueReader;
+ // Non-nullable value types can be packed or not.
+ if (FieldCodec.IsPackedRepeatedField(tag))
+ {
+ int length = ctx.ReadLength();
+ if (length > 0)
+ {
+ int oldLimit = SegmentedBufferHelper.PushLimit(ref ctx.state, length);
+
+ // If the content is fixed size then we can calculate the length
+ // of the repeated field and pre-initialize the underlying collection.
+ //
+ // Check that the supplied length doesn't exceed the underlying buffer.
+ // That prevents a malicious length from initializing a very large collection.
+ if (codec.FixedSize > 0 && length % codec.FixedSize == 0 && ParsingPrimitives.IsDataAvailable(ref ctx.state, length))
+ {
+ EnsureSize(count + (length / codec.FixedSize));
+
+ while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
+ {
+ // Only FieldCodecs with a fixed size can reach here, and they are all known
+ // types that don't allow the user to specify a custom reader action.
+ // reader action will never return null.
+ array[count++] = reader(ref ctx);
+ }
+ }
+ else
+ {
+ // Content is variable size so add until we reach the limit.
+ while (!SegmentedBufferHelper.IsReachedLimit(ref ctx.state))
+ {
+ Add(reader(ref ctx));
+ }
+ }
+ SegmentedBufferHelper.PopLimit(ref ctx.state, oldLimit);
+ }
+ // Empty packed field. Odd, but valid - just ignore.
+ }
+ else
+ {
+ // Not packed... (possibly not packable)
+ do
+ {
+ Add(reader(ref ctx));
+ } while (ParsingPrimitives.MaybeConsumeTag(ref ctx.buffer, ref ctx.state, tag));
+ }
+ }
+
+ ///
+ /// Calculates the size of this collection based on the given codec.
+ ///
+ /// The codec to use when encoding each field.
+ /// The number of bytes that would be written to an output by one of the WriteTo methods,
+ /// using the same codec.
+ public int CalculateSize(FieldCodec codec)
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+ uint tag = codec.Tag;
+ if (codec.PackedRepeatedField)
+ {
+ int dataSize = CalculatePackedDataSize(codec);
+ return CodedOutputStream.ComputeRawVarint32Size(tag) +
+ CodedOutputStream.ComputeLengthSize(dataSize) +
+ dataSize;
+ }
+ else
+ {
+ var sizeCalculator = codec.ValueSizeCalculator;
+ int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
+ if (codec.EndTag != 0)
+ {
+ size += count * CodedOutputStream.ComputeRawVarint32Size(codec.EndTag);
+ }
+ for (int i = 0; i < count; i++)
+ {
+ size += sizeCalculator(array[i]);
+ }
+ return size;
+ }
+ }
+
+ private int CalculatePackedDataSize(FieldCodec codec)
+ {
+ int fixedSize = codec.FixedSize;
+ if (fixedSize == 0)
+ {
+ var calculator = codec.ValueSizeCalculator;
+ int tmp = 0;
+ for (int i = 0; i < count; i++)
+ {
+ tmp += calculator(array[i]);
+ }
+ return tmp;
+ }
+ else
+ {
+ return fixedSize * Count;
+ }
+ }
+
+ ///
+ /// Writes the contents of this collection to the given ,
+ /// encoding each value using the specified codec.
+ ///
+ /// The output stream to write to.
+ /// The codec to use when encoding each value.
+ public void WriteTo(CodedOutputStream output, FieldCodec codec)
+ {
+ WriteContext.Initialize(output, out WriteContext ctx);
+ try
+ {
+ WriteTo(ref ctx, codec);
+ }
+ finally
+ {
+ ctx.CopyStateTo(output);
+ }
+ }
+
+ ///
+ /// Writes the contents of this collection to the given write context,
+ /// encoding each value using the specified codec.
+ ///
+ /// The write context to write to.
+ /// The codec to use when encoding each value.
+ [SecuritySafeCritical]
+ public void WriteTo(ref WriteContext ctx, FieldCodec codec)
+ {
+ if (count == 0)
+ {
+ return;
+ }
+ var writer = codec.ValueWriter;
+ var tag = codec.Tag;
+ if (codec.PackedRepeatedField)
+ {
+ // Packed primitive type
+ int size = CalculatePackedDataSize(codec);
+ ctx.WriteTag(tag);
+ ctx.WriteLength(size);
+ for (int i = 0; i < count; i++)
+ {
+ writer(ref ctx, array[i]);
+ }
+ }
+ else
+ {
+ // Not packed: a simple tag/value pair for each value.
+ // Can't use codec.WriteTagAndValue, as that omits default values.
+ for (int i = 0; i < count; i++)
+ {
+ ctx.WriteTag(tag);
+ writer(ref ctx, array[i]);
+ if (codec.EndTag != 0)
+ {
+ ctx.WriteTag(codec.EndTag);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Gets and sets the capacity of the RepeatedField's internal array. WHen set, the internal array is reallocated to the given capacity.
+ /// The new value is less than Count -or- when Count is less than 0.
+ ///
+ public int Capacity
+ {
+ get { return array.Length; }
+ set
+ {
+ if (value < count)
+ {
+ throw new ArgumentOutOfRangeException("Capacity", value,
+ $"Cannot set Capacity to a value smaller than the current item count, {count}");
+ }
+
+ if (value >= 0 && value != array.Length)
+ {
+ SetSize(value);
+ }
+ }
+ }
+
+ // May increase the size of the internal array, but will never shrink it.
+ private void EnsureSize(int size)
+ {
+ if (array.Length < size)
+ {
+ size = Math.Max(size, MinArraySize);
+ int newSize = Math.Max(array.Length * 2, size);
+ SetSize(newSize);
+ }
+ }
+
+ // Sets the internal array to an exact size.
+ private void SetSize(int size)
+ {
+ if (size != array.Length)
+ {
+ var tmp = new T[size];
+ Array.Copy(array, 0, tmp, 0, count);
+ array = tmp;
+ }
+ }
+
+ ///
+ /// Adds the specified item to the collection.
+ ///
+ /// The item to add.
+ public void Add(T item)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+ EnsureSize(count + 1);
+ array[count++] = item;
+ }
+
+ ///
+ /// Removes all items from the collection.
+ ///
+ public void Clear()
+ {
+ array = EmptyArray;
+ count = 0;
+ }
+
+ ///
+ /// Determines whether this collection contains the given item.
+ ///
+ /// The item to find.
+ /// true if this collection contains the given item; false otherwise.
+ public bool Contains(T item)
+ {
+ return IndexOf(item) != -1;
+ }
+
+ ///
+ /// Copies this collection to the given array.
+ ///
+ /// The array to copy to.
+ /// The first index of the array to copy to.
+ public void CopyTo(T[] array, int arrayIndex)
+ {
+ Array.Copy(this.array, 0, array, arrayIndex, count);
+ }
+
+ ///
+ /// Removes the specified item from the collection
+ ///
+ /// The item to remove.
+ /// true if the item was found and removed; false otherwise.
+ public bool Remove(T item)
+ {
+ int index = IndexOf(item);
+ if (index == -1)
+ {
+ return false;
+ }
+ Array.Copy(array, index + 1, array, index, count - index - 1);
+ count--;
+ array[count] = default(T);
+ return true;
+ }
+
+ ///
+ /// Gets the number of elements contained in the collection.
+ ///
+ public int Count => count;
+
+ ///
+ /// Gets a value indicating whether the collection is read-only.
+ ///
+ public bool IsReadOnly => false;
+
+ ///
+ /// Adds all of the specified values into this collection.
+ ///
+ /// The values to add to this collection.
+ public void AddRange(IEnumerable values)
+ {
+ ProtoPreconditions.CheckNotNull(values, nameof(values));
+
+ // Optimization 1: If the collection we're adding is already a RepeatedField,
+ // we know the values are valid.
+ var otherRepeatedField = values as RepeatedField;
+ if (otherRepeatedField != null)
+ {
+ EnsureSize(count + otherRepeatedField.count);
+ Array.Copy(otherRepeatedField.array, 0, array, count, otherRepeatedField.count);
+ count += otherRepeatedField.count;
+ return;
+ }
+
+ // Optimization 2: The collection is an ICollection, so we can expand
+ // just once and ask the collection to copy itself into the array.
+ var collection = values as ICollection;
+ if (collection != null)
+ {
+ var extraCount = collection.Count;
+ // For reference types and nullable value types, we need to check that there are no nulls
+ // present. (This isn't a thread-safe approach, but we don't advertise this is thread-safe.)
+ // We expect the JITter to optimize this test to true/false, so it's effectively conditional
+ // specialization.
+ if (default(T) == null)
+ {
+ // TODO: Measure whether iterating once to check and then letting the collection copy
+ // itself is faster or slower than iterating and adding as we go. For large
+ // collections this will not be great in terms of cache usage... but the optimized
+ // copy may be significantly faster than doing it one at a time.
+ foreach (var item in collection)
+ {
+ if (item == null)
+ {
+ throw new ArgumentException("Sequence contained null element", nameof(values));
+ }
+ }
+ }
+ EnsureSize(count + extraCount);
+ collection.CopyTo(array, count);
+ count += extraCount;
+ return;
+ }
+
+ // We *could* check for ICollection as well, but very very few collections implement
+ // ICollection but not ICollection. (HashSet does, for one...)
+
+ // Fall back to a slower path of adding items one at a time.
+ foreach (T item in values)
+ {
+ Add(item);
+ }
+ }
+
+ ///
+ /// Adds all of the specified values into this collection. This method is present to
+ /// allow repeated fields to be constructed from queries within collection initializers.
+ /// Within non-collection-initializer code, consider using the equivalent
+ /// method instead for clarity.
+ ///
+ /// The values to add to this collection.
+ public void Add(IEnumerable values)
+ {
+ AddRange(values);
+ }
+
+ ///
+ /// Returns an enumerator that iterates through the collection.
+ ///
+ ///
+ /// An enumerator that can be used to iterate through the collection.
+ ///
+ public IEnumerator GetEnumerator()
+ {
+ for (int i = 0; i < count; i++)
+ {
+ yield return array[i];
+ }
+ }
+
+ ///
+ /// Determines whether the specified , is equal to this instance.
+ ///
+ /// The to compare with this instance.
+ ///
+ /// true if the specified is equal to this instance; otherwise, false.
+ ///
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as RepeatedField);
+ }
+
+ ///
+ /// Returns an enumerator that iterates through a collection.
+ ///
+ ///
+ /// An object that can be used to iterate through the collection.
+ ///
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ ///
+ public override int GetHashCode()
+ {
+ int hash = 0;
+ for (int i = 0; i < count; i++)
+ {
+ hash = hash * 31 + array[i].GetHashCode();
+ }
+ return hash;
+ }
+
+ ///
+ /// Compares this repeated field with another for equality.
+ ///
+ /// The repeated field to compare this with.
+ /// true if refers to an equal repeated field; false otherwise.
+ public bool Equals(RepeatedField other)
+ {
+ if (ReferenceEquals(other, null))
+ {
+ return false;
+ }
+ if (ReferenceEquals(other, this))
+ {
+ return true;
+ }
+ if (other.Count != this.Count)
+ {
+ return false;
+ }
+ EqualityComparer comparer = EqualityComparer;
+ for (int i = 0; i < count; i++)
+ {
+ if (!comparer.Equals(array[i], other.array[i]))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Returns the index of the given item within the collection, or -1 if the item is not
+ /// present.
+ ///
+ /// The item to find in the collection.
+ /// The zero-based index of the item, or -1 if it is not found.
+ public int IndexOf(T item)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+ EqualityComparer comparer = EqualityComparer;
+ for (int i = 0; i < count; i++)
+ {
+ if (comparer.Equals(array[i], item))
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ ///
+ /// Inserts the given item at the specified index.
+ ///
+ /// The index at which to insert the item.
+ /// The item to insert.
+ public void Insert(int index, T item)
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(item, nameof(item));
+ if (index < 0 || index > count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ EnsureSize(count + 1);
+ Array.Copy(array, index, array, index + 1, count - index);
+ array[index] = item;
+ count++;
+ }
+
+ ///
+ /// Removes the item at the given index.
+ ///
+ /// The zero-based index of the item to remove.
+ public void RemoveAt(int index)
+ {
+ if (index < 0 || index >= count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ Array.Copy(array, index + 1, array, index, count - index - 1);
+ count--;
+ array[count] = default(T);
+ }
+
+ ///
+ /// Returns a string representation of this repeated field, in the same
+ /// way as it would be represented by the default JSON formatter.
+ ///
+ public override string ToString()
+ {
+ var writer = new StringWriter();
+ JsonFormatter.Default.WriteList(writer, this);
+ return writer.ToString();
+ }
+
+ ///
+ /// Gets or sets the item at the specified index.
+ ///
+ ///
+ /// The element at the specified index.
+ ///
+ /// The zero-based index of the element to get or set.
+ /// The item at the specified index.
+ public T this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ return array[index];
+ }
+ set
+ {
+ if (index < 0 || index >= count)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+ ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+ array[index] = value;
+ }
+ }
+
+ #region Explicit interface implementation for IList and ICollection.
+ bool IList.IsFixedSize => false;
+
+ void ICollection.CopyTo(Array array, int index)
+ {
+ Array.Copy(this.array, 0, array, index, count);
+ }
+
+ bool ICollection.IsSynchronized => false;
+
+ object ICollection.SyncRoot => this;
+
+ object IList.this[int index]
+ {
+ get { return this[index]; }
+ set { this[index] = (T)value; }
+ }
+
+ int IList.Add(object value)
+ {
+ Add((T) value);
+ return count - 1;
+ }
+
+ bool IList.Contains(object value)
+ {
+ return (value is T && Contains((T)value));
+ }
+
+ int IList.IndexOf(object value)
+ {
+ if (!(value is T))
+ {
+ return -1;
+ }
+ return IndexOf((T)value);
+ }
+
+ void IList.Insert(int index, object value)
+ {
+ Insert(index, (T) value);
+ }
+
+ void IList.Remove(object value)
+ {
+ if (!(value is T))
+ {
+ return;
+ }
+ Remove((T)value);
+ }
+ #endregion
+ }
+}
diff --git a/Libs/Google.Protobuf/Compatibility/MethodInfoExtensions.cs b/Libs/Google.Protobuf/Compatibility/MethodInfoExtensions.cs
new file mode 100644
index 0000000..0fe73ee
--- /dev/null
+++ b/Libs/Google.Protobuf/Compatibility/MethodInfoExtensions.cs
@@ -0,0 +1,47 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2017 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+#if NET35
+using System;
+using System.Reflection;
+
+namespace LC.Google.Protobuf.Compatibility
+{
+ // .NET Core (at least netstandard1.0) doesn't have Delegate.CreateDelegate, and .NET 3.5 doesn't have
+ // MethodInfo.CreateDelegate. Proxy from one to the other on .NET 3.5...
+ internal static class MethodInfoExtensions
+ {
+ internal static Delegate CreateDelegate(this MethodInfo method, Type type) =>
+ Delegate.CreateDelegate(type, method);
+ }
+}
+#endif
diff --git a/Libs/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs b/Libs/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs
new file mode 100644
index 0000000..aa4c4dd
--- /dev/null
+++ b/Libs/Google.Protobuf/Compatibility/PropertyInfoExtensions.cs
@@ -0,0 +1,72 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System.Reflection;
+
+namespace LC.Google.Protobuf.Compatibility
+{
+ ///
+ /// Extension methods for , effectively providing
+ /// the familiar members from previous desktop framework versions while
+ /// targeting the newer releases, .NET Core etc.
+ ///
+ internal static class PropertyInfoExtensions
+ {
+ ///
+ /// Returns the public getter of a property, or null if there is no such getter
+ /// (either because it's read-only, or the getter isn't public).
+ ///
+ internal static MethodInfo GetGetMethod(this PropertyInfo target)
+ {
+#if NET35
+ var method = target.GetGetMethod();
+#else
+ var method = target.GetMethod;
+#endif
+ return method != null && method.IsPublic ? method : null;
+ }
+
+ ///
+ /// Returns the public setter of a property, or null if there is no such setter
+ /// (either because it's write-only, or the setter isn't public).
+ ///
+ internal static MethodInfo GetSetMethod(this PropertyInfo target)
+ {
+#if NET35
+ var method = target.GetSetMethod();
+#else
+ var method = target.SetMethod;
+#endif
+ return method != null && method.IsPublic ? method : null;
+ }
+ }
+}
diff --git a/Libs/Google.Protobuf/Compatibility/StreamExtensions.cs b/Libs/Google.Protobuf/Compatibility/StreamExtensions.cs
new file mode 100644
index 0000000..f475c94
--- /dev/null
+++ b/Libs/Google.Protobuf/Compatibility/StreamExtensions.cs
@@ -0,0 +1,66 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+#if NET35
+using System;
+using System.IO;
+
+namespace LC.Google.Protobuf.Compatibility
+{
+ ///
+ /// Extension methods for in order to provide
+ /// backwards compatibility with .NET 3.5
+ ///
+ public static class StreamExtensions
+ {
+ // 81920 seems to be the default buffer size used in .NET 4.5.1
+ private const int BUFFER_SIZE = 81920;
+
+ ///
+ /// Write the contents of the current stream to the destination stream
+ ///
+ public static void CopyTo(this Stream source, Stream destination)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int numBytesRead;
+ while ((numBytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
+ destination.Write(buffer, 0, numBytesRead);
+ }
+ }
+ }
+}
+#endif
diff --git a/Libs/Google.Protobuf/Compatibility/TypeExtensions.cs b/Libs/Google.Protobuf/Compatibility/TypeExtensions.cs
new file mode 100644
index 0000000..be67850
--- /dev/null
+++ b/Libs/Google.Protobuf/Compatibility/TypeExtensions.cs
@@ -0,0 +1,106 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Reflection;
+
+#if !NET35
+namespace LC.Google.Protobuf.Compatibility
+{
+ ///
+ /// Provides extension methods on Type that just proxy to TypeInfo.
+ /// These are used to support the new type system from .NET 4.5, without
+ /// having calls to GetTypeInfo all over the place. While the methods here are meant to be
+ /// broadly compatible with the desktop framework, there are some subtle differences in behaviour - but
+ /// they're not expected to affect our use cases. While the class is internal, that should be fine: we can
+ /// evaluate each new use appropriately.
+ ///
+ internal static class TypeExtensions
+ {
+ ///
+ /// See https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom
+ ///
+ internal static bool IsAssignableFrom(this Type target, Type c)
+ {
+ return target.GetTypeInfo().IsAssignableFrom(c.GetTypeInfo());
+ }
+
+ ///
+ /// Returns a representation of the public property associated with the given name in the given type,
+ /// including inherited properties or null if there is no such public property.
+ /// Here, "public property" means a property where either the getter, or the setter, or both, is public.
+ ///
+ internal static PropertyInfo GetProperty(this Type target, string name)
+ {
+ // GetDeclaredProperty only returns properties declared in the given type, so we need to recurse.
+ while (target != null)
+ {
+ var typeInfo = target.GetTypeInfo();
+ var ret = typeInfo.GetDeclaredProperty(name);
+ if (ret != null && ((ret.CanRead && ret.GetMethod.IsPublic) || (ret.CanWrite && ret.SetMethod.IsPublic)))
+ {
+ return ret;
+ }
+ target = typeInfo.BaseType;
+ }
+ return null;
+ }
+
+ ///
+ /// Returns a representation of the public method associated with the given name in the given type,
+ /// including inherited methods.
+ ///
+ ///
+ /// This has a few differences compared with Type.GetMethod in the desktop framework. It will throw
+ /// if there is an ambiguous match even between a private method and a public one, but it *won't* throw
+ /// if there are two overloads at different levels in the type hierarchy (e.g. class Base declares public void Foo(int) and
+ /// class Child : Base declares public void Foo(long)).
+ ///
+ /// One type in the hierarchy declared more than one method with the same name
+ internal static MethodInfo GetMethod(this Type target, string name)
+ {
+ // GetDeclaredMethod only returns methods declared in the given type, so we need to recurse.
+ while (target != null)
+ {
+ var typeInfo = target.GetTypeInfo();
+ var ret = typeInfo.GetDeclaredMethod(name);
+ if (ret != null && ret.IsPublic)
+ {
+ return ret;
+ }
+ target = typeInfo.BaseType;
+ }
+ return null;
+ }
+ }
+}
+#endif
diff --git a/Libs/Google.Protobuf/Extension.cs b/Libs/Google.Protobuf/Extension.cs
new file mode 100644
index 0000000..8feee04
--- /dev/null
+++ b/Libs/Google.Protobuf/Extension.cs
@@ -0,0 +1,119 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Represents a non-generic extension definition. This API is experimental and subject to change.
+ ///
+ public abstract class Extension
+ {
+ internal abstract Type TargetType { get; }
+
+ ///
+ /// Internal use. Creates a new extension with the specified field number.
+ ///
+ protected Extension(int fieldNumber)
+ {
+ FieldNumber = fieldNumber;
+ }
+
+ internal abstract IExtensionValue CreateValue();
+
+ ///
+ /// Gets the field number of this extension
+ ///
+ public int FieldNumber { get; }
+
+ internal abstract bool IsRepeated { get; }
+ }
+
+ ///
+ /// Represents a type-safe extension identifier used for getting and setting single extension values in instances.
+ /// This API is experimental and subject to change.
+ ///
+ /// The message type this field applies to
+ /// The field value type of this extension
+ public sealed class Extension : Extension where TTarget : IExtendableMessage
+ {
+ private readonly FieldCodec codec;
+
+ ///
+ /// Creates a new extension identifier with the specified field number and codec
+ ///
+ public Extension(int fieldNumber, FieldCodec codec) : base(fieldNumber)
+ {
+ this.codec = codec;
+ }
+
+ internal TValue DefaultValue => codec.DefaultValue;
+
+ internal override Type TargetType => typeof(TTarget);
+
+ internal override bool IsRepeated => false;
+
+ internal override IExtensionValue CreateValue()
+ {
+ return new ExtensionValue(codec);
+ }
+ }
+
+ ///
+ /// Represents a type-safe extension identifier used for getting repeated extension values in instances.
+ /// This API is experimental and subject to change.
+ ///
+ /// The message type this field applies to
+ /// The repeated field value type of this extension
+ public sealed class RepeatedExtension : Extension where TTarget : IExtendableMessage
+ {
+ private readonly FieldCodec codec;
+
+ ///
+ /// Creates a new repeated extension identifier with the specified field number and codec
+ ///
+ public RepeatedExtension(int fieldNumber, FieldCodec codec) : base(fieldNumber)
+ {
+ this.codec = codec;
+ }
+
+ internal override Type TargetType => typeof(TTarget);
+
+ internal override bool IsRepeated => true;
+
+ internal override IExtensionValue CreateValue()
+ {
+ return new RepeatedExtensionValue(codec);
+ }
+ }
+}
diff --git a/Libs/Google.Protobuf/ExtensionRegistry.cs b/Libs/Google.Protobuf/ExtensionRegistry.cs
new file mode 100644
index 0000000..44667db
--- /dev/null
+++ b/Libs/Google.Protobuf/ExtensionRegistry.cs
@@ -0,0 +1,184 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Provides extensions to messages while parsing. This API is experimental and subject to change.
+ ///
+ public sealed class ExtensionRegistry : ICollection, IDeepCloneable
+ {
+ internal sealed class ExtensionComparer : IEqualityComparer
+ {
+ public bool Equals(Extension a, Extension b)
+ {
+ return new ObjectIntPair(a.TargetType, a.FieldNumber).Equals(new ObjectIntPair(b.TargetType, b.FieldNumber));
+ }
+ public int GetHashCode(Extension a)
+ {
+ return new ObjectIntPair(a.TargetType, a.FieldNumber).GetHashCode();
+ }
+
+ internal static ExtensionComparer Instance = new ExtensionComparer();
+ }
+ private IDictionary, Extension> extensions;
+
+ ///
+ /// Creates a new empty extension registry
+ ///
+ public ExtensionRegistry()
+ {
+ extensions = new Dictionary, Extension>();
+ }
+
+ private ExtensionRegistry(IDictionary, Extension> collection)
+ {
+ extensions = collection.ToDictionary(k => k.Key, v => v.Value);
+ }
+
+ ///
+ /// Gets the total number of extensions in this extension registry
+ ///
+ public int Count => extensions.Count;
+
+ ///
+ /// Returns whether the registry is readonly
+ ///
+ bool ICollection.IsReadOnly => false;
+
+ internal bool ContainsInputField(uint lastTag, Type target, out Extension extension)
+ {
+ return extensions.TryGetValue(new ObjectIntPair(target, WireFormat.GetTagFieldNumber(lastTag)), out extension);
+ }
+
+ ///
+ /// Adds the specified extension to the registry
+ ///
+ public void Add(Extension extension)
+ {
+ ProtoPreconditions.CheckNotNull(extension, nameof(extension));
+
+ extensions.Add(new ObjectIntPair(extension.TargetType, extension.FieldNumber), extension);
+ }
+
+ ///
+ /// Adds the specified extensions to the registry
+ ///
+ public void AddRange(IEnumerable extensions)
+ {
+ ProtoPreconditions.CheckNotNull(extensions, nameof(extensions));
+
+ foreach (var extension in extensions)
+ {
+ Add(extension);
+ }
+ }
+
+ ///
+ /// Clears the registry of all values
+ ///
+ public void Clear()
+ {
+ extensions.Clear();
+ }
+
+ ///
+ /// Gets whether the extension registry contains the specified extension
+ ///
+ public bool Contains(Extension item)
+ {
+ ProtoPreconditions.CheckNotNull(item, nameof(item));
+
+ return extensions.ContainsKey(new ObjectIntPair(item.TargetType, item.FieldNumber));
+ }
+
+ ///
+ /// Copies the arrays in the registry set to the specified array at the specified index
+ ///
+ /// The array to copy to
+ /// The array index to start at
+ void ICollection.CopyTo(Extension[] array, int arrayIndex)
+ {
+ ProtoPreconditions.CheckNotNull(array, nameof(array));
+ if (arrayIndex < 0 || arrayIndex >= array.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+ }
+ if (array.Length - arrayIndex < Count)
+ {
+ throw new ArgumentException("The provided array is shorter than the number of elements in the registry");
+ }
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ Extension extension = array[i];
+ extensions.Add(new ObjectIntPair(extension.TargetType, extension.FieldNumber), extension);
+ }
+ }
+
+ ///
+ /// Returns an enumerator to enumerate through the items in the registry
+ ///
+ /// Returns an enumerator for the extensions in this registry
+ public IEnumerator GetEnumerator()
+ {
+ return extensions.Values.GetEnumerator();
+ }
+
+ ///
+ /// Removes the specified extension from the set
+ ///
+ /// The extension
+ /// true if the extension was removed, otherwise false
+ public bool Remove(Extension item)
+ {
+ ProtoPreconditions.CheckNotNull(item, nameof(item));
+
+ return extensions.Remove(new ObjectIntPair(item.TargetType, item.FieldNumber));
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ ///
+ /// Clones the registry into a new registry
+ ///
+ public ExtensionRegistry Clone()
+ {
+ return new ExtensionRegistry(extensions);
+ }
+ }
+}
diff --git a/Libs/Google.Protobuf/ExtensionSet.cs b/Libs/Google.Protobuf/ExtensionSet.cs
new file mode 100644
index 0000000..1f1917b
--- /dev/null
+++ b/Libs/Google.Protobuf/ExtensionSet.cs
@@ -0,0 +1,377 @@
+#region Copyright notice and license
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#endregion
+
+using LC.Google.Protobuf.Collections;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security;
+
+namespace LC.Google.Protobuf
+{
+ ///
+ /// Methods for managing s with null checking.
+ ///
+ /// Most users will not use this class directly and its API is experimental and subject to change.
+ ///
+ public static class ExtensionSet
+ {
+ private static bool TryGetValue(ref ExtensionSet set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage
+ {
+ if (set == null)
+ {
+ value = null;
+ return false;
+ }
+ return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value);
+ }
+
+ ///
+ /// Gets the value of the specified extension
+ ///
+ public static TValue Get(ref ExtensionSet set, Extension extension) where TTarget : IExtendableMessage
+ {
+ IExtensionValue value;
+ if (TryGetValue(ref set, extension, out value))
+ {
+ return ((ExtensionValue)value).GetValue();
+ }
+ else
+ {
+ return extension.DefaultValue;
+ }
+ }
+
+ ///
+ /// Gets the value of the specified repeated extension or null if it doesn't exist in this set
+ ///
+ public static RepeatedField Get(ref ExtensionSet set, RepeatedExtension extension) where TTarget : IExtendableMessage
+ {
+ IExtensionValue value;
+ if (TryGetValue(ref set, extension, out value))
+ {
+ return ((RepeatedExtensionValue)value).GetValue();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the value of the specified repeated extension, registering it if it doesn't exist
+ ///
+ public static RepeatedField GetOrInitialize(ref ExtensionSet set, RepeatedExtension extension) where TTarget : IExtendableMessage
+ {
+ IExtensionValue value;
+ if (set == null)
+ {
+ value = extension.CreateValue();
+ set = new ExtensionSet();
+ set.ValuesByNumber.Add(extension.FieldNumber, value);
+ }
+ else
+ {
+ if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value))
+ {
+ value = extension.CreateValue();
+ set.ValuesByNumber.Add(extension.FieldNumber, value);
+ }
+ }
+
+ return ((RepeatedExtensionValue)value).GetValue();
+ }
+
+ ///
+ /// Sets the value of the specified extension. This will make a new instance of ExtensionSet if the set is null.
+ ///
+ public static void Set(ref ExtensionSet set, Extension extension, TValue value) where TTarget : IExtendableMessage
+ {
+ ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
+
+ IExtensionValue extensionValue;
+ if (set == null)
+ {
+ extensionValue = extension.CreateValue();
+ set = new ExtensionSet();
+ set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);
+ }
+ else
+ {
+ if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out extensionValue))
+ {
+ extensionValue = extension.CreateValue();
+ set.ValuesByNumber.Add(extension.FieldNumber, extensionValue);
+ }
+ }
+
+ ((ExtensionValue)extensionValue).SetValue(value);
+ }
+
+ ///
+ /// Gets whether the value of the specified extension is set
+ ///
+ public static bool Has(ref ExtensionSet set, Extension extension) where TTarget : IExtendableMessage
+ {
+ IExtensionValue value;
+ return TryGetValue(ref set, extension, out value);
+ }
+
+ ///
+ /// Clears the value of the specified extension
+ ///
+ public static void Clear(ref ExtensionSet set, Extension