| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 | #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 System;using System.Collections;using System.Collections.Generic;using System.Linq;namespace Google.Protobuf.Collections{    /// <summary>    /// Representation of a map field in a Protocol Buffer message.    /// </summary>    /// <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>    /// <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>    public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>    {        // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)        private bool frozen;        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>>();        public MapField<TKey, TValue> Clone()        {            var clone = new MapField<TKey, TValue>();            // 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;        }        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;        }        public bool ContainsKey(TKey key)        {            ThrowHelper.ThrowIfNull(key, "key");            return map.ContainsKey(key);        }        public bool Remove(TKey key)        {            this.CheckMutable();            ThrowHelper.ThrowIfNull(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;            }        }        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;            }        }        public TValue this[TKey key]        {            get            {                ThrowHelper.ThrowIfNull(key, "key");                TValue value;                if (TryGetValue(key, out value))                {                    return value;                }                throw new KeyNotFoundException();            }            set            {                ThrowHelper.ThrowIfNull(key, "key");                if (value == null && (typeof(TValue) == typeof(ByteString) || typeof(TValue) == typeof(string)))                {                    ThrowHelper.ThrowIfNull(value, "value");                }                this.CheckMutable();                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?        public ICollection<TKey> Keys { get { return list.Select(t => t.Key).ToList(); } }        public ICollection<TValue> Values { get { return list.Select(t => t.Value).ToList(); } }        public void Add(IDictionary<TKey, TValue> entries)        {            ThrowHelper.ThrowIfNull(entries, "entries");            foreach (var pair in entries)            {                Add(pair.Key, pair.Value);            }        }        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()        {            return list.GetEnumerator();        }        IEnumerator IEnumerable.GetEnumerator()        {            return GetEnumerator();        }        void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)        {            Add(item.Key, item.Value);        }        public void Clear()        {            this.CheckMutable();            list.Clear();            map.Clear();        }        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);        }        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)        {            list.CopyTo(array, arrayIndex);        }        bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)        {            this.CheckMutable();            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;            }        }        public int Count { get { return list.Count; } }        public bool IsReadOnly { get { return frozen; } }        public void Freeze()        {            if (IsFrozen)            {                return;            }            frozen = true;            // Only values can be frozen, as all the key types are simple.            // Everything can be done in-place, as we're just freezing objects.            if (typeof(IFreezable).IsAssignableFrom(typeof(TValue)))            {                for (var node = list.First; node != null; node = node.Next)                {                    var pair = node.Value;                    IFreezable freezableValue = pair.Value as IFreezable;                    if (freezableValue != null)                    {                        freezableValue.Freeze();                    }                }            }        }        public bool IsFrozen { get { return frozen; } }        public override bool Equals(object other)        {            return Equals(other as MapField<TKey, TValue>);        }        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;        }        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));        }                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);            }        }        public int CalculateSize(Codec codec)        {            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;        }        /// <summary>        /// A codec for a specific map field. This contains all the information required to encoded and        /// decode the nested messages.        /// </summary>        public sealed class Codec        {            private readonly FieldCodec<TKey> keyCodec;            private readonly FieldCodec<TValue> valueCodec;            private readonly uint mapTag;            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 (input.ReadTag(out tag))                    {                        if (tag == 0)                        {                            throw InvalidProtocolBufferException.InvalidTag();                        }                        if (tag == codec.keyCodec.Tag)                        {                            Key = codec.keyCodec.Read(input);                        }                        else if (tag == codec.valueCodec.Tag)                        {                            Value = codec.valueCodec.Read(input);                        }                        else if (WireFormat.IsEndGroupTag(tag))                        {                            // TODO(jonskeet): Do we need this? (Given that we don't support groups...)                            return;                        }                    }                }                public void WriteTo(CodedOutputStream output)                {                    codec.keyCodec.Write(output, Key);                    codec.valueCodec.Write(output, Value);                }                public int CalculateSize()                {                    return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);                }            }        }    }}
 |