123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- #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 Google.Protobuf.Collections;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- namespace Google.Protobuf
- {
- /// <summary>
- /// Methods for managing <see cref="ExtensionSet{TTarget}"/>s with null checking.
- ///
- /// Most users will not use this class directly and its API is experimental and subject to change.
- /// </summary>
- public static class ExtensionSet
- {
- private static bool TryGetValue<TTarget>(ref ExtensionSet<TTarget> set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage<TTarget>
- {
- if (set == null)
- {
- value = null;
- return false;
- }
- return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value);
- }
- /// <summary>
- /// Gets the value of the specified extension
- /// </summary>
- public static TValue Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- IExtensionValue value;
- if (TryGetValue(ref set, extension, out value))
- {
- return ((ExtensionValue<TValue>)value).GetValue();
- }
- else
- {
- return extension.DefaultValue;
- }
- }
- /// <summary>
- /// Gets the value of the specified repeated extension or null if it doesn't exist in this set
- /// </summary>
- public static RepeatedField<TValue> Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- IExtensionValue value;
- if (TryGetValue(ref set, extension, out value))
- {
- return ((RepeatedExtensionValue<TValue>)value).GetValue();
- }
- else
- {
- return null;
- }
- }
- /// <summary>
- /// Gets the value of the specified repeated extension, registering it if it doesn't exist
- /// </summary>
- public static RepeatedField<TValue> GetOrInitialize<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- IExtensionValue value;
- if (set == null)
- {
- value = extension.CreateValue();
- set = new ExtensionSet<TTarget>();
- 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<TValue>)value).GetValue();
- }
- /// <summary>
- /// Sets the value of the specified extension. This will make a new instance of ExtensionSet if the set is null.
- /// </summary>
- public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget>
- {
- ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value));
- IExtensionValue extensionValue;
- if (set == null)
- {
- extensionValue = extension.CreateValue();
- set = new ExtensionSet<TTarget>();
- 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<TValue>)extensionValue).SetValue(value);
- }
- /// <summary>
- /// Gets whether the value of the specified extension is set
- /// </summary>
- public static bool Has<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- IExtensionValue value;
- return TryGetValue(ref set, extension, out value);
- }
- /// <summary>
- /// Clears the value of the specified extension
- /// </summary>
- public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- if (set == null)
- {
- return;
- }
- set.ValuesByNumber.Remove(extension.FieldNumber);
- if (set.ValuesByNumber.Count == 0)
- {
- set = null;
- }
- }
- /// <summary>
- /// Clears the value of the specified extension
- /// </summary>
- public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget>
- {
- if (set == null)
- {
- return;
- }
- set.ValuesByNumber.Remove(extension.FieldNumber);
- if (set.ValuesByNumber.Count == 0)
- {
- set = null;
- }
- }
- /// <summary>
- /// Tries to merge a field from the coded input, returning true if the field was merged.
- /// If the set is null or the field was not otherwise merged, this returns false.
- /// </summary>
- public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget>
- {
- Extension extension;
- int lastFieldNumber = WireFormat.GetTagFieldNumber(stream.LastTag);
-
- IExtensionValue extensionValue;
- if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue))
- {
- extensionValue.MergeFrom(stream);
- return true;
- }
- else if (stream.ExtensionRegistry != null && stream.ExtensionRegistry.ContainsInputField(stream, typeof(TTarget), out extension))
- {
- IExtensionValue value = extension.CreateValue();
- value.MergeFrom(stream);
- set = (set ?? new ExtensionSet<TTarget>());
- set.ValuesByNumber.Add(extension.FieldNumber, value);
- return true;
- }
- else
- {
- return false;
- }
- }
- /// <summary>
- /// Merges the second set into the first set, creating a new instance if first is null
- /// </summary>
- public static void MergeFrom<TTarget>(ref ExtensionSet<TTarget> first, ExtensionSet<TTarget> second) where TTarget : IExtendableMessage<TTarget>
- {
- if (second == null)
- {
- return;
- }
- if (first == null)
- {
- first = new ExtensionSet<TTarget>();
- }
- foreach (var pair in second.ValuesByNumber)
- {
- IExtensionValue value;
- if (first.ValuesByNumber.TryGetValue(pair.Key, out value))
- {
- value.MergeFrom(pair.Value);
- }
- else
- {
- var cloned = pair.Value.Clone();
- first.ValuesByNumber[pair.Key] = cloned;
- }
- }
- }
- /// <summary>
- /// Clones the set into a new set. If the set is null, this returns null
- /// </summary>
- public static ExtensionSet<TTarget> Clone<TTarget>(ExtensionSet<TTarget> set) where TTarget : IExtendableMessage<TTarget>
- {
- if (set == null)
- {
- return null;
- }
- var newSet = new ExtensionSet<TTarget>();
- foreach (var pair in set.ValuesByNumber)
- {
- var cloned = pair.Value.Clone();
- newSet.ValuesByNumber[pair.Key] = cloned;
- }
- return newSet;
- }
- }
- /// <summary>
- /// Used for keeping track of extensions in messages.
- /// <see cref="IExtendableMessage{T}"/> methods route to this set.
- ///
- /// Most users will not need to use this class directly
- /// </summary>
- /// <typeparam name="TTarget">The message type that extensions in this set target</typeparam>
- public sealed class ExtensionSet<TTarget> where TTarget : IExtendableMessage<TTarget>
- {
- internal Dictionary<int, IExtensionValue> ValuesByNumber { get; } = new Dictionary<int, IExtensionValue>();
- /// <summary>
- /// Gets a hash code of the set
- /// </summary>
- public override int GetHashCode()
- {
- int ret = typeof(TTarget).GetHashCode();
- foreach (KeyValuePair<int, IExtensionValue> field in ValuesByNumber)
- {
- // Use ^ here to make the field order irrelevant.
- int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();
- ret ^= hash;
- }
- return ret;
- }
- /// <summary>
- /// Returns whether this set is equal to the other object
- /// </summary>
- public override bool Equals(object other)
- {
- if (ReferenceEquals(this, other))
- {
- return true;
- }
- ExtensionSet<TTarget> otherSet = other as ExtensionSet<TTarget>;
- if (ValuesByNumber.Count != otherSet.ValuesByNumber.Count)
- {
- return false;
- }
- foreach (var pair in ValuesByNumber)
- {
- IExtensionValue secondValue;
- if (!otherSet.ValuesByNumber.TryGetValue(pair.Key, out secondValue))
- {
- return false;
- }
- if (!pair.Value.Equals(secondValue))
- {
- return false;
- }
- }
- return true;
- }
- /// <summary>
- /// Calculates the size of this extension set
- /// </summary>
- public int CalculateSize()
- {
- int size = 0;
- foreach (var value in ValuesByNumber.Values)
- {
- size += value.CalculateSize();
- }
- return size;
- }
- /// <summary>
- /// Writes the extension values in this set to the output stream
- /// </summary>
- public void WriteTo(CodedOutputStream stream)
- {
- foreach (var value in ValuesByNumber.Values)
- {
- value.WriteTo(stream);
- }
- }
- internal bool IsInitialized()
- {
- return ValuesByNumber.Values.All(v => v.IsInitialized());
- }
- }
- }
|