1*1b3f573fSAndroid Build Coastguard Worker #region Copyright notice and license 2*1b3f573fSAndroid Build Coastguard Worker // Protocol Buffers - Google's data interchange format 3*1b3f573fSAndroid Build Coastguard Worker // Copyright 2008 Google Inc. All rights reserved. 4*1b3f573fSAndroid Build Coastguard Worker // https://developers.google.com/protocol-buffers/ 5*1b3f573fSAndroid Build Coastguard Worker // 6*1b3f573fSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without 7*1b3f573fSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are 8*1b3f573fSAndroid Build Coastguard Worker // met: 9*1b3f573fSAndroid Build Coastguard Worker // 10*1b3f573fSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright 11*1b3f573fSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer. 12*1b3f573fSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above 13*1b3f573fSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer 14*1b3f573fSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the 15*1b3f573fSAndroid Build Coastguard Worker // distribution. 16*1b3f573fSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its 17*1b3f573fSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from 18*1b3f573fSAndroid Build Coastguard Worker // this software without specific prior written permission. 19*1b3f573fSAndroid Build Coastguard Worker // 20*1b3f573fSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21*1b3f573fSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23*1b3f573fSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24*1b3f573fSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25*1b3f573fSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26*1b3f573fSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27*1b3f573fSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28*1b3f573fSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29*1b3f573fSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30*1b3f573fSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31*1b3f573fSAndroid Build Coastguard Worker #endregion 32*1b3f573fSAndroid Build Coastguard Worker 33*1b3f573fSAndroid Build Coastguard Worker using Google.Protobuf.Collections; 34*1b3f573fSAndroid Build Coastguard Worker using System; 35*1b3f573fSAndroid Build Coastguard Worker using System.Collections.Generic; 36*1b3f573fSAndroid Build Coastguard Worker using System.Linq; 37*1b3f573fSAndroid Build Coastguard Worker using System.Reflection; 38*1b3f573fSAndroid Build Coastguard Worker using System.Security; 39*1b3f573fSAndroid Build Coastguard Worker 40*1b3f573fSAndroid Build Coastguard Worker namespace Google.Protobuf 41*1b3f573fSAndroid Build Coastguard Worker { 42*1b3f573fSAndroid Build Coastguard Worker /// <summary> 43*1b3f573fSAndroid Build Coastguard Worker /// Methods for managing <see cref="ExtensionSet{TTarget}"/>s with null checking. 44*1b3f573fSAndroid Build Coastguard Worker /// 45*1b3f573fSAndroid Build Coastguard Worker /// Most users will not use this class directly and its API is experimental and subject to change. 46*1b3f573fSAndroid Build Coastguard Worker /// </summary> 47*1b3f573fSAndroid Build Coastguard Worker public static class ExtensionSet 48*1b3f573fSAndroid Build Coastguard Worker { 49*1b3f573fSAndroid Build Coastguard Worker private static bool TryGetValue<TTarget>(ref ExtensionSet<TTarget> set, Extension extension, out IExtensionValue value) where TTarget : IExtendableMessage<TTarget> 50*1b3f573fSAndroid Build Coastguard Worker { 51*1b3f573fSAndroid Build Coastguard Worker if (set == null) 52*1b3f573fSAndroid Build Coastguard Worker { 53*1b3f573fSAndroid Build Coastguard Worker value = null; 54*1b3f573fSAndroid Build Coastguard Worker return false; 55*1b3f573fSAndroid Build Coastguard Worker } 56*1b3f573fSAndroid Build Coastguard Worker return set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value); 57*1b3f573fSAndroid Build Coastguard Worker } 58*1b3f573fSAndroid Build Coastguard Worker 59*1b3f573fSAndroid Build Coastguard Worker /// <summary> 60*1b3f573fSAndroid Build Coastguard Worker /// Gets the value of the specified extension 61*1b3f573fSAndroid Build Coastguard Worker /// </summary> 62*1b3f573fSAndroid Build Coastguard Worker public static TValue Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 63*1b3f573fSAndroid Build Coastguard Worker { 64*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value; 65*1b3f573fSAndroid Build Coastguard Worker if (TryGetValue(ref set, extension, out value)) 66*1b3f573fSAndroid Build Coastguard Worker { 67*1b3f573fSAndroid Build Coastguard Worker // The stored ExtensionValue can be a different type to what is being requested. 68*1b3f573fSAndroid Build Coastguard Worker // This happens when the same extension proto is compiled in different assemblies. 69*1b3f573fSAndroid Build Coastguard Worker // To allow consuming assemblies to still get the value when the TValue type is 70*1b3f573fSAndroid Build Coastguard Worker // different, this get method: 71*1b3f573fSAndroid Build Coastguard Worker // 1. Attempts to cast the value to the expected ExtensionValue<TValue>. 72*1b3f573fSAndroid Build Coastguard Worker // This is the usual case. It is used first because it avoids possibly boxing the value. 73*1b3f573fSAndroid Build Coastguard Worker // 2. Fallback to get the value as object from IExtensionValue then casting. 74*1b3f573fSAndroid Build Coastguard Worker // This allows for someone to specify a TValue of object. They can then convert 75*1b3f573fSAndroid Build Coastguard Worker // the values to bytes and reparse using expected value. 76*1b3f573fSAndroid Build Coastguard Worker // 3. If neither of these work, throw a user friendly error that the types aren't compatible. 77*1b3f573fSAndroid Build Coastguard Worker if (value is ExtensionValue<TValue> extensionValue) 78*1b3f573fSAndroid Build Coastguard Worker { 79*1b3f573fSAndroid Build Coastguard Worker return extensionValue.GetValue(); 80*1b3f573fSAndroid Build Coastguard Worker } 81*1b3f573fSAndroid Build Coastguard Worker else if (value.GetValue() is TValue underlyingValue) 82*1b3f573fSAndroid Build Coastguard Worker { 83*1b3f573fSAndroid Build Coastguard Worker return underlyingValue; 84*1b3f573fSAndroid Build Coastguard Worker } 85*1b3f573fSAndroid Build Coastguard Worker else 86*1b3f573fSAndroid Build Coastguard Worker { 87*1b3f573fSAndroid Build Coastguard Worker var valueType = value.GetType().GetTypeInfo(); 88*1b3f573fSAndroid Build Coastguard Worker if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(ExtensionValue<>)) 89*1b3f573fSAndroid Build Coastguard Worker { 90*1b3f573fSAndroid Build Coastguard Worker var storedType = valueType.GenericTypeArguments[0]; 91*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException( 92*1b3f573fSAndroid Build Coastguard Worker "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " + 93*1b3f573fSAndroid Build Coastguard Worker "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'."); 94*1b3f573fSAndroid Build Coastguard Worker } 95*1b3f573fSAndroid Build Coastguard Worker else 96*1b3f573fSAndroid Build Coastguard Worker { 97*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName); 98*1b3f573fSAndroid Build Coastguard Worker } 99*1b3f573fSAndroid Build Coastguard Worker } 100*1b3f573fSAndroid Build Coastguard Worker } 101*1b3f573fSAndroid Build Coastguard Worker else 102*1b3f573fSAndroid Build Coastguard Worker { 103*1b3f573fSAndroid Build Coastguard Worker return extension.DefaultValue; 104*1b3f573fSAndroid Build Coastguard Worker } 105*1b3f573fSAndroid Build Coastguard Worker } 106*1b3f573fSAndroid Build Coastguard Worker 107*1b3f573fSAndroid Build Coastguard Worker /// <summary> 108*1b3f573fSAndroid Build Coastguard Worker /// Gets the value of the specified repeated extension or null if it doesn't exist in this set 109*1b3f573fSAndroid Build Coastguard Worker /// </summary> 110*1b3f573fSAndroid Build Coastguard Worker public static RepeatedField<TValue> Get<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 111*1b3f573fSAndroid Build Coastguard Worker { 112*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value; 113*1b3f573fSAndroid Build Coastguard Worker if (TryGetValue(ref set, extension, out value)) 114*1b3f573fSAndroid Build Coastguard Worker { 115*1b3f573fSAndroid Build Coastguard Worker if (value is RepeatedExtensionValue<TValue> extensionValue) 116*1b3f573fSAndroid Build Coastguard Worker { 117*1b3f573fSAndroid Build Coastguard Worker return extensionValue.GetValue(); 118*1b3f573fSAndroid Build Coastguard Worker } 119*1b3f573fSAndroid Build Coastguard Worker else 120*1b3f573fSAndroid Build Coastguard Worker { 121*1b3f573fSAndroid Build Coastguard Worker var valueType = value.GetType().GetTypeInfo(); 122*1b3f573fSAndroid Build Coastguard Worker if (valueType.IsGenericType && valueType.GetGenericTypeDefinition() == typeof(RepeatedExtensionValue<>)) 123*1b3f573fSAndroid Build Coastguard Worker { 124*1b3f573fSAndroid Build Coastguard Worker var storedType = valueType.GenericTypeArguments[0]; 125*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException( 126*1b3f573fSAndroid Build Coastguard Worker "The stored extension value has a type of '" + storedType.AssemblyQualifiedName + "'. " + 127*1b3f573fSAndroid Build Coastguard Worker "This a different from the requested type of '" + typeof(TValue).AssemblyQualifiedName + "'."); 128*1b3f573fSAndroid Build Coastguard Worker } 129*1b3f573fSAndroid Build Coastguard Worker else 130*1b3f573fSAndroid Build Coastguard Worker { 131*1b3f573fSAndroid Build Coastguard Worker throw new InvalidOperationException("Unexpected extension value type: " + valueType.AssemblyQualifiedName); 132*1b3f573fSAndroid Build Coastguard Worker } 133*1b3f573fSAndroid Build Coastguard Worker } 134*1b3f573fSAndroid Build Coastguard Worker } 135*1b3f573fSAndroid Build Coastguard Worker else 136*1b3f573fSAndroid Build Coastguard Worker { 137*1b3f573fSAndroid Build Coastguard Worker return null; 138*1b3f573fSAndroid Build Coastguard Worker } 139*1b3f573fSAndroid Build Coastguard Worker } 140*1b3f573fSAndroid Build Coastguard Worker 141*1b3f573fSAndroid Build Coastguard Worker /// <summary> 142*1b3f573fSAndroid Build Coastguard Worker /// Gets the value of the specified repeated extension, registering it if it doesn't exist 143*1b3f573fSAndroid Build Coastguard Worker /// </summary> 144*1b3f573fSAndroid Build Coastguard Worker public static RepeatedField<TValue> GetOrInitialize<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 145*1b3f573fSAndroid Build Coastguard Worker { 146*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value; 147*1b3f573fSAndroid Build Coastguard Worker if (set == null) 148*1b3f573fSAndroid Build Coastguard Worker { 149*1b3f573fSAndroid Build Coastguard Worker value = extension.CreateValue(); 150*1b3f573fSAndroid Build Coastguard Worker set = new ExtensionSet<TTarget>(); 151*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Add(extension.FieldNumber, value); 152*1b3f573fSAndroid Build Coastguard Worker } 153*1b3f573fSAndroid Build Coastguard Worker else 154*1b3f573fSAndroid Build Coastguard Worker { 155*1b3f573fSAndroid Build Coastguard Worker if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out value)) 156*1b3f573fSAndroid Build Coastguard Worker { 157*1b3f573fSAndroid Build Coastguard Worker value = extension.CreateValue(); 158*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Add(extension.FieldNumber, value); 159*1b3f573fSAndroid Build Coastguard Worker } 160*1b3f573fSAndroid Build Coastguard Worker } 161*1b3f573fSAndroid Build Coastguard Worker 162*1b3f573fSAndroid Build Coastguard Worker return ((RepeatedExtensionValue<TValue>)value).GetValue(); 163*1b3f573fSAndroid Build Coastguard Worker } 164*1b3f573fSAndroid Build Coastguard Worker 165*1b3f573fSAndroid Build Coastguard Worker /// <summary> 166*1b3f573fSAndroid Build Coastguard Worker /// Sets the value of the specified extension. This will make a new instance of ExtensionSet if the set is null. 167*1b3f573fSAndroid Build Coastguard Worker /// </summary> 168*1b3f573fSAndroid Build Coastguard Worker public static void Set<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension, TValue value) where TTarget : IExtendableMessage<TTarget> 169*1b3f573fSAndroid Build Coastguard Worker { 170*1b3f573fSAndroid Build Coastguard Worker ProtoPreconditions.CheckNotNullUnconstrained(value, nameof(value)); 171*1b3f573fSAndroid Build Coastguard Worker 172*1b3f573fSAndroid Build Coastguard Worker IExtensionValue extensionValue; 173*1b3f573fSAndroid Build Coastguard Worker if (set == null) 174*1b3f573fSAndroid Build Coastguard Worker { 175*1b3f573fSAndroid Build Coastguard Worker extensionValue = extension.CreateValue(); 176*1b3f573fSAndroid Build Coastguard Worker set = new ExtensionSet<TTarget>(); 177*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Add(extension.FieldNumber, extensionValue); 178*1b3f573fSAndroid Build Coastguard Worker } 179*1b3f573fSAndroid Build Coastguard Worker else 180*1b3f573fSAndroid Build Coastguard Worker { 181*1b3f573fSAndroid Build Coastguard Worker if (!set.ValuesByNumber.TryGetValue(extension.FieldNumber, out extensionValue)) 182*1b3f573fSAndroid Build Coastguard Worker { 183*1b3f573fSAndroid Build Coastguard Worker extensionValue = extension.CreateValue(); 184*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Add(extension.FieldNumber, extensionValue); 185*1b3f573fSAndroid Build Coastguard Worker } 186*1b3f573fSAndroid Build Coastguard Worker } 187*1b3f573fSAndroid Build Coastguard Worker 188*1b3f573fSAndroid Build Coastguard Worker ((ExtensionValue<TValue>)extensionValue).SetValue(value); 189*1b3f573fSAndroid Build Coastguard Worker } 190*1b3f573fSAndroid Build Coastguard Worker 191*1b3f573fSAndroid Build Coastguard Worker /// <summary> 192*1b3f573fSAndroid Build Coastguard Worker /// Gets whether the value of the specified extension is set 193*1b3f573fSAndroid Build Coastguard Worker /// </summary> 194*1b3f573fSAndroid Build Coastguard Worker public static bool Has<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 195*1b3f573fSAndroid Build Coastguard Worker { 196*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value; 197*1b3f573fSAndroid Build Coastguard Worker return TryGetValue(ref set, extension, out value); 198*1b3f573fSAndroid Build Coastguard Worker } 199*1b3f573fSAndroid Build Coastguard Worker 200*1b3f573fSAndroid Build Coastguard Worker /// <summary> 201*1b3f573fSAndroid Build Coastguard Worker /// Clears the value of the specified extension 202*1b3f573fSAndroid Build Coastguard Worker /// </summary> 203*1b3f573fSAndroid Build Coastguard Worker public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, Extension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 204*1b3f573fSAndroid Build Coastguard Worker { 205*1b3f573fSAndroid Build Coastguard Worker if (set == null) 206*1b3f573fSAndroid Build Coastguard Worker { 207*1b3f573fSAndroid Build Coastguard Worker return; 208*1b3f573fSAndroid Build Coastguard Worker } 209*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Remove(extension.FieldNumber); 210*1b3f573fSAndroid Build Coastguard Worker if (set.ValuesByNumber.Count == 0) 211*1b3f573fSAndroid Build Coastguard Worker { 212*1b3f573fSAndroid Build Coastguard Worker set = null; 213*1b3f573fSAndroid Build Coastguard Worker } 214*1b3f573fSAndroid Build Coastguard Worker } 215*1b3f573fSAndroid Build Coastguard Worker 216*1b3f573fSAndroid Build Coastguard Worker /// <summary> 217*1b3f573fSAndroid Build Coastguard Worker /// Clears the value of the specified extension 218*1b3f573fSAndroid Build Coastguard Worker /// </summary> 219*1b3f573fSAndroid Build Coastguard Worker public static void Clear<TTarget, TValue>(ref ExtensionSet<TTarget> set, RepeatedExtension<TTarget, TValue> extension) where TTarget : IExtendableMessage<TTarget> 220*1b3f573fSAndroid Build Coastguard Worker { 221*1b3f573fSAndroid Build Coastguard Worker if (set == null) 222*1b3f573fSAndroid Build Coastguard Worker { 223*1b3f573fSAndroid Build Coastguard Worker return; 224*1b3f573fSAndroid Build Coastguard Worker } 225*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Remove(extension.FieldNumber); 226*1b3f573fSAndroid Build Coastguard Worker if (set.ValuesByNumber.Count == 0) 227*1b3f573fSAndroid Build Coastguard Worker { 228*1b3f573fSAndroid Build Coastguard Worker set = null; 229*1b3f573fSAndroid Build Coastguard Worker } 230*1b3f573fSAndroid Build Coastguard Worker } 231*1b3f573fSAndroid Build Coastguard Worker 232*1b3f573fSAndroid Build Coastguard Worker /// <summary> 233*1b3f573fSAndroid Build Coastguard Worker /// Tries to merge a field from the coded input, returning true if the field was merged. 234*1b3f573fSAndroid Build Coastguard Worker /// If the set is null or the field was not otherwise merged, this returns false. 235*1b3f573fSAndroid Build Coastguard Worker /// </summary> 236*1b3f573fSAndroid Build Coastguard Worker public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, CodedInputStream stream) where TTarget : IExtendableMessage<TTarget> 237*1b3f573fSAndroid Build Coastguard Worker { 238*1b3f573fSAndroid Build Coastguard Worker ParseContext.Initialize(stream, out ParseContext ctx); 239*1b3f573fSAndroid Build Coastguard Worker try 240*1b3f573fSAndroid Build Coastguard Worker { 241*1b3f573fSAndroid Build Coastguard Worker return TryMergeFieldFrom<TTarget>(ref set, ref ctx); 242*1b3f573fSAndroid Build Coastguard Worker } 243*1b3f573fSAndroid Build Coastguard Worker finally 244*1b3f573fSAndroid Build Coastguard Worker { 245*1b3f573fSAndroid Build Coastguard Worker ctx.CopyStateTo(stream); 246*1b3f573fSAndroid Build Coastguard Worker } 247*1b3f573fSAndroid Build Coastguard Worker } 248*1b3f573fSAndroid Build Coastguard Worker 249*1b3f573fSAndroid Build Coastguard Worker /// <summary> 250*1b3f573fSAndroid Build Coastguard Worker /// Tries to merge a field from the coded input, returning true if the field was merged. 251*1b3f573fSAndroid Build Coastguard Worker /// If the set is null or the field was not otherwise merged, this returns false. 252*1b3f573fSAndroid Build Coastguard Worker /// </summary> 253*1b3f573fSAndroid Build Coastguard Worker public static bool TryMergeFieldFrom<TTarget>(ref ExtensionSet<TTarget> set, ref ParseContext ctx) where TTarget : IExtendableMessage<TTarget> 254*1b3f573fSAndroid Build Coastguard Worker { 255*1b3f573fSAndroid Build Coastguard Worker Extension extension; 256*1b3f573fSAndroid Build Coastguard Worker int lastFieldNumber = WireFormat.GetTagFieldNumber(ctx.LastTag); 257*1b3f573fSAndroid Build Coastguard Worker 258*1b3f573fSAndroid Build Coastguard Worker IExtensionValue extensionValue; 259*1b3f573fSAndroid Build Coastguard Worker if (set != null && set.ValuesByNumber.TryGetValue(lastFieldNumber, out extensionValue)) 260*1b3f573fSAndroid Build Coastguard Worker { 261*1b3f573fSAndroid Build Coastguard Worker extensionValue.MergeFrom(ref ctx); 262*1b3f573fSAndroid Build Coastguard Worker return true; 263*1b3f573fSAndroid Build Coastguard Worker } 264*1b3f573fSAndroid Build Coastguard Worker else if (ctx.ExtensionRegistry != null && ctx.ExtensionRegistry.ContainsInputField(ctx.LastTag, typeof(TTarget), out extension)) 265*1b3f573fSAndroid Build Coastguard Worker { 266*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value = extension.CreateValue(); 267*1b3f573fSAndroid Build Coastguard Worker value.MergeFrom(ref ctx); 268*1b3f573fSAndroid Build Coastguard Worker set = (set ?? new ExtensionSet<TTarget>()); 269*1b3f573fSAndroid Build Coastguard Worker set.ValuesByNumber.Add(extension.FieldNumber, value); 270*1b3f573fSAndroid Build Coastguard Worker return true; 271*1b3f573fSAndroid Build Coastguard Worker } 272*1b3f573fSAndroid Build Coastguard Worker else 273*1b3f573fSAndroid Build Coastguard Worker { 274*1b3f573fSAndroid Build Coastguard Worker return false; 275*1b3f573fSAndroid Build Coastguard Worker } 276*1b3f573fSAndroid Build Coastguard Worker } 277*1b3f573fSAndroid Build Coastguard Worker 278*1b3f573fSAndroid Build Coastguard Worker /// <summary> 279*1b3f573fSAndroid Build Coastguard Worker /// Merges the second set into the first set, creating a new instance if first is null 280*1b3f573fSAndroid Build Coastguard Worker /// </summary> 281*1b3f573fSAndroid Build Coastguard Worker public static void MergeFrom<TTarget>(ref ExtensionSet<TTarget> first, ExtensionSet<TTarget> second) where TTarget : IExtendableMessage<TTarget> 282*1b3f573fSAndroid Build Coastguard Worker { 283*1b3f573fSAndroid Build Coastguard Worker if (second == null) 284*1b3f573fSAndroid Build Coastguard Worker { 285*1b3f573fSAndroid Build Coastguard Worker return; 286*1b3f573fSAndroid Build Coastguard Worker } 287*1b3f573fSAndroid Build Coastguard Worker if (first == null) 288*1b3f573fSAndroid Build Coastguard Worker { 289*1b3f573fSAndroid Build Coastguard Worker first = new ExtensionSet<TTarget>(); 290*1b3f573fSAndroid Build Coastguard Worker } 291*1b3f573fSAndroid Build Coastguard Worker foreach (var pair in second.ValuesByNumber) 292*1b3f573fSAndroid Build Coastguard Worker { 293*1b3f573fSAndroid Build Coastguard Worker IExtensionValue value; 294*1b3f573fSAndroid Build Coastguard Worker if (first.ValuesByNumber.TryGetValue(pair.Key, out value)) 295*1b3f573fSAndroid Build Coastguard Worker { 296*1b3f573fSAndroid Build Coastguard Worker value.MergeFrom(pair.Value); 297*1b3f573fSAndroid Build Coastguard Worker } 298*1b3f573fSAndroid Build Coastguard Worker else 299*1b3f573fSAndroid Build Coastguard Worker { 300*1b3f573fSAndroid Build Coastguard Worker var cloned = pair.Value.Clone(); 301*1b3f573fSAndroid Build Coastguard Worker first.ValuesByNumber[pair.Key] = cloned; 302*1b3f573fSAndroid Build Coastguard Worker } 303*1b3f573fSAndroid Build Coastguard Worker } 304*1b3f573fSAndroid Build Coastguard Worker } 305*1b3f573fSAndroid Build Coastguard Worker 306*1b3f573fSAndroid Build Coastguard Worker /// <summary> 307*1b3f573fSAndroid Build Coastguard Worker /// Clones the set into a new set. If the set is null, this returns null 308*1b3f573fSAndroid Build Coastguard Worker /// </summary> 309*1b3f573fSAndroid Build Coastguard Worker public static ExtensionSet<TTarget> Clone<TTarget>(ExtensionSet<TTarget> set) where TTarget : IExtendableMessage<TTarget> 310*1b3f573fSAndroid Build Coastguard Worker { 311*1b3f573fSAndroid Build Coastguard Worker if (set == null) 312*1b3f573fSAndroid Build Coastguard Worker { 313*1b3f573fSAndroid Build Coastguard Worker return null; 314*1b3f573fSAndroid Build Coastguard Worker } 315*1b3f573fSAndroid Build Coastguard Worker 316*1b3f573fSAndroid Build Coastguard Worker var newSet = new ExtensionSet<TTarget>(); 317*1b3f573fSAndroid Build Coastguard Worker foreach (var pair in set.ValuesByNumber) 318*1b3f573fSAndroid Build Coastguard Worker { 319*1b3f573fSAndroid Build Coastguard Worker var cloned = pair.Value.Clone(); 320*1b3f573fSAndroid Build Coastguard Worker newSet.ValuesByNumber[pair.Key] = cloned; 321*1b3f573fSAndroid Build Coastguard Worker } 322*1b3f573fSAndroid Build Coastguard Worker return newSet; 323*1b3f573fSAndroid Build Coastguard Worker } 324*1b3f573fSAndroid Build Coastguard Worker } 325*1b3f573fSAndroid Build Coastguard Worker 326*1b3f573fSAndroid Build Coastguard Worker /// <summary> 327*1b3f573fSAndroid Build Coastguard Worker /// Used for keeping track of extensions in messages. 328*1b3f573fSAndroid Build Coastguard Worker /// <see cref="IExtendableMessage{T}"/> methods route to this set. 329*1b3f573fSAndroid Build Coastguard Worker /// 330*1b3f573fSAndroid Build Coastguard Worker /// Most users will not need to use this class directly 331*1b3f573fSAndroid Build Coastguard Worker /// </summary> 332*1b3f573fSAndroid Build Coastguard Worker /// <typeparam name="TTarget">The message type that extensions in this set target</typeparam> 333*1b3f573fSAndroid Build Coastguard Worker public sealed class ExtensionSet<TTarget> where TTarget : IExtendableMessage<TTarget> 334*1b3f573fSAndroid Build Coastguard Worker { 335*1b3f573fSAndroid Build Coastguard Worker internal Dictionary<int, IExtensionValue> ValuesByNumber { get; } = new Dictionary<int, IExtensionValue>(); 336*1b3f573fSAndroid Build Coastguard Worker 337*1b3f573fSAndroid Build Coastguard Worker /// <summary> 338*1b3f573fSAndroid Build Coastguard Worker /// Gets a hash code of the set 339*1b3f573fSAndroid Build Coastguard Worker /// </summary> GetHashCode()340*1b3f573fSAndroid Build Coastguard Worker public override int GetHashCode() 341*1b3f573fSAndroid Build Coastguard Worker { 342*1b3f573fSAndroid Build Coastguard Worker int ret = typeof(TTarget).GetHashCode(); 343*1b3f573fSAndroid Build Coastguard Worker foreach (KeyValuePair<int, IExtensionValue> field in ValuesByNumber) 344*1b3f573fSAndroid Build Coastguard Worker { 345*1b3f573fSAndroid Build Coastguard Worker // Use ^ here to make the field order irrelevant. 346*1b3f573fSAndroid Build Coastguard Worker int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode(); 347*1b3f573fSAndroid Build Coastguard Worker ret ^= hash; 348*1b3f573fSAndroid Build Coastguard Worker } 349*1b3f573fSAndroid Build Coastguard Worker return ret; 350*1b3f573fSAndroid Build Coastguard Worker } 351*1b3f573fSAndroid Build Coastguard Worker 352*1b3f573fSAndroid Build Coastguard Worker /// <summary> 353*1b3f573fSAndroid Build Coastguard Worker /// Returns whether this set is equal to the other object 354*1b3f573fSAndroid Build Coastguard Worker /// </summary> Equals(object other)355*1b3f573fSAndroid Build Coastguard Worker public override bool Equals(object other) 356*1b3f573fSAndroid Build Coastguard Worker { 357*1b3f573fSAndroid Build Coastguard Worker if (ReferenceEquals(this, other)) 358*1b3f573fSAndroid Build Coastguard Worker { 359*1b3f573fSAndroid Build Coastguard Worker return true; 360*1b3f573fSAndroid Build Coastguard Worker } 361*1b3f573fSAndroid Build Coastguard Worker ExtensionSet<TTarget> otherSet = other as ExtensionSet<TTarget>; 362*1b3f573fSAndroid Build Coastguard Worker if (ValuesByNumber.Count != otherSet.ValuesByNumber.Count) 363*1b3f573fSAndroid Build Coastguard Worker { 364*1b3f573fSAndroid Build Coastguard Worker return false; 365*1b3f573fSAndroid Build Coastguard Worker } 366*1b3f573fSAndroid Build Coastguard Worker foreach (var pair in ValuesByNumber) 367*1b3f573fSAndroid Build Coastguard Worker { 368*1b3f573fSAndroid Build Coastguard Worker IExtensionValue secondValue; 369*1b3f573fSAndroid Build Coastguard Worker if (!otherSet.ValuesByNumber.TryGetValue(pair.Key, out secondValue)) 370*1b3f573fSAndroid Build Coastguard Worker { 371*1b3f573fSAndroid Build Coastguard Worker return false; 372*1b3f573fSAndroid Build Coastguard Worker } 373*1b3f573fSAndroid Build Coastguard Worker if (!pair.Value.Equals(secondValue)) 374*1b3f573fSAndroid Build Coastguard Worker { 375*1b3f573fSAndroid Build Coastguard Worker return false; 376*1b3f573fSAndroid Build Coastguard Worker } 377*1b3f573fSAndroid Build Coastguard Worker } 378*1b3f573fSAndroid Build Coastguard Worker return true; 379*1b3f573fSAndroid Build Coastguard Worker } 380*1b3f573fSAndroid Build Coastguard Worker 381*1b3f573fSAndroid Build Coastguard Worker /// <summary> 382*1b3f573fSAndroid Build Coastguard Worker /// Calculates the size of this extension set 383*1b3f573fSAndroid Build Coastguard Worker /// </summary> CalculateSize()384*1b3f573fSAndroid Build Coastguard Worker public int CalculateSize() 385*1b3f573fSAndroid Build Coastguard Worker { 386*1b3f573fSAndroid Build Coastguard Worker int size = 0; 387*1b3f573fSAndroid Build Coastguard Worker foreach (var value in ValuesByNumber.Values) 388*1b3f573fSAndroid Build Coastguard Worker { 389*1b3f573fSAndroid Build Coastguard Worker size += value.CalculateSize(); 390*1b3f573fSAndroid Build Coastguard Worker } 391*1b3f573fSAndroid Build Coastguard Worker return size; 392*1b3f573fSAndroid Build Coastguard Worker } 393*1b3f573fSAndroid Build Coastguard Worker 394*1b3f573fSAndroid Build Coastguard Worker /// <summary> 395*1b3f573fSAndroid Build Coastguard Worker /// Writes the extension values in this set to the output stream 396*1b3f573fSAndroid Build Coastguard Worker /// </summary> WriteTo(CodedOutputStream stream)397*1b3f573fSAndroid Build Coastguard Worker public void WriteTo(CodedOutputStream stream) 398*1b3f573fSAndroid Build Coastguard Worker { 399*1b3f573fSAndroid Build Coastguard Worker 400*1b3f573fSAndroid Build Coastguard Worker WriteContext.Initialize(stream, out WriteContext ctx); 401*1b3f573fSAndroid Build Coastguard Worker try 402*1b3f573fSAndroid Build Coastguard Worker { 403*1b3f573fSAndroid Build Coastguard Worker WriteTo(ref ctx); 404*1b3f573fSAndroid Build Coastguard Worker } 405*1b3f573fSAndroid Build Coastguard Worker finally 406*1b3f573fSAndroid Build Coastguard Worker { 407*1b3f573fSAndroid Build Coastguard Worker ctx.CopyStateTo(stream); 408*1b3f573fSAndroid Build Coastguard Worker } 409*1b3f573fSAndroid Build Coastguard Worker } 410*1b3f573fSAndroid Build Coastguard Worker 411*1b3f573fSAndroid Build Coastguard Worker /// <summary> 412*1b3f573fSAndroid Build Coastguard Worker /// Writes the extension values in this set to the write context 413*1b3f573fSAndroid Build Coastguard Worker /// </summary> 414*1b3f573fSAndroid Build Coastguard Worker [SecuritySafeCritical] WriteTo(ref WriteContext ctx)415*1b3f573fSAndroid Build Coastguard Worker public void WriteTo(ref WriteContext ctx) 416*1b3f573fSAndroid Build Coastguard Worker { 417*1b3f573fSAndroid Build Coastguard Worker foreach (var value in ValuesByNumber.Values) 418*1b3f573fSAndroid Build Coastguard Worker { 419*1b3f573fSAndroid Build Coastguard Worker value.WriteTo(ref ctx); 420*1b3f573fSAndroid Build Coastguard Worker } 421*1b3f573fSAndroid Build Coastguard Worker } 422*1b3f573fSAndroid Build Coastguard Worker IsInitialized()423*1b3f573fSAndroid Build Coastguard Worker internal bool IsInitialized() 424*1b3f573fSAndroid Build Coastguard Worker { 425*1b3f573fSAndroid Build Coastguard Worker return ValuesByNumber.Values.All(v => v.IsInitialized()); 426*1b3f573fSAndroid Build Coastguard Worker } 427*1b3f573fSAndroid Build Coastguard Worker } 428*1b3f573fSAndroid Build Coastguard Worker } 429