xref: /aosp_15_r20/external/protobuf/csharp/src/Google.Protobuf/ExtensionSet.cs (revision 1b3f573f81763fcece89efc2b6a5209149e44ab8)
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