// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.
// http://code.google.com/p/protobuf/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.IO;
using Google.ProtocolBuffers.Collections;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
///
/// 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.
///
/// Every message contains an UnknownFieldSet.
///
/// Most users will never need to use this class directly.
///
public sealed class UnknownFieldSet {
private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary());
private readonly IDictionary fields;
private UnknownFieldSet(IDictionary fields) {
this.fields = fields;
}
///
/// Creates a new unknown field set builder.
///
public static Builder CreateBuilder() {
return new Builder();
}
///
/// Creates a new unknown field set builder
/// and initialize it from .
///
public static Builder CreateBuilder(UnknownFieldSet original) {
return new Builder().MergeFrom(original);
}
public static UnknownFieldSet DefaultInstance {
get { return defaultInstance; }
}
///
/// Returns a read-only view of the mapping from field numbers to values.
///
public IDictionary FieldDictionary {
get { return Dictionaries.AsReadOnly(fields); }
}
///
/// Checks whether or not the given field number is present in the set.
///
public bool HasField(int field) {
return fields.ContainsKey(field);
}
///
/// Fetches a field by number, returning an empty field if not present.
/// Never returns null.
///
public UnknownField this[int number] {
get {
UnknownField ret;
if (!fields.TryGetValue(number, out ret)) {
ret = UnknownField.DefaultInstance;
}
return ret;
}
}
///
/// 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 SerializedSize {
get {
int result = 0;
foreach (KeyValuePair entry in fields) {
result += entry.Value.GetSerializedSize(entry.Key);
}
return result;
}
}
///
/// Converts the set to a string in protocol buffer text format. This
/// is just a trivial wrapper around TextFormat.PrintToString.
///
public override String ToString() {
return TextFormat.PrintToString(this);
}
///
/// Serializes the message to a ByteString and returns it. This is
/// just a trivial wrapper around WriteTo(CodedOutputStream).
///
///
public ByteString ToByteString() {
ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
WriteTo(codedBuilder.CodedOutput);
return codedBuilder.Build();
}
///
/// Serializes the message to a byte array and returns it. This is
/// just a trivial wrapper around WriteTo(CodedOutputStream).
///
///
public byte[] ToByteArray() {
byte[] data = new byte[SerializedSize];
CodedOutputStream output = CodedOutputStream.CreateInstance(data);
WriteTo(output);
output.CheckNoSpaceLeft();
return data;
}
///
/// Serializes the message and writes it to . This is
/// just a trivial wrapper around WriteTo(CodedOutputStream).
///
///
public void WriteTo(Stream output) {
CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
WriteTo(codedOutput);
codedOutput.Flush();
}
///
/// Serializes the set and writes it to using
/// the MessageSet wire format.
///
public void WriteAsMessageSetTo(CodedOutputStream output) {
foreach (KeyValuePair entry in fields) {
entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
}
}
///
/// Gets the number of bytes required to encode this set using the MessageSet
/// wire format.
///
public int SerializedSizeAsMessageSet {
get {
int result = 0;
foreach (KeyValuePair entry in fields) {
result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
}
return result;
}
}
///
/// Parses an UnknownFieldSet from the given input.
///
public static UnknownFieldSet ParseFrom(CodedInputStream input) {
return CreateBuilder().MergeFrom(input).Build();
}
///
/// Parses an UnknownFieldSet from the given data.
///
public static UnknownFieldSet ParseFrom(ByteString data) {
return CreateBuilder().MergeFrom(data).Build();
}
///
/// Parses an UnknownFieldSet from the given data.
///
public static UnknownFieldSet ParseFrom(byte[] data) {
return CreateBuilder().MergeFrom(data).Build();
}
///
/// Parses an UnknownFieldSet from the given input.
///
public static UnknownFieldSet ParseFrom(Stream input) {
return CreateBuilder().MergeFrom(input).Build();
}
///
/// Builder for UnknownFieldSets.
///
public sealed class Builder
{
///
/// Mapping from number to field. Note that by using a SortedList we ensure
/// that the fields will be serialized in ascending order.
///
private IDictionary fields = new SortedList();
// Optimization: We keep around a builder for 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).
int lastFieldNumber;
UnknownField.Builder lastField;
internal Builder() {
}
///
/// Returns a field builder for the specified field number, including any values
/// which already exist.
///
private UnknownField.Builder GetFieldBuilder(int number) {
if (lastField != null) {
if (number == lastFieldNumber) {
return lastField;
}
// Note: AddField() will reset lastField and lastFieldNumber.
AddField(lastFieldNumber, lastField.Build());
}
if (number == 0) {
return null;
}
lastField = UnknownField.CreateBuilder();
UnknownField existing;
if (fields.TryGetValue(number, out existing)) {
lastField.MergeFrom(existing);
}
lastFieldNumber = number;
return lastField;
}
///
/// Build the UnknownFieldSet and return it. Once this method has been called,
/// this instance will no longer be usable. Calling any method after this
/// will throw a NullReferenceException.
///
public UnknownFieldSet Build() {
GetFieldBuilder(0); // Force lastField to be built.
UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
fields = null;
return result;
}
///
/// Adds a field to the set. If a field with the same number already exists, it
/// is replaced.
///
public Builder AddField(int number, UnknownField field) {
if (number == 0) {
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
if (lastField != null && lastFieldNumber == number) {
// Discard this.
lastField = null;
lastFieldNumber = 0;
}
fields[number] = field;
return this;
}
///
/// Resets the builder to an empty set.
///
public Builder Clear() {
fields.Clear();
lastFieldNumber = 0;
lastField = null;
return this;
}
///
/// Parse an entire message from and merge
/// its fields into this set.
///
public Builder MergeFrom(CodedInputStream input) {
while (true) {
uint tag = input.ReadTag();
if (tag == 0 || !MergeFieldFrom(tag, input)) {
break;
}
}
return this;
}
///
/// Parse a single field from and merge it
/// into this set.
///
/// The field's tag number, which was already parsed.
/// The coded input stream containing the field
/// false if the tag is an "end group" tag, true otherwise
public bool MergeFieldFrom(uint tag, CodedInputStream input) {
int number = WireFormat.GetTagFieldNumber(tag);
switch (WireFormat.GetTagWireType(tag)) {
case WireFormat.WireType.Varint:
// TODO(jonskeet): Check this is correct (different to Java)
GetFieldBuilder(number).AddVarint(input.ReadUInt64());
return true;
case WireFormat.WireType.Fixed64:
GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
return true;
case WireFormat.WireType.LengthDelimited:
GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
return true;
case WireFormat.WireType.StartGroup: {
Builder subBuilder = CreateBuilder();
input.ReadUnknownGroup(number, subBuilder);
GetFieldBuilder(number).AddGroup(subBuilder.Build());
return true;
}
case WireFormat.WireType.EndGroup:
return false;
case WireFormat.WireType.Fixed32:
GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
return true;
default:
throw InvalidProtocolBufferException.InvalidWireType();
}
}
///
/// Parses as an UnknownFieldSet and merge it
/// with the set being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
///
public Builder MergeFrom(Stream input) {
CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
MergeFrom(codedInput);
codedInput.CheckLastTagWas(0);
return this;
}
///
/// Parses as an UnknownFieldSet and merge it
/// with the set being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
///
public Builder MergeFrom(ByteString data) {
CodedInputStream input = data.CreateCodedInput();
MergeFrom(input);
input.CheckLastTagWas(0);
return this;
}
///
/// Parses as an UnknownFieldSet and merge it
/// with the set being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
///
public Builder MergeFrom(byte[] data) {
CodedInputStream input = CodedInputStream.CreateInstance(data);
MergeFrom(input);
input.CheckLastTagWas(0);
return this;
}
///
/// Convenience method for merging a new field containing a single varint
/// value. This is used in particular when an unknown enum value is
/// encountered.
///
public Builder MergeVarintField(int number, ulong value) {
if (number == 0) {
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
GetFieldBuilder(number).AddVarint(value);
return this;
}
///
/// 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.
///
public Builder MergeFrom(UnknownFieldSet other) {
if (other != DefaultInstance) {
foreach(KeyValuePair entry in other.fields) {
MergeField(entry.Key, entry.Value);
}
}
return this;
}
///
/// Checks if the given field number is present in the set.
///
public bool HasField(int number) {
if (number == 0) {
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
return number == lastFieldNumber || fields.ContainsKey(number);
}
///
/// Adds a field to the unknown field set. If a field with the same
/// number already exists, the two are merged.
///
public Builder MergeField(int number, UnknownField field) {
if (number == 0) {
throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
}
if (HasField(number)) {
GetFieldBuilder(number).MergeFrom(field);
} else {
// Optimization: We could call getFieldBuilder(number).mergeFrom(field)
// in this case, but that would create a copy of the Field object.
// We'd rather reuse the one passed to us, so call AddField() instead.
AddField(number, field);
}
return this;
}
internal void MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry, IBuilder builder) {
while (true) {
uint tag = input.ReadTag();
if (tag == 0) {
break;
}
if (!MergeFieldFrom(input, extensionRegistry, builder, tag)) {
// end group tag
break;
}
}
}
///
/// Like
/// but parses a single field.
///
/// The input to read the field from
/// Registry to use when an extension field is encountered
/// Builder to merge field into, if it's a known field
/// The tag, which should already have been read from the input
/// true unless the tag is an end-group tag
internal bool MergeFieldFrom(CodedInputStream input,
ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) {
MessageDescriptor type = builder.DescriptorForType;
if (type.Options.MessageSetWireFormat && tag == WireFormat.MessageSetTag.ItemStart) {
MergeMessageSetExtensionFromCodedStream(input, extensionRegistry, builder);
return true;
}
WireFormat.WireType wireType = WireFormat.GetTagWireType(tag);
int fieldNumber = WireFormat.GetTagFieldNumber(tag);
FieldDescriptor field;
IMessage defaultFieldInstance = null;
if (type.IsExtensionNumber(fieldNumber)) {
ExtensionInfo extension = extensionRegistry[type, fieldNumber];
if (extension == null) {
field = null;
} else {
field = extension.Descriptor;
defaultFieldInstance = extension.DefaultInstance;
}
} else {
field = type.FindFieldByNumber(fieldNumber);
}
// Unknown field or wrong wire type. Skip.
if (field == null || wireType != WireFormat.GetWireType(field.FieldType)) {
return MergeFieldFrom(tag, input);
}
object value;
switch (field.FieldType) {
case FieldType.Group:
case FieldType.Message: {
IBuilder subBuilder;
if (defaultFieldInstance != null) {
subBuilder = defaultFieldInstance.WeakCreateBuilderForType();
} else {
subBuilder = builder.CreateBuilderForField(field);
}
if (!field.IsRepeated) {
subBuilder.WeakMergeFrom((IMessage)builder[field]);
}
if (field.FieldType == FieldType.Group) {
input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry);
} else {
input.ReadMessage(subBuilder, extensionRegistry);
}
value = subBuilder.WeakBuild();
break;
}
case FieldType.Enum: {
int rawValue = input.ReadEnum();
value = field.EnumType.FindValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
if (value == null) {
MergeVarintField(fieldNumber, (ulong)rawValue);
return true;
}
break;
}
default:
value = input.ReadPrimitiveField(field.FieldType);
break;
}
if (field.IsRepeated) {
builder.WeakAddRepeatedField(field, value);
} else {
builder[field] = value;
}
return true;
}
///
/// Called by MergeFieldFrom to parse a MessageSet extension.
///
private void MergeMessageSetExtensionFromCodedStream(CodedInputStream input,
ExtensionRegistry extensionRegistry, IBuilder builder) {
MessageDescriptor type = builder.DescriptorForType;
// The wire format for MessageSet is:
// message MessageSet {
// repeated group Item = 1 {
// required int32 typeId = 2;
// required bytes message = 3;
// }
// }
// "typeId" is the extension's field number. The extension can only be
// a message type, where "message" contains the encoded bytes of that
// message.
//
// In practice, we will probably never see a MessageSet item in which
// the message appears before the type ID, or where either field does not
// appear exactly once. However, in theory such cases are valid, so we
// should be prepared to accept them.
int typeId = 0;
ByteString rawBytes = null; // If we encounter "message" before "typeId"
IBuilder subBuilder = null;
FieldDescriptor field = null;
while (true) {
uint tag = input.ReadTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.MessageSetTag.TypeID) {
typeId = input.ReadInt32();
// Zero is not a valid type ID.
if (typeId != 0) {
ExtensionInfo extension = extensionRegistry[type, typeId];
if (extension != null) {
field = extension.Descriptor;
subBuilder = extension.DefaultInstance.WeakCreateBuilderForType();
IMessage originalMessage = (IMessage)builder[field];
if (originalMessage != null) {
subBuilder.WeakMergeFrom(originalMessage);
}
if (rawBytes != null) {
// We already encountered the message. Parse it now.
// TODO(jonskeet): Check this is okay. It's subtly different from the Java, as it doesn't create an input stream from rawBytes.
// In fact, why don't we just call MergeFrom(rawBytes)? And what about the extension registry?
subBuilder.WeakMergeFrom(rawBytes.CreateCodedInput());
rawBytes = null;
}
} else {
// Unknown extension number. If we already saw data, put it
// in rawBytes.
if (rawBytes != null) {
MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(rawBytes).Build());
rawBytes = null;
}
}
}
} else if (tag == WireFormat.MessageSetTag.Message) {
if (typeId == 0) {
// We haven't seen a type ID yet, so we have to store the raw bytes for now.
rawBytes = input.ReadBytes();
} else if (subBuilder == null) {
// We don't know how to parse this. Ignore it.
MergeField(typeId, UnknownField.CreateBuilder().AddLengthDelimited(input.ReadBytes()).Build());
} else {
// We already know the type, so we can parse directly from the input
// with no copying. Hooray!
input.ReadMessage(subBuilder, extensionRegistry);
}
} else {
// Unknown tag. Skip it.
if (!input.SkipField(tag)) {
break; // end of group
}
}
}
input.CheckLastTagWas(WireFormat.MessageSetTag.ItemEnd);
if (subBuilder != null) {
builder[field] = subBuilder.WeakBuild();
}
}
}
}
}