| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 | #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.#endregionusing Google.Protobuf.Reflection;using System;using System.Collections;using System.Collections.Generic;using System.Linq;using Google.Protobuf.Compatibility;namespace Google.Protobuf.Collections{    /// <summary>    /// Representation of a map field in a Protocol Buffer message.    /// </summary>    /// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>    /// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>    /// <remarks>    /// This implementation preserves insertion order for simplicity of testing    /// code using maps fields. Overwriting an existing entry does not change the    /// position of that entry within the map. Equality is not order-sensitive.    /// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.    /// </remarks>    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary    {        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)        private readonly bool allowNullValues;        private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =            new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();        private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();        /// <summary>        /// Constructs a new map field, defaulting the value nullability to only allow null values for message types        /// and non-nullable value types.        /// </summary>        public MapField() : this(typeof(IMessage).IsAssignableFrom(typeof(TValue)) || Nullable.GetUnderlyingType(typeof(TValue)) != null)        {        }        /// <summary>        /// Constructs a new map field, overriding the choice of whether null values are permitted in the map.        /// This is used by wrapper types, where maps with string and bytes wrappers as the value types        /// support null values.        /// </summary>        /// <param name="allowNullValues">Whether null values are permitted in the map or not.</param>        public MapField(bool allowNullValues)        {            if (allowNullValues && typeof(TValue).IsValueType() && Nullable.GetUnderlyingType(typeof(TValue)) == null)            {                throw new ArgumentException("allowNullValues", "Non-nullable value types do not support null values");            }            this.allowNullValues = allowNullValues;        }        /// <summary>        /// Creates a deep clone of this object.        /// </summary>        /// <returns>        /// A deep clone of this object.        /// </returns>        public MapField<TKey, TValue> Clone()        {            var clone = new MapField<TKey, TValue>(allowNullValues);            // Keys are never cloneable. Values might be.            if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))            {                foreach (var pair in list)                {                    clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone());                }            }            else            {                // Nothing is cloneable, so we don't need to worry.                clone.Add(this);            }            return clone;        }        /// <summary>        /// Adds the specified key/value pair to the map.        /// </summary>        /// <remarks>        /// This operation fails if the key already exists in the map. To replace an existing entry, use the indexer.        /// </remarks>        /// <param name="key">The key to add</param>        /// <param name="value">The value to add.</param>        /// <exception cref="System.ArgumentException">The given key already exists in map.</exception>        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", "key");            }            this[key] = value;        }        /// <summary>        /// Determines whether the specified key is present in the map.        /// </summary>        /// <param name="key">The key to check.</param>        /// <returns><c>true</c> if the map contains the given key; <c>false</c> otherwise.</returns>        public bool ContainsKey(TKey key)        {            Preconditions.CheckNotNullUnconstrained(key, "key");            return map.ContainsKey(key);        }        /// <summary>        /// Removes the entry identified by the given key from the map.        /// </summary>        /// <param name="key">The key indicating the entry to remove from the map.</param>        /// <returns><c>true</c> if the map contained the given key before the entry was removed; <c>false</c> otherwise.</returns>        public bool Remove(TKey key)        {            Preconditions.CheckNotNullUnconstrained(key, "key");            LinkedListNode<KeyValuePair<TKey, TValue>> node;            if (map.TryGetValue(key, out node))            {                map.Remove(key);                node.List.Remove(node);                return true;            }            else            {                return false;            }        }        /// <summary>        /// Gets the value associated with the specified key.        /// </summary>        /// <param name="key">The key whose value to get.</param>        /// <param name="value">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 <paramref name="value"/> parameter.        /// This parameter is passed uninitialized.</param>        /// <returns><c>true</c> if the map contains an element with the specified key; otherwise, <c>false</c>.</returns>        public bool TryGetValue(TKey key, out TValue value)        {            LinkedListNode<KeyValuePair<TKey, TValue>> node;            if (map.TryGetValue(key, out node))            {                value = node.Value.Value;                return true;            }            else            {                value = default(TValue);                return false;            }        }        /// <summary>        /// Gets or sets the value associated with the specified key.        /// </summary>        /// <param name="key">The key of the value to get or set.</param>        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>        /// <returns>The value associated with the specified key. If the specified key is not found,        /// a get operation throws a <see cref="KeyNotFoundException"/>, and a set operation creates a new element with the specified key.</returns>        public TValue this[TKey key]        {            get            {                Preconditions.CheckNotNullUnconstrained(key, "key");                TValue value;                if (TryGetValue(key, out value))                {                    return value;                }                throw new KeyNotFoundException();            }            set            {                Preconditions.CheckNotNullUnconstrained(key, "key");                // value == null check here is redundant, but avoids boxing.                if (value == null && !allowNullValues)                {                    Preconditions.CheckNotNullUnconstrained(value, "value");                }                LinkedListNode<KeyValuePair<TKey, TValue>> node;                var pair = new KeyValuePair<TKey, TValue>(key, value);                if (map.TryGetValue(key, out node))                {                    node.Value = pair;                }                else                {                    node = list.AddLast(pair);                    map[key] = node;                }            }        }        // TODO: Make these views?        /// <summary>        /// Gets a collection containing the keys in the map.        /// </summary>        public ICollection<TKey> Keys { get { return list.Select(t => t.Key).ToList(); } }        /// <summary>        /// Gets a collection containing the values in the map.        /// </summary>        public ICollection<TValue> Values { get { return list.Select(t => t.Value).ToList(); } }        /// <summary>        /// Adds the specified entries to the map.        /// </summary>        /// <param name="entries">The entries to add to the map.</param>        public void Add(IDictionary<TKey, TValue> entries)        {            Preconditions.CheckNotNull(entries, "entries");            foreach (var pair in entries)            {                Add(pair.Key, pair.Value);            }        }        /// <summary>        /// Returns an enumerator that iterates through the collection.        /// </summary>        /// <returns>        /// An enumerator that can be used to iterate through the collection.        /// </returns>        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()        {            return list.GetEnumerator();        }        /// <summary>        /// Returns an enumerator that iterates through a collection.        /// </summary>        /// <returns>        /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.        /// </returns>        IEnumerator IEnumerable.GetEnumerator()        {            return GetEnumerator();        }        /// <summary>        /// Adds the specified item to the map.        /// </summary>        /// <param name="item">The item to add to the map.</param>        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)        {            Add(item.Key, item.Value);        }        /// <summary>        /// Removes all items from the map.        /// </summary>        public void Clear()        {            list.Clear();            map.Clear();        }        /// <summary>        /// Determines whether map contains an entry equivalent to the given key/value pair.        /// </summary>        /// <param name="item">The key/value pair to find.</param>        /// <returns></returns>        bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)        {            TValue value;            return TryGetValue(item.Key, out value)                && EqualityComparer<TValue>.Default.Equals(item.Value, value);        }        /// <summary>        /// Copies the key/value pairs in this map to an array.        /// </summary>        /// <param name="array">The array to copy the entries into.</param>        /// <param name="arrayIndex">The index of the array at which to start copying values.</param>        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)        {            list.CopyTo(array, arrayIndex);        }        /// <summary>        /// Removes the specified key/value pair from the map.        /// </summary>        /// <remarks>Both the key and the value must be found for the entry to be removed.</remarks>        /// <param name="item">The key/value pair to remove.</param>        /// <returns><c>true</c> if the key/value pair was found and removed; <c>false</c> otherwise.</returns>        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)        {            if (item.Key == null)            {                throw new ArgumentException("Key is null", "item");            }            LinkedListNode<KeyValuePair<TKey, TValue>> node;            if (map.TryGetValue(item.Key, out node) &&                EqualityComparer<TValue>.Default.Equals(item.Value, node.Value.Value))            {                map.Remove(item.Key);                node.List.Remove(node);                return true;            }            else            {                return false;            }        }        /// <summary>        /// Returns whether or not this map allows values to be null.        /// </summary>        public bool AllowsNullValues { get { return allowNullValues; } }        /// <summary>        /// Gets the number of elements contained in the map.        /// </summary>        public int Count { get { return list.Count; } }        /// <summary>        /// Gets a value indicating whether the map is read-only.        /// </summary>        public bool IsReadOnly { get { return false; } }        /// <summary>        /// Determines whether the specified <see cref="System.Object" />, is equal to this instance.        /// </summary>        /// <param name="other">The <see cref="System.Object" /> to compare with this instance.</param>        /// <returns>        ///   <c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>false</c>.        /// </returns>        public override bool Equals(object other)        {            return Equals(other as MapField<TKey, TValue>);        }        /// <summary>        /// Returns a hash code for this instance.        /// </summary>        /// <returns>        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.         /// </returns>        public override int GetHashCode()        {            var valueComparer = EqualityComparer<TValue>.Default;            int hash = 0;            foreach (var pair in list)            {                hash ^= pair.Key.GetHashCode() * 31 + valueComparer.GetHashCode(pair.Value);            }            return hash;        }        /// <summary>        /// Compares this map with another for equality.        /// </summary>        /// <remarks>        /// The order of the key/value pairs in the maps is not deemed significant in this comparison.        /// </remarks>        /// <param name="other">The map to compare this with.</param>        /// <returns><c>true</c> if <paramref name="other"/> refers to an equal map; <c>false</c> otherwise.</returns>        public bool Equals(MapField<TKey, TValue> other)        {            if (other == null)            {                return false;            }            if (other == this)            {                return true;            }            if (other.Count != this.Count)            {                return false;            }            var valueComparer = EqualityComparer<TValue>.Default;            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;        }        /// <summary>        /// Adds entries to the map from the given stream.        /// </summary>        /// <remarks>        /// 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.        /// </remarks>        /// <param name="input">Stream to read from</param>        /// <param name="codec">Codec describing how the key/value pairs are encoded</param>        public void AddEntriesFrom(CodedInputStream input, Codec codec)        {            var adapter = new Codec.MessageAdapter(codec);            do            {                adapter.Reset();                input.ReadMessage(adapter);                this[adapter.Key] = adapter.Value;            } while (input.MaybeConsumeTag(codec.MapTag));        }        /// <summary>        /// Writes the contents of this map to the given coded output stream, using the specified codec        /// to encode each entry.        /// </summary>        /// <param name="output">The output stream to write to.</param>        /// <param name="codec">The codec to use for each entry.</param>        public void WriteTo(CodedOutputStream output, Codec codec)        {            var message = new Codec.MessageAdapter(codec);            foreach (var entry in list)            {                message.Key = entry.Key;                message.Value = entry.Value;                output.WriteTag(codec.MapTag);                output.WriteMessage(message);            }        }        /// <summary>        /// Calculates the size of this map based on the given entry codec.        /// </summary>        /// <param name="codec">The codec to use to encode each entry.</param>        /// <returns></returns>        public int CalculateSize(Codec codec)        {            if (Count == 0)            {                return 0;            }            var message = new Codec.MessageAdapter(codec);            int size = 0;            foreach (var entry in list)            {                message.Key = entry.Key;                message.Value = entry.Value;                size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);                size += CodedOutputStream.ComputeMessageSize(message);            }            return size;        }        #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)        {            Preconditions.CheckNotNull(key, "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            {                Preconditions.CheckNotNull(key, "key");                if (!(key is TKey))                {                    return null;                }                TValue value;                TryGetValue((TKey)key, out value);                return value;            }            set            {                this[(TKey)key] = (TValue)value;            }        }        #endregion        private class DictionaryEnumerator : IDictionaryEnumerator        {            private readonly IEnumerator<KeyValuePair<TKey, TValue>> enumerator;            internal DictionaryEnumerator(IEnumerator<KeyValuePair<TKey, TValue>> 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; } }        }        /// <summary>        /// A codec for a specific map field. This contains all the information required to encode and        /// decode the nested messages.        /// </summary>        public sealed class Codec        {            private readonly FieldCodec<TKey> keyCodec;            private readonly FieldCodec<TValue> valueCodec;            private readonly uint mapTag;            /// <summary>            /// Creates a new entry codec based on a separate key codec and value codec,            /// and the tag to use for each map entry.            /// </summary>            /// <param name="keyCodec">The key codec.</param>            /// <param name="valueCodec">The value codec.</param>            /// <param name="mapTag">The map tag to use to introduce each map entry.</param>            public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)            {                this.keyCodec = keyCodec;                this.valueCodec = valueCodec;                this.mapTag = mapTag;            }            /// <summary>            /// The tag used in the enclosing message to indicate map entries.            /// </summary>            internal uint MapTag { get { return mapTag; } }            /// <summary>            /// A mutable message class, used for parsing and serializing. This            /// delegates the work to a codec, but implements the <see cref="IMessage"/> interface            /// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.            /// This is nested inside Codec as it's tightly coupled to the associated codec,            /// and it's simpler if it has direct access to all its fields.            /// </summary>            internal class MessageAdapter : IMessage            {                private readonly Codec codec;                internal TKey Key { get; set; }                internal TValue Value { get; set; }                internal MessageAdapter(Codec codec)                {                    this.codec = codec;                }                internal void Reset()                {                    Key = codec.keyCodec.DefaultValue;                    Value = codec.valueCodec.DefaultValue;                }                public void MergeFrom(CodedInputStream input)                {                    uint tag;                    while ((tag = input.ReadTag()) != 0)                    {                        if (tag == codec.keyCodec.Tag)                        {                            Key = codec.keyCodec.Read(input);                        }                        else if (tag == codec.valueCodec.Tag)                        {                            Value = codec.valueCodec.Read(input);                        }                        else                         {                            input.SkipLastField();                        }                    }                }                public void WriteTo(CodedOutputStream output)                {                    codec.keyCodec.WriteTagAndValue(output, Key);                    codec.valueCodec.WriteTagAndValue(output, Value);                }                public int CalculateSize()                {                    return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);                }                MessageDescriptor IMessage.Descriptor { get { return null; } }            }        }    }}
 |