UnknownField.cs 13.3 KB
Newer Older
1
#region Copyright notice and license
Jon Skeet's avatar
Jon Skeet committed
2 3 4 5
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
// http://github.com/jskeet/dotnet-protobufs/
// Original C++/Java/Python code:
Jon Skeet's avatar
Jon Skeet committed
6 7
// http://code.google.com/p/protobuf/
//
Jon Skeet's avatar
Jon Skeet committed
8 9 10
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
Jon Skeet's avatar
Jon Skeet committed
11
//
Jon Skeet's avatar
Jon Skeet committed
12 13 14 15 16 17 18 19 20
//     * 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.
Jon Skeet's avatar
Jon Skeet committed
21
//
Jon Skeet's avatar
Jon Skeet committed
22 23 24 25 26 27 28 29 30 31 32
// 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.
33 34
#endregion

Jon Skeet's avatar
Jon Skeet committed
35
using System;
Jon Skeet's avatar
Jon Skeet committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Google.ProtocolBuffers.Collections;

namespace Google.ProtocolBuffers {
  /// <summary>
  /// Represents a single field in an UnknownFieldSet.
  /// 
  /// An UnknownField consists of five lists of values. The lists correspond
   /// to the five "wire types" used in the protocol buffer binary format.
   /// The wire type of each field can be determined from the encoded form alone,
   /// without knowing the field's declared type. So, we are able to parse
   /// unknown values at least this far and separate them. Normally, only one
   /// of the five lists will contain any values, since it is impossible to
   /// define a valid message type that declares two different types for the
   /// same field number. However, the code is designed to allow for the case
   /// where the same unknown field number is encountered using multiple different
   /// wire types.
   /// 
   /// UnknownField is an immutable class. To construct one, you must use an
   /// UnknownField.Builder.
  /// </summary>
  public sealed class UnknownField {

    private static readonly UnknownField defaultInstance = CreateBuilder().Build();
    private readonly ReadOnlyCollection<ulong> varintList;
    private readonly ReadOnlyCollection<uint> fixed32List;
    private readonly ReadOnlyCollection<ulong> fixed64List;
    private readonly ReadOnlyCollection<ByteString> lengthDelimitedList;
    private readonly ReadOnlyCollection<UnknownFieldSet> groupList;

    private UnknownField(ReadOnlyCollection<ulong> varintList,
        ReadOnlyCollection<uint> fixed32List, 
        ReadOnlyCollection<ulong> fixed64List, 
        ReadOnlyCollection<ByteString> lengthDelimitedList, 
        ReadOnlyCollection<UnknownFieldSet> groupList) {
      this.varintList = varintList;
      this.fixed32List = fixed32List;
      this.fixed64List = fixed64List;
      this.lengthDelimitedList = lengthDelimitedList;
      this.groupList = groupList;
    }

    public static UnknownField DefaultInstance { 
      get { return defaultInstance; } 
    }

    /// <summary>
    /// The list of varint values for this field.
    /// </summary>
    public IList<ulong> VarintList {
      get { return varintList; }
    }

    /// <summary>
    /// The list of fixed32 values for this field.
    /// </summary>
    public IList<uint> Fixed32List {
      get { return fixed32List; }
    }

    /// <summary>
    /// The list of fixed64 values for this field.
    /// </summary>
    public IList<ulong> Fixed64List {
      get { return fixed64List; }
    }

    /// <summary>
    /// The list of length-delimited values for this field.
    /// </summary>
    public IList<ByteString> LengthDelimitedList {
      get { return lengthDelimitedList; }
    }

    /// <summary>
    /// The list of embedded group values for this field. These
    /// are represented using UnknownFieldSets rather than Messages
    /// since the group's type is presumably unknown.
    /// </summary>
    public IList<UnknownFieldSet> GroupList {
      get { return groupList; }
    }

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
    public override bool Equals(object other) {
      if (ReferenceEquals(this, other)) {
        return true;
      }
      UnknownField otherField = other as UnknownField;
      return otherField != null
        && Lists.Equals(varintList, otherField.varintList)
        && Lists.Equals(fixed32List, otherField.fixed32List)
        && Lists.Equals(fixed64List, otherField.fixed64List)
        && Lists.Equals(lengthDelimitedList, otherField.lengthDelimitedList)
        && Lists.Equals(groupList, otherField.groupList);
    }

    public override int GetHashCode() {
      int hash = 43;
      hash = hash * 47 + Lists.GetHashCode(varintList);
      hash = hash * 47 + Lists.GetHashCode(fixed32List);
      hash = hash * 47 + Lists.GetHashCode(fixed64List);
      hash = hash * 47 + Lists.GetHashCode(lengthDelimitedList);
      hash = hash * 47 + Lists.GetHashCode(groupList);
      return hash;
    }

Jon Skeet's avatar
Jon Skeet committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    /// <summary>
    /// Constructs a new Builder.
    /// </summary>
    public static Builder CreateBuilder() {
      return new Builder();
    }

    /// <summary>
    /// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>.
    /// </summary>
    public static Builder CreateBuilder(UnknownField copyFrom) {
      return new Builder().MergeFrom(copyFrom);
    }
   
    /// <summary>
    /// Serializes the field, including the field number, and writes it to
    /// <paramref name="output"/>.
    /// </summary>
    public void WriteTo(int fieldNumber, CodedOutputStream output) {
      foreach (ulong value in varintList) {
        output.WriteUInt64(fieldNumber, value);
      }
      foreach (uint value in fixed32List) {
        output.WriteFixed32(fieldNumber, value);
      }
      foreach (ulong value in fixed64List) {
        output.WriteFixed64(fieldNumber, value);
      }
      foreach (ByteString value in lengthDelimitedList) {
        output.WriteBytes(fieldNumber, value);
      }
      foreach (UnknownFieldSet value in groupList) {
175
#pragma warning disable 0612
Jon Skeet's avatar
Jon Skeet committed
176
        output.WriteUnknownGroup(fieldNumber, value);
177
#pragma warning restore 0612
Jon Skeet's avatar
Jon Skeet committed
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
      }
    }

    /// <summary>
    /// Computes the number of bytes required to encode this field, including field
    /// number.
    /// </summary>
    public int GetSerializedSize(int fieldNumber) {
      int result = 0;
      foreach (ulong value in varintList) {
        result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value);
      }
      foreach (uint value in fixed32List) {
        result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value);
      }
      foreach (ulong value in fixed64List) {
        result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value);
      }
      foreach (ByteString value in lengthDelimitedList) {
        result += CodedOutputStream.ComputeBytesSize(fieldNumber, value);
      }
      foreach (UnknownFieldSet value in groupList) {
200
#pragma warning disable 0612
Jon Skeet's avatar
Jon Skeet committed
201
        result += CodedOutputStream.ComputeUnknownGroupSize(fieldNumber, value);
202
#pragma warning restore 0612
Jon Skeet's avatar
Jon Skeet committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
      }
      return result;
    }

    /// <summary>
    /// Serializes the length-delimited values of the field, including field
    /// number, and writes them to <paramref name="output"/> using the MessageSet wire format.
    /// </summary>
    /// <param name="fieldNumber"></param>
    /// <param name="output"></param>
    public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) {
      foreach (ByteString value in lengthDelimitedList) {
        output.WriteRawMessageSetExtension(fieldNumber, value);
      }
    }

    /// <summary>
    /// Get the number of bytes required to encode this field, incuding field number,
    /// using the MessageSet wire format.
    /// </summary>
    public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) {
      int result = 0;
      foreach (ByteString value in lengthDelimitedList) {
        result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value);
      }
      return result;
    }

    /// <summary>
    /// Used to build instances of UnknownField.
    /// </summary>
    public sealed class Builder {

      private List<ulong> varintList;
      private List<uint> fixed32List;
      private List<ulong> fixed64List;
      private List<ByteString> lengthDelimitedList;
      private List<UnknownFieldSet> groupList;

      /// <summary>
      /// Builds the field. After building, the builder is reset to an empty
      /// state. (This is actually easier than making it unusable.)
      /// </summary>
      public UnknownField Build() {
        return new UnknownField(MakeReadOnly(ref varintList),
            MakeReadOnly(ref fixed32List),
            MakeReadOnly(ref fixed64List),
            MakeReadOnly(ref lengthDelimitedList),
            MakeReadOnly(ref groupList));
      }

      /// <summary>
      /// Merge the values in <paramref name="other" /> into this field.  For each list
      /// of values, <paramref name="other"/>'s values are append to the ones in this
      /// field.
      /// </summary>
      public Builder MergeFrom(UnknownField other) {
        varintList = AddAll(varintList, other.VarintList);
        fixed32List = AddAll(fixed32List, other.Fixed32List);
        fixed64List = AddAll(fixed64List, other.Fixed64List);
        lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList);
        groupList = AddAll(groupList, other.GroupList);
        return this;
      }

      /// <summary>
      /// Returns a new list containing all of the given specified values from
      /// both the <paramref name="current"/> and <paramref name="extras"/> lists.
      /// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
      /// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
      /// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
      /// </summary>
      private static List<T> AddAll<T>(List<T> current, IList<T> extras)
      {
        if (extras.Count == 0) {
          return current;
        }
Jon Skeet's avatar
Jon Skeet committed
280
           if (current == null) {
Jon Skeet's avatar
Jon Skeet committed
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
          current = new List<T>(extras);
        } else {
          current.AddRange(extras);
        }
        return current;
      }

      /// <summary>
      /// Clears the contents of this builder.
      /// </summary>
      public Builder Clear() {
        varintList = null;
        fixed32List = null;
        fixed64List = null;
        lengthDelimitedList = null;
        groupList = null;
        return this;
      }

      /// <summary>
      /// Adds a varint value.
      /// </summary>
Jon Skeet's avatar
Jon Skeet committed
303
      [CLSCompliant(false)]
Jon Skeet's avatar
Jon Skeet committed
304 305 306 307 308 309 310 311
      public Builder AddVarint(ulong value) {
        varintList = Add(varintList, value);
        return this;
      }

      /// <summary>
      /// Adds a fixed32 value.
      /// </summary>
Jon Skeet's avatar
Jon Skeet committed
312
      [CLSCompliant(false)]
Jon Skeet's avatar
Jon Skeet committed
313 314 315 316 317 318 319 320
      public Builder AddFixed32(uint value) {
        fixed32List = Add(fixed32List, value);
        return this;
      }

      /// <summary>
      /// Adds a fixed64 value.
      /// </summary>
Jon Skeet's avatar
Jon Skeet committed
321
      [CLSCompliant(false)]
Jon Skeet's avatar
Jon Skeet committed
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
      public Builder AddFixed64(ulong value) {
        fixed64List = Add(fixed64List, value);
        return this;
      }

      /// <summary>
      /// Adds a length-delimited value.
      /// </summary>
      public Builder AddLengthDelimited(ByteString value) {
        lengthDelimitedList = Add(lengthDelimitedList, value);
        return this;
      }

      /// <summary>
      /// Adds an embedded group.
      /// </summary>
      /// <param name="value"></param>
      /// <returns></returns>
      public Builder AddGroup(UnknownFieldSet value) {
        groupList = Add(groupList, value);
        return this;
      }

      /// <summary>
      /// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
      /// a new list if <paramref name="list"/> is null. The list is returned - either
      /// the original reference or the new list.
      /// </summary>
      private static List<T> Add<T>(List<T> list, T value) {
        if (list == null) {
          list = new List<T>();
        }
        list.Add(value);
        return list;
      }

      /// <summary>
      /// Returns a read-only version of the given IList, and clears
      /// the field used for <paramref name="list"/>. If the value
      /// is null, an empty list is produced using Lists.Empty.
      /// </summary>
      /// <returns></returns>
      private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) {
        ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list);
Jon Skeet's avatar
Jon Skeet committed
366
           list = null;
Jon Skeet's avatar
Jon Skeet committed
367 368 369 370 371
        return ret;
      }
    }
  }
}