#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2015 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * 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.
//
// 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.
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using Google.Protobuf.Reflection;
namespace Google.Protobuf
{
///
/// Used to keep track of fields which were seen when parsing a protocol message
/// but whose field numbers or types are unrecognized. This most frequently
/// occurs when new fields are added to a message type and then messages containing
/// those fields are read by old software that was built before the new types were
/// added.
///
/// Most users will never need to use this class directly.
///
public sealed partial class UnknownFieldSet
{
private readonly IDictionary fields;
///
/// Creates a new UnknownFieldSet.
///
internal UnknownFieldSet()
{
this.fields = new Dictionary();
}
///
/// Checks whether or not the given field number is present in the set.
///
internal bool HasField(int field)
{
return fields.ContainsKey(field);
}
///
/// Serializes the set and writes it to .
///
public void WriteTo(CodedOutputStream output)
{
foreach (KeyValuePair entry in fields)
{
entry.Value.WriteTo(entry.Key, output);
}
}
///
/// Gets the number of bytes required to encode this set.
///
public int CalculateSize()
{
int result = 0;
foreach (KeyValuePair entry in fields)
{
result += entry.Value.GetSerializedSize(entry.Key);
}
return result;
}
///
/// Checks if two unknown field sets are equal.
///
public override bool Equals(object other)
{
if (ReferenceEquals(this, other))
{
return true;
}
UnknownFieldSet otherSet = other as UnknownFieldSet;
IDictionary otherFields = otherSet.fields;
if (fields.Count != otherFields.Count)
{
return false;
}
foreach (KeyValuePair leftEntry in fields)
{
UnknownField rightValue;
if (!otherFields.TryGetValue(leftEntry.Key, out rightValue))
{
return false;
}
if (!leftEntry.Value.Equals(rightValue))
{
return false;
}
}
return true;
}
///
/// Gets the unknown field set's hash code.
///
public override int GetHashCode()
{
int ret = 1;
foreach (KeyValuePair field in fields)
{
// Use ^ here to make the field order irrelevant.
int hash = field.Key.GetHashCode() ^ field.Value.GetHashCode();
ret ^= hash;
}
return ret;
}
// Optimization: We keep around the last field that was
// modified so that we can efficiently add to it multiple times in a
// row (important when parsing an unknown repeated field).
private int lastFieldNumber;
private UnknownField lastField;
private UnknownField GetOrAddField(int number)
{
if (lastField != null && number == lastFieldNumber)
{
return lastField;
}
if (number == 0)
{
return null;
}
UnknownField existing;
if (fields.TryGetValue(number, out existing))
{
return existing;
}
lastField = new UnknownField();
AddOrReplaceField(number, lastField);
lastFieldNumber = number;
return lastField;
}
///
/// Adds a field to the set. If a field with the same number already exists, it
/// is replaced.
///
internal UnknownFieldSet AddOrReplaceField(int number, UnknownField field)
{
if (number == 0)
{
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
fields[number] = field;
return this;
}
///
/// Parse a single field from and merge it
/// into this set.
///
/// The coded input stream containing the field
/// false if the tag is an "end group" tag, true otherwise
private bool MergeFieldFrom(CodedInputStream input)
{
uint tag = input.LastTag;
int number = WireFormat.GetTagFieldNumber(tag);
switch (WireFormat.GetTagWireType(tag))
{
case WireFormat.WireType.Varint:
{
ulong uint64 = input.ReadUInt64();
GetOrAddField(number).AddVarint(uint64);
return true;
}
case WireFormat.WireType.Fixed32:
{
uint uint32 = input.ReadFixed32();
GetOrAddField(number).AddFixed32(uint32);
return true;
}
case WireFormat.WireType.Fixed64:
{
ulong uint64 = input.ReadFixed64();
GetOrAddField(number).AddFixed64(uint64);
return true;
}
case WireFormat.WireType.LengthDelimited:
{
ByteString bytes = input.ReadBytes();
GetOrAddField(number).AddLengthDelimited(bytes);
return true;
}
case WireFormat.WireType.StartGroup:
{
uint endTag = WireFormat.MakeTag(number, WireFormat.WireType.EndGroup);
UnknownFieldSet set = new UnknownFieldSet();
while (input.ReadTag() != endTag)
{
set.MergeFieldFrom(input);
}
GetOrAddField(number).AddGroup(set);
return true;
}
case WireFormat.WireType.EndGroup:
{
return false;
}
default:
throw InvalidProtocolBufferException.InvalidWireType();
}
}
///
/// Create a new UnknownFieldSet if unknownFields is null.
/// Parse a single field from and merge it
/// into unknownFields. If is configured to discard unknown fields,
/// will be returned as-is and the field will be skipped.
///
/// The UnknownFieldSet which need to be merged
/// The coded input stream containing the field
/// The merged UnknownFieldSet
public static UnknownFieldSet MergeFieldFrom(UnknownFieldSet unknownFields,
CodedInputStream input)
{
if (input.DiscardUnknownFields)
{
input.SkipLastField();
return unknownFields;
}
if (unknownFields == null)
{
unknownFields = new UnknownFieldSet();
}
if (!unknownFields.MergeFieldFrom(input))
{
throw new InvalidProtocolBufferException("Merge an unknown field of end-group tag, indicating that the corresponding start-group was missing."); // match the old code-gen
}
return unknownFields;
}
///
/// Merges the fields from into this set.
/// If a field number exists in both sets, the values in
/// will be appended to the values in this set.
///
private UnknownFieldSet MergeFrom(UnknownFieldSet other)
{
if (other != null)
{
foreach (KeyValuePair entry in other.fields)
{
MergeField(entry.Key, entry.Value);
}
}
return this;
}
///
/// Created a new UnknownFieldSet to if
/// needed and merges the fields from into the first set.
/// If a field number exists in both sets, the values in
/// will be appended to the values in this set.
///
public static UnknownFieldSet MergeFrom(UnknownFieldSet unknownFields,
UnknownFieldSet other)
{
if (other == null)
{
return unknownFields;
}
if (unknownFields == null)
{
unknownFields = new UnknownFieldSet();
}
unknownFields.MergeFrom(other);
return unknownFields;
}
///
/// Adds a field to the unknown field set. If a field with the same
/// number already exists, the two are merged.
///
private UnknownFieldSet MergeField(int number, UnknownField field)
{
if (number == 0)
{
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
if (HasField(number))
{
GetOrAddField(number).MergeFrom(field);
}
else
{
AddOrReplaceField(number, field);
}
return this;
}
///
/// Clone an unknown field set from .
///
public static UnknownFieldSet Clone(UnknownFieldSet other)
{
if (other == null)
{
return null;
}
UnknownFieldSet unknownFields = new UnknownFieldSet();
unknownFields.MergeFrom(other);
return unknownFields;
}
}
}