blob: 76afe68032ec6a98628fa141d6f37d189a5b945e [file] [log] [blame]
Jon Skeet68036862008-10-22 13:30:34 +01001// Protocol Buffers - Google's data interchange format
2// Copyright 2008 Google Inc.
3// http://code.google.com/p/protobuf/
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16using System.Collections.Generic;
17using System.Collections.ObjectModel;
18using Google.ProtocolBuffers.Collections;
19
20namespace Google.ProtocolBuffers {
21 /// <summary>
22 /// Represents a single field in an UnknownFieldSet.
23 ///
24 /// An UnknownField consists of five lists of values. The lists correspond
25 /// to the five "wire types" used in the protocol buffer binary format.
26 /// The wire type of each field can be determined from the encoded form alone,
27 /// without knowing the field's declared type. So, we are able to parse
28 /// unknown values at least this far and separate them. Normally, only one
29 /// of the five lists will contain any values, since it is impossible to
30 /// define a valid message type that declares two different types for the
31 /// same field number. However, the code is designed to allow for the case
32 /// where the same unknown field number is encountered using multiple different
33 /// wire types.
34 ///
35 /// UnknownField is an immutable class. To construct one, you must use an
36 /// UnknownField.Builder.
37 /// </summary>
38 public sealed class UnknownField {
39
40 private static readonly UnknownField defaultInstance = CreateBuilder().Build();
41 private readonly ReadOnlyCollection<ulong> varintList;
42 private readonly ReadOnlyCollection<uint> fixed32List;
43 private readonly ReadOnlyCollection<ulong> fixed64List;
44 private readonly ReadOnlyCollection<ByteString> lengthDelimitedList;
45 private readonly ReadOnlyCollection<UnknownFieldSet> groupList;
46
47 private UnknownField(ReadOnlyCollection<ulong> varintList,
48 ReadOnlyCollection<uint> fixed32List,
49 ReadOnlyCollection<ulong> fixed64List,
50 ReadOnlyCollection<ByteString> lengthDelimitedList,
51 ReadOnlyCollection<UnknownFieldSet> groupList) {
52 this.varintList = varintList;
53 this.fixed32List = fixed32List;
54 this.fixed64List = fixed64List;
55 this.lengthDelimitedList = lengthDelimitedList;
56 this.groupList = groupList;
57 }
58
59 public static UnknownField DefaultInstance {
60 get { return defaultInstance; }
61 }
62
63 /// <summary>
64 /// The list of varint values for this field.
65 /// </summary>
66 public IList<ulong> VarintList {
67 get { return varintList; }
68 }
69
70 /// <summary>
71 /// The list of fixed32 values for this field.
72 /// </summary>
73 public IList<uint> Fixed32List {
74 get { return fixed32List; }
75 }
76
77 /// <summary>
78 /// The list of fixed64 values for this field.
79 /// </summary>
80 public IList<ulong> Fixed64List {
81 get { return fixed64List; }
82 }
83
84 /// <summary>
85 /// The list of length-delimited values for this field.
86 /// </summary>
87 public IList<ByteString> LengthDelimitedList {
88 get { return lengthDelimitedList; }
89 }
90
91 /// <summary>
92 /// The list of embedded group values for this field. These
93 /// are represented using UnknownFieldSets rather than Messages
94 /// since the group's type is presumably unknown.
95 /// </summary>
96 public IList<UnknownFieldSet> GroupList {
97 get { return groupList; }
98 }
99
100 /// <summary>
101 /// Constructs a new Builder.
102 /// </summary>
103 public static Builder CreateBuilder() {
104 return new Builder();
105 }
106
107 /// <summary>
108 /// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>.
109 /// </summary>
110 public static Builder CreateBuilder(UnknownField copyFrom) {
111 return new Builder().MergeFrom(copyFrom);
112 }
113
114 /// <summary>
115 /// Serializes the field, including the field number, and writes it to
116 /// <paramref name="output"/>.
117 /// </summary>
118 public void WriteTo(int fieldNumber, CodedOutputStream output) {
119 foreach (ulong value in varintList) {
120 output.WriteUInt64(fieldNumber, value);
121 }
122 foreach (uint value in fixed32List) {
123 output.WriteFixed32(fieldNumber, value);
124 }
125 foreach (ulong value in fixed64List) {
126 output.WriteFixed64(fieldNumber, value);
127 }
128 foreach (ByteString value in lengthDelimitedList) {
129 output.WriteBytes(fieldNumber, value);
130 }
131 foreach (UnknownFieldSet value in groupList) {
132 output.WriteUnknownGroup(fieldNumber, value);
133 }
134 }
135
136 /// <summary>
137 /// Computes the number of bytes required to encode this field, including field
138 /// number.
139 /// </summary>
140 public int GetSerializedSize(int fieldNumber) {
141 int result = 0;
142 foreach (ulong value in varintList) {
143 result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value);
144 }
145 foreach (uint value in fixed32List) {
146 result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value);
147 }
148 foreach (ulong value in fixed64List) {
149 result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value);
150 }
151 foreach (ByteString value in lengthDelimitedList) {
152 result += CodedOutputStream.ComputeBytesSize(fieldNumber, value);
153 }
154 foreach (UnknownFieldSet value in groupList) {
155 result += CodedOutputStream.ComputeUnknownGroupSize(fieldNumber, value);
156 }
157 return result;
158 }
159
160 /// <summary>
161 /// Serializes the length-delimited values of the field, including field
162 /// number, and writes them to <paramref name="output"/> using the MessageSet wire format.
163 /// </summary>
164 /// <param name="fieldNumber"></param>
165 /// <param name="output"></param>
166 public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) {
167 foreach (ByteString value in lengthDelimitedList) {
168 output.WriteRawMessageSetExtension(fieldNumber, value);
169 }
170 }
171
172 /// <summary>
173 /// Get the number of bytes required to encode this field, incuding field number,
174 /// using the MessageSet wire format.
175 /// </summary>
176 public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) {
177 int result = 0;
178 foreach (ByteString value in lengthDelimitedList) {
179 result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value);
180 }
181 return result;
182 }
183
184 /// <summary>
185 /// Used to build instances of UnknownField.
186 /// </summary>
187 public sealed class Builder {
188
189 private List<ulong> varintList;
190 private List<uint> fixed32List;
191 private List<ulong> fixed64List;
192 private List<ByteString> lengthDelimitedList;
193 private List<UnknownFieldSet> groupList;
194
195 /// <summary>
196 /// Builds the field. After building, the builder is reset to an empty
197 /// state. (This is actually easier than making it unusable.)
198 /// </summary>
199 public UnknownField Build() {
200 return new UnknownField(MakeReadOnly(ref varintList),
201 MakeReadOnly(ref fixed32List),
202 MakeReadOnly(ref fixed64List),
203 MakeReadOnly(ref lengthDelimitedList),
204 MakeReadOnly(ref groupList));
205 }
206
207 /// <summary>
208 /// Merge the values in <paramref name="other" /> into this field. For each list
209 /// of values, <paramref name="other"/>'s values are append to the ones in this
210 /// field.
211 /// </summary>
212 public Builder MergeFrom(UnknownField other) {
213 varintList = AddAll(varintList, other.VarintList);
214 fixed32List = AddAll(fixed32List, other.Fixed32List);
215 fixed64List = AddAll(fixed64List, other.Fixed64List);
216 lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList);
217 groupList = AddAll(groupList, other.GroupList);
218 return this;
219 }
220
221 /// <summary>
222 /// Returns a new list containing all of the given specified values from
223 /// both the <paramref name="current"/> and <paramref name="extras"/> lists.
224 /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
225 /// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
226 /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
227 /// </summary>
228 private static List<T> AddAll<T>(List<T> current, IList<T> extras)
229 {
230 if (extras.Count == 0) {
231 return current;
232 }
233 if (current == null) {
234 current = new List<T>(extras);
235 } else {
236 current.AddRange(extras);
237 }
238 return current;
239 }
240
241 /// <summary>
242 /// Clears the contents of this builder.
243 /// </summary>
244 public Builder Clear() {
245 varintList = null;
246 fixed32List = null;
247 fixed64List = null;
248 lengthDelimitedList = null;
249 groupList = null;
250 return this;
251 }
252
253 /// <summary>
254 /// Adds a varint value.
255 /// </summary>
256 public Builder AddVarint(ulong value) {
257 varintList = Add(varintList, value);
258 return this;
259 }
260
261 /// <summary>
262 /// Adds a fixed32 value.
263 /// </summary>
264 public Builder AddFixed32(uint value) {
265 fixed32List = Add(fixed32List, value);
266 return this;
267 }
268
269 /// <summary>
270 /// Adds a fixed64 value.
271 /// </summary>
272 public Builder AddFixed64(ulong value) {
273 fixed64List = Add(fixed64List, value);
274 return this;
275 }
276
277 /// <summary>
278 /// Adds a length-delimited value.
279 /// </summary>
280 public Builder AddLengthDelimited(ByteString value) {
281 lengthDelimitedList = Add(lengthDelimitedList, value);
282 return this;
283 }
284
285 /// <summary>
286 /// Adds an embedded group.
287 /// </summary>
288 /// <param name="value"></param>
289 /// <returns></returns>
290 public Builder AddGroup(UnknownFieldSet value) {
291 groupList = Add(groupList, value);
292 return this;
293 }
294
295 /// <summary>
296 /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
297 /// a new list if <paramref name="list"/> is null. The list is returned - either
298 /// the original reference or the new list.
299 /// </summary>
300 private static List<T> Add<T>(List<T> list, T value) {
301 if (list == null) {
302 list = new List<T>();
303 }
304 list.Add(value);
305 return list;
306 }
307
308 /// <summary>
309 /// Returns a read-only version of the given IList, and clears
310 /// the field used for <paramref name="list"/>. If the value
311 /// is null, an empty list is produced using Lists.Empty.
312 /// </summary>
313 /// <returns></returns>
314 private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) {
315 ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list);
316 list = null;
317 return ret;
318 }
319 }
320 }
321}