1#region Copyright notice and license
2// Protocol Buffers - Google's data interchange format
3// Copyright 2017 Google Inc.  All rights reserved.
4// https://developers.google.com/protocol-buffers/
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10//     * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12//     * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16//     * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31#endregion
32
33using System;
34using System.Collections.Generic;
35using System.Collections.ObjectModel;
36using Google.Protobuf.Collections;
37
38namespace Google.Protobuf
39{
40    /// <summary>
41    /// Represents a single field in an UnknownFieldSet.
42    ///
43    /// An UnknownField consists of four lists of values. The lists correspond
44    /// to the four "wire types" used in the protocol buffer binary format.
45    /// Normally, only one of the four lists will contain any values, since it
46    /// is impossible to define a valid message type that declares two different
47    /// types for the same field number. However, the code is designed to allow
48    /// for the case where the same unknown field number is encountered using
49    /// multiple different wire types.
50    ///
51    /// </summary>
52    internal sealed class UnknownField
53    {
54        private List<ulong> varintList;
55        private List<uint> fixed32List;
56        private List<ulong> fixed64List;
57        private List<ByteString> lengthDelimitedList;
58        private List<UnknownFieldSet> groupList;
59
60        /// <summary>
61        /// Creates a new UnknownField.
62        /// </summary>
63        public UnknownField()
64        {
65        }
66
67        /// <summary>
68        /// Checks if two unknown field are equal.
69        /// </summary>
70        public override bool Equals(object other)
71        {
72            if (ReferenceEquals(this, other))
73            {
74                return true;
75            }
76            UnknownField otherField = other as UnknownField;
77            return otherField != null
78                   && Lists.Equals(varintList, otherField.varintList)
79                   && Lists.Equals(fixed32List, otherField.fixed32List)
80                   && Lists.Equals(fixed64List, otherField.fixed64List)
81                   && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList)
82                   && Lists.Equals(groupList, otherField.groupList);
83        }
84
85        /// <summary>
86        /// Get the hash code of the unknown field.
87        /// </summary>
88        public override int GetHashCode()
89        {
90            int hash = 43;
91            hash = hash * 47 + Lists.GetHashCode(varintList);
92            hash = hash * 47 + Lists.GetHashCode(fixed32List);
93            hash = hash * 47 + Lists.GetHashCode(fixed64List);
94            hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList);
95            hash = hash * 47 + Lists.GetHashCode(groupList);
96            return hash;
97        }
98
99        /// <summary>
100        /// Serializes the field, including the field number, and writes it to
101        /// <paramref name="output"/>
102        /// </summary>
103        /// <param name="fieldNumber">The unknown field number.</param>
104        /// <param name="output">The write context to write to.</param>
105        internal void WriteTo(int fieldNumber, ref WriteContext output)
106        {
107            if (varintList != null)
108            {
109                foreach (ulong value in varintList)
110                {
111                    output.WriteTag(fieldNumber, WireFormat.WireType.Varint);
112                    output.WriteUInt64(value);
113                }
114            }
115            if (fixed32List != null)
116            {
117                foreach (uint value in fixed32List)
118                {
119                    output.WriteTag(fieldNumber, WireFormat.WireType.Fixed32);
120                    output.WriteFixed32(value);
121                }
122            }
123            if (fixed64List != null)
124            {
125                foreach (ulong value in fixed64List)
126                {
127                    output.WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
128                    output.WriteFixed64(value);
129                }
130            }
131            if (lengthDelimitedList != null)
132            {
133                foreach (ByteString value in lengthDelimitedList)
134                {
135                    output.WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
136                    output.WriteBytes(value);
137                }
138            }
139            if (groupList != null)
140            {
141                foreach (UnknownFieldSet value in groupList)
142                {
143                    output.WriteTag(fieldNumber, WireFormat.WireType.StartGroup);
144                    value.WriteTo(ref output);
145                    output.WriteTag(fieldNumber, WireFormat.WireType.EndGroup);
146                }
147            }
148        }
149
150        /// <summary>
151        /// Computes the number of bytes required to encode this field, including field
152        /// number.
153        /// </summary>
154        internal int GetSerializedSize(int fieldNumber)
155        {
156            int result = 0;
157            if (varintList != null)
158            {
159                result += CodedOutputStream.ComputeTagSize(fieldNumber) * varintList.Count;
160                foreach (ulong value in varintList)
161                {
162                    result += CodedOutputStream.ComputeUInt64Size(value);
163                }
164            }
165            if (fixed32List != null)
166            {
167                result += CodedOutputStream.ComputeTagSize(fieldNumber) * fixed32List.Count;
168                result += CodedOutputStream.ComputeFixed32Size(1) * fixed32List.Count;
169            }
170            if (fixed64List != null)
171            {
172                result += CodedOutputStream.ComputeTagSize(fieldNumber) * fixed64List.Count;
173                result += CodedOutputStream.ComputeFixed64Size(1) * fixed64List.Count;
174            }
175            if (lengthDelimitedList != null)
176            {
177                result += CodedOutputStream.ComputeTagSize(fieldNumber) * lengthDelimitedList.Count;
178                foreach (ByteString value in lengthDelimitedList)
179                {
180                    result += CodedOutputStream.ComputeBytesSize(value);
181                }
182            }
183            if (groupList != null)
184            {
185                result += CodedOutputStream.ComputeTagSize(fieldNumber) * 2 * groupList.Count;
186                foreach (UnknownFieldSet value in groupList)
187                {
188                    result += value.CalculateSize();
189                }
190            }
191            return result;
192        }
193
194        /// <summary>
195        /// Merge the values in <paramref name="other" /> into this field.  For each list
196        /// of values, <paramref name="other"/>'s values are append to the ones in this
197        /// field.
198        /// </summary>
199        internal UnknownField MergeFrom(UnknownField other)
200        {
201            varintList = AddAll(varintList, other.varintList);
202            fixed32List = AddAll(fixed32List, other.fixed32List);
203            fixed64List = AddAll(fixed64List, other.fixed64List);
204            lengthDelimitedList = AddAll(lengthDelimitedList, other.lengthDelimitedList);
205            groupList = AddAll(groupList, other.groupList);
206            return this;
207        }
208
209        /// <summary>
210        /// Returns a new list containing all of the given specified values from
211        /// both the <paramref name="current"/> and <paramref name="extras"/> lists.
212        /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
213        /// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
214        /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
215        /// </summary>
216        private static List<T> AddAll<T>(List<T> current, IList<T> extras)
217        {
218            if (extras.Count == 0)
219            {
220                return current;
221            }
222            if (current == null)
223            {
224                current = new List<T>(extras);
225            }
226            else
227            {
228                current.AddRange(extras);
229            }
230            return current;
231        }
232
233        /// <summary>
234        /// Adds a varint value.
235        /// </summary>
236        internal UnknownField AddVarint(ulong value)
237        {
238            varintList = Add(varintList, value);
239            return this;
240        }
241
242        /// <summary>
243        /// Adds a fixed32 value.
244        /// </summary>
245        internal UnknownField AddFixed32(uint value)
246        {
247            fixed32List = Add(fixed32List, value);
248            return this;
249        }
250
251        /// <summary>
252        /// Adds a fixed64 value.
253        /// </summary>
254        internal UnknownField AddFixed64(ulong value)
255        {
256            fixed64List = Add(fixed64List, value);
257            return this;
258        }
259
260        /// <summary>
261        /// Adds a length-delimited value.
262        /// </summary>
263        internal UnknownField AddLengthDelimited(ByteString value)
264        {
265            lengthDelimitedList = Add(lengthDelimitedList, value);
266            return this;
267        }
268
269        internal UnknownField AddGroup(UnknownFieldSet value)
270        {
271            groupList = Add(groupList, value);
272            return this;
273        }
274
275        /// <summary>
276        /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
277        /// a new list if <paramref name="list"/> is null. The list is returned - either
278        /// the original reference or the new list.
279        /// </summary>
280        private static List<T> Add<T>(List<T> list, T value)
281        {
282            if (list == null)
283            {
284                list = new List<T>();
285            }
286            list.Add(value);
287            return list;
288        }
289    }
290}
291