diff options
author | Jon Skeet <jonskeet@google.com> | 2015-07-17 08:26:04 +0100 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2015-07-17 08:26:04 +0100 |
commit | 59eeebee870332ea2b9085688ba524c69311f662 (patch) | |
tree | 4300e8aeaff6a3b706c398496a692f5ed25f43b7 /csharp/src/ProtocolBuffers/Collections | |
parent | 0f442a7533b1d06ce0092b28f10af481b99e1369 (diff) | |
download | protobuf-59eeebee870332ea2b9085688ba524c69311f662.tar.gz protobuf-59eeebee870332ea2b9085688ba524c69311f662.tar.bz2 protobuf-59eeebee870332ea2b9085688ba524c69311f662.zip |
First pass at the big rename from ProtocolBuffers to Google.Protobuf.
We'll see what I've missed when CI fails...
Diffstat (limited to 'csharp/src/ProtocolBuffers/Collections')
3 files changed, 0 insertions, 1182 deletions
diff --git a/csharp/src/ProtocolBuffers/Collections/MapField.cs b/csharp/src/ProtocolBuffers/Collections/MapField.cs deleted file mode 100644 index f5e4fd3a..00000000 --- a/csharp/src/ProtocolBuffers/Collections/MapField.cs +++ /dev/null @@ -1,565 +0,0 @@ -#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.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>>, IDictionary - { - // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.) - private readonly bool allowNullValues; - 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>>(); - - /// <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; - } - - 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; - } - - 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"); - // value == null check here is redundant, but avoids boxing. - if (value == null && !allowNullValues) - { - 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; - } - } - - /// <summary> - /// Returns whether or not this map allows values to be null. - /// </summary> - public bool AllowsNullValues { get { return allowNullValues; } } - - 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) - { - 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) - { - ThrowHelper.ThrowIfNull(key, "key"); - this.CheckMutable(); - 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 IsFrozen; } } - - 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 - { - ThrowHelper.ThrowIfNull(key, "key"); - if (!(key is TKey)) - { - return null; - } - TValue value; - TryGetValue((TKey)key, out value); - return value; - } - - set - { - if (frozen) - { - throw new NotSupportedException("Dictionary is frozen"); - } - 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 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.WriteTagAndValue(output, Key); - codec.valueCodec.WriteTagAndValue(output, Value); - } - - public int CalculateSize() - { - return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value); - } - } - } - } -} diff --git a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs b/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs deleted file mode 100644 index 84360667..00000000 --- a/csharp/src/ProtocolBuffers/Collections/ReadOnlyDictionary.cs +++ /dev/null @@ -1,147 +0,0 @@ -#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 Google.Protobuf.Collections
-{
- /// <summary>
- /// Read-only wrapper around another dictionary.
- /// </summary>
- internal sealed class ReadOnlyDictionary<TKey, TValue> : IDictionary<TKey, TValue>
- {
- private readonly IDictionary<TKey, TValue> wrapped;
-
- public ReadOnlyDictionary(IDictionary<TKey, TValue> 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<TKey> 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<TValue> Values
- {
- get { return wrapped.Values; }
- }
-
- public TValue this[TKey key]
- {
- get { return wrapped[key]; }
- set { throw new InvalidOperationException(); }
- }
-
- public void Add(KeyValuePair<TKey, TValue> item)
- {
- throw new InvalidOperationException();
- }
-
- public void Clear()
- {
- throw new InvalidOperationException();
- }
-
- public bool Contains(KeyValuePair<TKey, TValue> item)
- {
- return wrapped.Contains(item);
- }
-
- public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
- {
- wrapped.CopyTo(array, arrayIndex);
- }
-
- public int Count
- {
- get { return wrapped.Count; }
- }
-
- public bool IsReadOnly
- {
- get { return true; }
- }
-
- public bool Remove(KeyValuePair<TKey, TValue> item)
- {
- throw new InvalidOperationException();
- }
-
- public IEnumerator<KeyValuePair<TKey, TValue>> 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/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs b/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs deleted file mode 100644 index 9bab41ea..00000000 --- a/csharp/src/ProtocolBuffers/Collections/RepeatedField.cs +++ /dev/null @@ -1,470 +0,0 @@ -#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; - -namespace Google.Protobuf.Collections -{ - /// <summary> - /// The contents of a repeated field: essentially, a collection with some extra - /// restrictions (no null values) and capabilities (deep cloning and freezing). - /// </summary> - /// <typeparam name="T">The element type of the repeated field.</typeparam> - public sealed class RepeatedField<T> : IList<T>, IList, IDeepCloneable<RepeatedField<T>>, IEquatable<RepeatedField<T>>, IFreezable - { - private static readonly T[] EmptyArray = new T[0]; - private const int MinArraySize = 8; - - private bool frozen; - private T[] array = EmptyArray; - private int count = 0; - - /// <summary> - /// Creates a deep clone of this repeated field. - /// </summary> - /// <remarks> - /// 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. - /// </remarks> - /// <returns>A deep clone of this repeated field.</returns> - public RepeatedField<T> Clone() - { - RepeatedField<T> clone = new RepeatedField<T>(); - // Clone is implicitly *not* frozen, even if this object is. - if (array != EmptyArray) - { - clone.array = (T[])array.Clone(); - IDeepCloneable<T>[] cloneableArray = clone.array as IDeepCloneable<T>[]; - if (cloneableArray != null) - { - for (int i = 0; i < count; i++) - { - clone.array[i] = cloneableArray[i].Clone(); - } - } - } - clone.count = count; - return clone; - } - - public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec) - { - // TODO: Inline some of the Add code, so we can avoid checking the size on every - // iteration and the mutability. - uint tag = input.LastTag; - var reader = codec.ValueReader; - // Value types can be packed or not. - if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited) - { - int length = input.ReadLength(); - if (length > 0) - { - int oldLimit = input.PushLimit(length); - while (!input.ReachedLimit) - { - Add(reader(input)); - } - input.PopLimit(oldLimit); - } - // Empty packed field. Odd, but valid - just ignore. - } - else - { - // Not packed... (possibly not packable) - do - { - Add(reader(input)); - } while (input.MaybeConsumeTag(tag)); - } - } - - public int CalculateSize(FieldCodec<T> codec) - { - if (count == 0) - { - return 0; - } - uint tag = codec.Tag; - if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited) - { - int dataSize = CalculatePackedDataSize(codec); - return CodedOutputStream.ComputeRawVarint32Size(tag) + - CodedOutputStream.ComputeLengthSize(dataSize) + - dataSize; - } - else - { - var sizeCalculator = codec.ValueSizeCalculator; - int size = count * CodedOutputStream.ComputeRawVarint32Size(tag); - for (int i = 0; i < count; i++) - { - size += sizeCalculator(array[i]); - } - return size; - } - } - - private int CalculatePackedDataSize(FieldCodec<T> 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; - } - } - - public void WriteTo(CodedOutputStream output, FieldCodec<T> codec) - { - if (count == 0) - { - return; - } - var writer = codec.ValueWriter; - var tag = codec.Tag; - if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited) - { - // Packed primitive type - uint size = (uint)CalculatePackedDataSize(codec); - output.WriteTag(tag); - output.WriteRawVarint32(size); - for (int i = 0; i < count; i++) - { - writer(output, 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++) - { - output.WriteTag(tag); - writer(output, array[i]); - } - } - } - - public bool IsFrozen { get { return frozen; } } - - public void Freeze() - { - frozen = true; - IFreezable[] freezableArray = array as IFreezable[]; - if (freezableArray != null) - { - for (int i = 0; i < count; i++) - { - freezableArray[i].Freeze(); - } - } - } - - private void EnsureSize(int size) - { - if (array.Length < size) - { - size = Math.Max(size, MinArraySize); - int newSize = Math.Max(array.Length * 2, size); - var tmp = new T[newSize]; - Array.Copy(array, 0, tmp, 0, array.Length); - array = tmp; - } - } - - public void Add(T item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - this.CheckMutable(); - EnsureSize(count + 1); - array[count++] = item; - } - - public void Clear() - { - this.CheckMutable(); - array = EmptyArray; - count = 0; - } - - public bool Contains(T item) - { - return IndexOf(item) != -1; - } - - public void CopyTo(T[] array, int arrayIndex) - { - Array.Copy(this.array, 0, array, arrayIndex, count); - } - - public bool Remove(T item) - { - this.CheckMutable(); - 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; - } - - public int Count { get { return count; } } - - public bool IsReadOnly { get { return IsFrozen; } } - - public void Add(RepeatedField<T> values) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - this.CheckMutable(); - EnsureSize(count + values.count); - // We know that all the values will be valid, because it's a RepeatedField. - Array.Copy(values.array, 0, array, count, values.count); - count += values.count; - } - - public void Add(IEnumerable<T> values) - { - if (values == null) - { - throw new ArgumentNullException("values"); - } - this.CheckMutable(); - // TODO: Check for ICollection and get the Count? - foreach (T item in values) - { - Add(item); - } - } - - public IEnumerator<T> GetEnumerator() - { - for (int i = 0; i < count; i++) - { - yield return array[i]; - } - } - - public override bool Equals(object obj) - { - return Equals(obj as RepeatedField<T>); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public override int GetHashCode() - { - int hash = 0; - for (int i = 0; i < count; i++) - { - hash = hash * 31 + array[i].GetHashCode(); - } - return hash; - } - - public bool Equals(RepeatedField<T> other) - { - if (ReferenceEquals(other, null)) - { - return false; - } - if (ReferenceEquals(other, this)) - { - return true; - } - if (other.Count != this.Count) - { - return false; - } - // TODO(jonskeet): Does this box for enums? - EqualityComparer<T> comparer = EqualityComparer<T>.Default; - for (int i = 0; i < count; i++) - { - if (!comparer.Equals(array[i], other.array[i])) - { - return false; - } - } - return true; - } - - public int IndexOf(T item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - // TODO(jonskeet): Does this box for enums? - EqualityComparer<T> comparer = EqualityComparer<T>.Default; - for (int i = 0; i < count; i++) - { - if (comparer.Equals(array[i], item)) - { - return i; - } - } - return -1; - } - - public void Insert(int index, T item) - { - if (item == null) - { - throw new ArgumentNullException("item"); - } - if (index < 0 || index > count) - { - throw new ArgumentOutOfRangeException("index"); - } - this.CheckMutable(); - EnsureSize(count + 1); - Array.Copy(array, index, array, index + 1, count - index); - array[index] = item; - count++; - } - - public void RemoveAt(int index) - { - if (index < 0 || index >= count) - { - throw new ArgumentOutOfRangeException("index"); - } - this.CheckMutable(); - Array.Copy(array, index + 1, array, index, count - index - 1); - count--; - array[count] = default(T); - } - - public T this[int index] - { - get - { - if (index < 0 || index >= count) - { - throw new ArgumentOutOfRangeException("index"); - } - return array[index]; - } - set - { - if (index < 0 || index >= count) - { - throw new ArgumentOutOfRangeException("index"); - } - this.CheckMutable(); - if (value == null) - { - throw new ArgumentNullException("value"); - } - array[index] = value; - } - } - - #region Explicit interface implementation for IList and ICollection. - bool IList.IsFixedSize { get { return IsFrozen; } } - - void ICollection.CopyTo(Array array, int index) - { - Array.Copy(this.array, 0, array, index, count); - } - - bool ICollection.IsSynchronized { get { return false; } } - - object ICollection.SyncRoot { get { return 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 - } -} |