From c0daf107249d95f454d489613de09a4898cefb20 Mon Sep 17 00:00:00 2001 From: Jon Skeet Date: Thu, 14 Aug 2008 20:33:35 +0100 Subject: Changed fixed size methods to return unsigned integers. Finished FieldSet. Introduced mapping from FieldType to WireType and MappedType. --- csharp/ProtocolBuffers/AbstractBuilder.cs | 6 +- csharp/ProtocolBuffers/Autogenerated.cs | 6 + csharp/ProtocolBuffers/CodedInputStream.cs | 83 +++++-- csharp/ProtocolBuffers/CodedOutputStream.cs | 14 +- .../ProtocolBuffers/Descriptors/EnumDescriptor.cs | 3 + .../Descriptors/EnumValueDescriptor.cs | 8 + .../ProtocolBuffers/Descriptors/FieldDescriptor.cs | 36 ++- .../Descriptors/FieldMappingAttribute.cs | 21 ++ csharp/ProtocolBuffers/Descriptors/FieldType.cs | 40 ++-- .../ProtocolBuffers/Descriptors/FileDescriptor.cs | 4 + .../Descriptors/MessageDescriptor.cs | 8 + .../FieldAccess/FieldAccessorTable.cs | 2 - csharp/ProtocolBuffers/FieldSet.cs | 249 ++++++++++++++++++++- csharp/ProtocolBuffers/GeneratedBuilder.cs | 12 +- csharp/ProtocolBuffers/IBuilder.cs | 8 +- csharp/ProtocolBuffers/ProtocolBuffers.csproj | 2 + csharp/ProtocolBuffers/UnknownField.cs | 24 +- csharp/ProtocolBuffers/WireFormat.cs | 38 ++++ 18 files changed, 480 insertions(+), 84 deletions(-) create mode 100644 csharp/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs create mode 100644 csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs (limited to 'csharp/ProtocolBuffers') diff --git a/csharp/ProtocolBuffers/AbstractBuilder.cs b/csharp/ProtocolBuffers/AbstractBuilder.cs index f8ed554d..8a8554e4 100644 --- a/csharp/ProtocolBuffers/AbstractBuilder.cs +++ b/csharp/ProtocolBuffers/AbstractBuilder.cs @@ -25,7 +25,7 @@ namespace Google.ProtocolBuffers { protected abstract IMessage BuildPartialImpl(); protected abstract IBuilder CloneImpl(); protected abstract IMessage DefaultInstanceForTypeImpl { get; } - protected abstract IBuilder NewBuilderForFieldImpl(FieldDescriptor field); + protected abstract IBuilder CreateBuilderForFieldImpl(FieldDescriptor field); protected abstract IBuilder ClearFieldImpl(FieldDescriptor field); protected abstract IBuilder AddRepeatedFieldImpl(FieldDescriptor field, object value); #endregion @@ -47,8 +47,8 @@ namespace Google.ProtocolBuffers { get { return DefaultInstanceForTypeImpl; } } - IBuilder IBuilder.NewBuilderForField(FieldDescriptor field) { - return NewBuilderForFieldImpl(field); + IBuilder IBuilder.CreateBuilderForField(FieldDescriptor field) { + return CreateBuilderForFieldImpl(field); } IBuilder IBuilder.ClearField(FieldDescriptor field) { diff --git a/csharp/ProtocolBuffers/Autogenerated.cs b/csharp/ProtocolBuffers/Autogenerated.cs index 5b999787..e4aee669 100644 --- a/csharp/ProtocolBuffers/Autogenerated.cs +++ b/csharp/ProtocolBuffers/Autogenerated.cs @@ -5,5 +5,11 @@ namespace Google.ProtocolBuffers { public class MessageOptions { public bool IsMessageSetWireFormat; } + + public class EnumValueDescriptorProto { + } + + public class FieldDescriptorProto { + } } } diff --git a/csharp/ProtocolBuffers/CodedInputStream.cs b/csharp/ProtocolBuffers/CodedInputStream.cs index d2a3657b..ff5d6f88 100644 --- a/csharp/ProtocolBuffers/CodedInputStream.cs +++ b/csharp/ProtocolBuffers/CodedInputStream.cs @@ -142,7 +142,7 @@ namespace Google.ProtocolBuffers { /// Read a double field from the stream. /// public double ReadDouble() { - return BitConverter.Int64BitsToDouble(ReadRawLittleEndian64()); + return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64()); } /// @@ -178,14 +178,14 @@ namespace Google.ProtocolBuffers { /// /// Read a fixed64 field from the stream. /// - public long ReadFixed64() { + public ulong ReadFixed64() { return ReadRawLittleEndian64(); } /// /// Read a fixed32 field from the stream. /// - public int ReadFixed32() { + public uint ReadFixed32() { return ReadRawLittleEndian32(); } @@ -293,14 +293,14 @@ namespace Google.ProtocolBuffers { /// Reads an sfixed32 field value from the stream. /// public int ReadSFixed32() { - return ReadRawLittleEndian32(); + return (int) ReadRawLittleEndian32(); } /// /// Reads an sfixed64 field value from the stream. /// public long ReadSFixed64() { - return ReadRawLittleEndian64(); + return (long) ReadRawLittleEndian64(); } /// @@ -410,26 +410,26 @@ namespace Google.ProtocolBuffers { /// /// Read a 32-bit little-endian integer from the stream. /// - public int ReadRawLittleEndian32() { - byte b1 = ReadRawByte(); - byte b2 = ReadRawByte(); - byte b3 = ReadRawByte(); - byte b4 = ReadRawByte(); + public uint ReadRawLittleEndian32() { + uint b1 = ReadRawByte(); + uint b2 = ReadRawByte(); + uint b3 = ReadRawByte(); + uint b4 = ReadRawByte(); return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); } /// /// Read a 64-bit little-endian integer from the stream. /// - public long ReadRawLittleEndian64() { - long b1 = ReadRawByte(); - long b2 = ReadRawByte(); - long b3 = ReadRawByte(); - long b4 = ReadRawByte(); - long b5 = ReadRawByte(); - long b6 = ReadRawByte(); - long b7 = ReadRawByte(); - long b8 = ReadRawByte(); + public ulong ReadRawLittleEndian64() { + ulong b1 = ReadRawByte(); + ulong b2 = ReadRawByte(); + ulong b3 = ReadRawByte(); + ulong b4 = ReadRawByte(); + ulong b5 = ReadRawByte(); + ulong b6 = ReadRawByte(); + ulong b7 = ReadRawByte(); + ulong b8 = ReadRawByte(); return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); } @@ -705,6 +705,51 @@ namespace Google.ProtocolBuffers { } } + /// + /// Reads and discards a single field, given its tag value. + /// + /// false if the tag is an end-group tag, in which case + /// nothing is skipped. Otherwise, returns true. + public bool SkipField(uint tag) { + switch (WireFormat.GetTagWireType(tag)) { + case WireFormat.WireType.Varint: + ReadInt32(); + return true; + case WireFormat.WireType.Fixed64: + ReadRawLittleEndian64(); + return true; + case WireFormat.WireType.LengthDelimited: + SkipRawBytes((int) ReadRawVarint32()); + return true; + case WireFormat.WireType.StartGroup: + SkipMessage(); + CheckLastTagWas( + WireFormat.MakeTag(WireFormat.GetTagFieldNumber(tag), + WireFormat.WireType.EndGroup)); + return true; + case WireFormat.WireType.EndGroup: + return false; + case WireFormat.WireType.Fixed32: + ReadRawLittleEndian32(); + return true; + default: + throw InvalidProtocolBufferException.InvalidWireType(); + } + } + + /// + /// Reads and discards an entire message. This will read either until EOF + /// or until an endgroup tag, whichever comes first. + /// + public void SkipMessage() { + while (true) { + uint tag = ReadTag(); + if (tag == 0 || !SkipField(tag)) { + return; + } + } + } + /// /// Reads and discards bytes. /// diff --git a/csharp/ProtocolBuffers/CodedOutputStream.cs b/csharp/ProtocolBuffers/CodedOutputStream.cs index 5fec5274..260db868 100644 --- a/csharp/ProtocolBuffers/CodedOutputStream.cs +++ b/csharp/ProtocolBuffers/CodedOutputStream.cs @@ -99,7 +99,7 @@ namespace Google.ProtocolBuffers { /// public void WriteDouble(int fieldNumber, double value) { WriteTag(fieldNumber, WireFormat.WireType.Fixed64); - WriteRawLittleEndian64(BitConverter.DoubleToInt64Bits(value)); + WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value)); } /// @@ -143,7 +143,7 @@ namespace Google.ProtocolBuffers { /// /// Writes a fixed64 field value, including tag, to the stream. /// - public void WriteFixed64(int fieldNumber, long value) { + public void WriteFixed64(int fieldNumber, ulong value) { WriteTag(fieldNumber, WireFormat.WireType.Fixed64); WriteRawLittleEndian64(value); } @@ -151,7 +151,7 @@ namespace Google.ProtocolBuffers { /// /// Writes a fixed32 field value, including tag, to the stream. /// - public void WriteFixed32(int fieldNumber, int value) { + public void WriteFixed32(int fieldNumber, uint value) { WriteTag(fieldNumber, WireFormat.WireType.Fixed32); WriteRawLittleEndian32(value); } @@ -259,8 +259,8 @@ namespace Google.ProtocolBuffers { case FieldType.Int64: WriteInt64(fieldNumber, (long)value); break; case FieldType.UInt64: WriteUInt64(fieldNumber, (ulong)value); break; case FieldType.Int32: WriteInt32(fieldNumber, (int)value); break; - case FieldType.Fixed64: WriteFixed64(fieldNumber, (long)value); break; - case FieldType.Fixed32: WriteFixed32(fieldNumber, (int)value); break; + case FieldType.Fixed64: WriteFixed64(fieldNumber, (ulong)value); break; + case FieldType.Fixed32: WriteFixed32(fieldNumber, (uint)value); break; case FieldType.Bool: WriteBool(fieldNumber, (bool)value); break; case FieldType.String: WriteString(fieldNumber, (string)value); break; case FieldType.Group: WriteGroup(fieldNumber, (IMessage)value); break; @@ -310,14 +310,14 @@ namespace Google.ProtocolBuffers { } } - public void WriteRawLittleEndian32(int value) { + public void WriteRawLittleEndian32(uint value) { WriteRawByte((byte)value); WriteRawByte((byte)(value >> 8)); WriteRawByte((byte)(value >> 16)); WriteRawByte((byte)(value >> 24)); } - public void WriteRawLittleEndian64(long value) { + public void WriteRawLittleEndian64(ulong value) { WriteRawByte((byte)value); WriteRawByte((byte)(value >> 8)); WriteRawByte((byte)(value >> 16)); diff --git a/csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs index c4f5c796..2d27b1f0 100644 --- a/csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs +++ b/csharp/ProtocolBuffers/Descriptors/EnumDescriptor.cs @@ -1,5 +1,8 @@  namespace Google.ProtocolBuffers.Descriptors { public class EnumDescriptor { + internal EnumValueDescriptor FindValueByNumber(int rawValue) { + throw new System.NotImplementedException(); + } } } diff --git a/csharp/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs index 48b3fb5c..faa83fd4 100644 --- a/csharp/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs +++ b/csharp/ProtocolBuffers/Descriptors/EnumValueDescriptor.cs @@ -2,6 +2,14 @@ namespace Google.ProtocolBuffers.Descriptors { public class EnumValueDescriptor { + + internal EnumValueDescriptor(DescriptorProtos.EnumValueDescriptorProto proto, + FileDescriptor file, + EnumDescriptor parent, + int index) { + enumDescriptor = parent; + } + private EnumDescriptor enumDescriptor; public int Number { diff --git a/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 45e7f964..cd2f19b1 100644 --- a/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/csharp/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -1,9 +1,19 @@ - -using System; +using System; +using System.Collections.Generic; +using System.Reflection; +using Google.ProtocolBuffers.Collections; namespace Google.ProtocolBuffers.Descriptors { public class FieldDescriptor { + private FieldDescriptor(DescriptorProtos.FieldDescriptorProto proto, + FileDescriptor file, + MessageDescriptor parent, + int index, + bool isExtension) { + enumType = null; + } + private EnumDescriptor enumType; public bool IsRequired { @@ -47,14 +57,28 @@ namespace Google.ProtocolBuffers.Descriptors { /// this will always be an empty list. For message fields it will /// always be null. For singular values, it will depend on the descriptor. /// - public object DefaultValue - { + public object DefaultValue { get { throw new NotImplementedException(); } } - public string Name - { + public string Name { get { throw new NotImplementedException(); } } + + /// + /// Immutable mapping from field type to mapped type. Built using the attributes on + /// FieldType values. + /// + public static readonly IDictionary FieldTypeToWireFormatMap = MapFieldTypes(); + + private static IDictionary MapFieldTypes() { + var map = new Dictionary(); + foreach (FieldInfo field in typeof(FieldType).GetFields(BindingFlags.Static | BindingFlags.Public)) { + FieldType fieldType = (FieldType)field.GetValue(null); + FieldMappingAttribute mapping = (FieldMappingAttribute)field.GetCustomAttributes(typeof(FieldMappingAttribute), false)[0]; + map[fieldType] = mapping.MappedType; + } + return Dictionaries.AsReadOnly(map); + } } } diff --git a/csharp/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs b/csharp/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs new file mode 100644 index 00000000..13e8be1e --- /dev/null +++ b/csharp/ProtocolBuffers/Descriptors/FieldMappingAttribute.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Google.ProtocolBuffers.Descriptors { + + /// + /// Defined specifically for the enumeration, + /// this allows each field type to specify the mapped type and wire type. + /// + [AttributeUsage(AttributeTargets.Field)] + internal class FieldMappingAttribute : Attribute { + internal FieldMappingAttribute(MappedType mappedType, WireFormat.WireType wireType) { + MappedType = mappedType; + WireType = wireType; + } + + internal MappedType MappedType { get; private set; } + internal WireFormat.WireType WireType { get; private set; } + } +} diff --git a/csharp/ProtocolBuffers/Descriptors/FieldType.cs b/csharp/ProtocolBuffers/Descriptors/FieldType.cs index d54ded53..30b82d69 100644 --- a/csharp/ProtocolBuffers/Descriptors/FieldType.cs +++ b/csharp/ProtocolBuffers/Descriptors/FieldType.cs @@ -1,23 +1,27 @@  namespace Google.ProtocolBuffers.Descriptors { + /// + /// Enumeration of all the possible field types. The odd formatting is to make it very clear + /// which attribute applies to which value, while maintaining a compact format. + /// public enum FieldType { - Double, - Float, - Int64, - UInt64, - Int32, - Fixed64, - Fixed32, - Bool, - String, - Group, - Message, - Bytes, - UInt32, - SFixed32, - SFixed64, - SInt32, - SInt64, - Enum + [FieldMapping(MappedType.Double, WireFormat.WireType.Fixed64)] Double, + [FieldMapping(MappedType.Single, WireFormat.WireType.Fixed32)] Float, + [FieldMapping(MappedType.Int64, WireFormat.WireType.Varint)] Int64, + [FieldMapping(MappedType.UInt64, WireFormat.WireType.Varint)] UInt64, + [FieldMapping(MappedType.Int32, WireFormat.WireType.Varint)] Int32, + [FieldMapping(MappedType.UInt64, WireFormat.WireType.Fixed64)] Fixed64, + [FieldMapping(MappedType.UInt32, WireFormat.WireType.Fixed32)] Fixed32, + [FieldMapping(MappedType.Boolean, WireFormat.WireType.Varint)] Bool, + [FieldMapping(MappedType.String, WireFormat.WireType.LengthDelimited)] String, + [FieldMapping(MappedType.Message, WireFormat.WireType.StartGroup)] Group, + [FieldMapping(MappedType.Message, WireFormat.WireType.LengthDelimited)] Message, + [FieldMapping(MappedType.ByteString, WireFormat.WireType.LengthDelimited)] Bytes, + [FieldMapping(MappedType.UInt32, WireFormat.WireType.Varint)] UInt32, + [FieldMapping(MappedType.Int32, WireFormat.WireType.Fixed32)] SFixed32, + [FieldMapping(MappedType.Int64, WireFormat.WireType.Fixed64)] SFixed64, + [FieldMapping(MappedType.Int32, WireFormat.WireType.Varint)] SInt32, + [FieldMapping(MappedType.Int64, WireFormat.WireType.Varint)] SInt64, + [FieldMapping(MappedType.Enum, WireFormat.WireType.Varint)] Enum } } diff --git a/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs new file mode 100644 index 00000000..0aaca886 --- /dev/null +++ b/csharp/ProtocolBuffers/Descriptors/FileDescriptor.cs @@ -0,0 +1,4 @@ +namespace Google.ProtocolBuffers.Descriptors { + class FileDescriptor { + } +} diff --git a/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs b/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs index 4e0834f0..df2972f6 100644 --- a/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs +++ b/csharp/ProtocolBuffers/Descriptors/MessageDescriptor.cs @@ -6,5 +6,13 @@ namespace Google.ProtocolBuffers.Descriptors { public IList Fields; public DescriptorProtos.MessageOptions Options; public string FullName; + + internal bool IsExtensionNumber(int fieldNumber) { + throw new System.NotImplementedException(); + } + + internal FieldDescriptor FindFieldByNumber(int fieldNumber) { + throw new System.NotImplementedException(); + } } } diff --git a/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs b/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs index d7b239c1..cf5220f6 100644 --- a/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs +++ b/csharp/ProtocolBuffers/FieldAccess/FieldAccessorTable.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using Google.ProtocolBuffers.Descriptors; namespace Google.ProtocolBuffers.FieldAccess { diff --git a/csharp/ProtocolBuffers/FieldSet.cs b/csharp/ProtocolBuffers/FieldSet.cs index 9ab13c65..4ad94417 100644 --- a/csharp/ProtocolBuffers/FieldSet.cs +++ b/csharp/ProtocolBuffers/FieldSet.cs @@ -104,14 +104,195 @@ namespace Google.ProtocolBuffers { } // TODO(jonskeet): Should this be in UnknownFieldSet.Builder really? + /// + /// Like + /// but parses a single field. + /// + /// The input to read the field from + /// The set of unknown fields to add the newly-read field to + /// Registry to use when an extension field is encountered + /// A builder (???) + /// The tag, which should already have been read from the input + /// true unless the tag is an end-group tag internal static bool MergeFieldFrom(CodedInputStream input, UnknownFieldSet.Builder unknownFields, ExtensionRegistry extensionRegistry, IBuilder builder, uint tag) { - throw new NotImplementedException(); + MessageDescriptor type = builder.DescriptorForType; + + if (type.Options.IsMessageSetWireFormat + && tag == WireFormat.MessageSetTag.ItemStart) { + MergeMessageSetExtensionFromCodedStream(input, unknownFields, extensionRegistry, builder); + return true; + } + + WireFormat.WireType wireType = WireFormat.GetTagWireType(tag); + int fieldNumber = WireFormat.GetTagFieldNumber(tag); + + FieldDescriptor field; + IMessage defaultInstance = null; + + if (type.IsExtensionNumber(fieldNumber)) { + ExtensionInfo extension = extensionRegistry[type, fieldNumber]; + if (extension == null) { + field = null; + } else { + field = extension.Descriptor; + defaultInstance = extension.DefaultInstance; + } + } else { + field = type.FindFieldByNumber(fieldNumber); + } + + // Unknown field or wrong wire type. Skip. + if (field == null || wireType != WireFormat.FieldTypeToWireFormatMap[field.FieldType]) { + return unknownFields.MergeFieldFrom(tag, input); + } + + object value; + switch (field.FieldType) { + case FieldType.Group: + case FieldType.Message: { + IBuilder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.CreateBuilderForType(); + } else { + subBuilder = builder.CreateBuilderForField(field); + } + if (!field.IsRepeated) { + subBuilder.MergeFrom((IMessage) builder[field]); + } + if (field.FieldType == FieldType.Group) { + input.ReadGroup(field.FieldNumber, subBuilder, extensionRegistry); + } else { + input.ReadMessage(subBuilder, extensionRegistry); + } + value = subBuilder.Build(); + 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) { + unknownFields.MergeVarintField(fieldNumber, (ulong) rawValue); + return true; + } + break; + } + default: + value = input.ReadPrimitiveField(field.FieldType); + break; + } + if (field.IsRepeated) { + builder.AddRepeatedField(field, value); + } else { + builder[field] = value; + } + return true; } + // TODO(jonskeet): Move to UnknownFieldSet.Builder? + /// + /// Called by MergeFieldFrom to parse a MessageSet extension. + /// + private static void MergeMessageSetExtensionFromCodedStream(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + 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.CreateBuilderForType(); + IMessage originalMessage = (IMessage) builder[field]; + if (originalMessage != null) { + subBuilder.MergeFrom(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.MergeFrom(rawBytes.CreateCodedInput()); + rawBytes = null; + } + } else { + // Unknown extension number. If we already saw data, put it + // in rawBytes. + if (rawBytes != null) { + unknownFields.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. + unknownFields.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.Build(); + } + } + + /// /// Clears all fields. /// @@ -120,7 +301,7 @@ namespace Google.ProtocolBuffers { } /// - /// + /// See /// /// /// If the field is not set, the behaviour when fetching this property varies by field type: @@ -172,7 +353,7 @@ namespace Google.ProtocolBuffers { } /// - /// + /// See /// internal object this[FieldDescriptor field, int index] { get { @@ -196,7 +377,7 @@ namespace Google.ProtocolBuffers { } /// - /// + /// See /// /// /// @@ -214,7 +395,7 @@ namespace Google.ProtocolBuffers { } /// - /// + /// See /// /// /// Since FieldSet itself does not have any way of knowing about @@ -259,14 +440,14 @@ namespace Google.ProtocolBuffers { } /// - /// + /// See /// public void ClearField(FieldDescriptor field) { fields.Remove(field); } /// - /// + /// See /// public int GetRepeatedFieldCount(FieldDescriptor field) { if (!field.IsRepeated) { @@ -276,6 +457,60 @@ namespace Google.ProtocolBuffers { return ((List) this[field]).Count; } + /// + /// Implementation of both MergeFrom methods. + /// + /// + private void MergeFields(IEnumerable> otherFields) { + // Note: We don't attempt to verify that other's fields have valid + // types. Doing so would be a losing battle. We'd have to verify + // all sub-messages as well, and we'd have to make copies of all of + // them to insure that they don't change after verification (since + // the IMessage interface itself cannot enforce immutability of + // implementations). + // TODO(jonskeet): Provide a function somewhere called MakeDeepCopy() + // which allows people to make secure deep copies of messages. + + foreach (KeyValuePair entry in otherFields) { + FieldDescriptor field = entry.Key; + object existingValue; + fields.TryGetValue(field, out existingValue); + if (field.IsRepeated) { + if (existingValue == null) { + existingValue = new List(); + fields[field] = existingValue; + } + List list = (List)existingValue; + foreach (object otherValue in (IEnumerable)entry.Value) { + list.Add(otherValue); + } + } else if (field.MappedType == MappedType.Message && existingValue != null) { + IMessage existingMessage = (IMessage)existingValue; + IMessage merged = existingMessage.CreateBuilderForType() + .MergeFrom(existingMessage) + .MergeFrom((IMessage)entry.Value) + .Build(); + this[field] = merged; + } else { + this[field] = entry.Value; + } + } + } + + /// + /// See + /// + public void MergeFrom(IMessage other) { + MergeFields(other.AllFields); + } + + /// + /// Like , but merges from another FieldSet. + /// + public void MergeFrom(FieldSet other) { + MergeFields(other.fields); + } + /// /// Verifies that the given object is of the correct type to be a valid /// value for the given field. diff --git a/csharp/ProtocolBuffers/GeneratedBuilder.cs b/csharp/ProtocolBuffers/GeneratedBuilder.cs index ffb794f6..a98dbccc 100644 --- a/csharp/ProtocolBuffers/GeneratedBuilder.cs +++ b/csharp/ProtocolBuffers/GeneratedBuilder.cs @@ -73,8 +73,8 @@ namespace Google.ProtocolBuffers { get { return DefaultInstanceForType; } } - protected override IBuilder NewBuilderForFieldImpl(FieldDescriptor field) { - return NewBuilderForField(field); + protected override IBuilder CreateBuilderForFieldImpl(FieldDescriptor field) { + return CreateBuilderForField(field); } protected override IBuilder ClearFieldImpl(FieldDescriptor field) { @@ -91,9 +91,9 @@ namespace Google.ProtocolBuffers { throw new NotImplementedException(); } - public abstract IMessage Build(); + public abstract TMessage Build(); - public abstract IMessage BuildPartial(); + public abstract TMessage BuildPartial(); public abstract IBuilder Clone(); @@ -105,11 +105,11 @@ namespace Google.ProtocolBuffers { throw new NotImplementedException(); } - public IMessage DefaultInstanceForType { + public TMessage DefaultInstanceForType { get { throw new NotImplementedException(); } } - public IBuilder NewBuilderForField(FieldDescriptor field) { + public IBuilder CreateBuilderForField(FieldDescriptor field) { throw new NotImplementedException(); } diff --git a/csharp/ProtocolBuffers/IBuilder.cs b/csharp/ProtocolBuffers/IBuilder.cs index f1366988..02b3b59b 100644 --- a/csharp/ProtocolBuffers/IBuilder.cs +++ b/csharp/ProtocolBuffers/IBuilder.cs @@ -88,7 +88,7 @@ namespace Google.ProtocolBuffers { IBuilder MergeFrom(CodedInputStream input); IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry); IMessage DefaultInstanceForType { get; } - IBuilder NewBuilderForField(FieldDescriptor field); + IBuilder CreateBuilderForField(FieldDescriptor field); IBuilder ClearField(FieldDescriptor field); IBuilder AddRepeatedField(FieldDescriptor field, object value); IBuilder MergeUnknownFields(UnknownFieldSet unknownFields); @@ -136,14 +136,14 @@ namespace Google.ProtocolBuffers { /// the message /// is missing one or more required fields; use BuildPartial to bypass /// this check - new IMessage Build(); + new T Build(); /// /// Like Build(), but does not throw an exception if the message is missing /// required fields. Instead, a partial message is returned. /// /// - new IMessage BuildPartial(); + new T BuildPartial(); /// /// Clones this builder. @@ -184,7 +184,7 @@ namespace Google.ProtocolBuffers { /// Get's the message's type's default instance. /// /// - new IMessage DefaultInstanceForType { get; } + new T DefaultInstanceForType { get; } /// /// Create a builder for messages of the appropriate type for the given field. diff --git a/csharp/ProtocolBuffers/ProtocolBuffers.csproj b/csharp/ProtocolBuffers/ProtocolBuffers.csproj index ea30503b..d3cd95f6 100644 --- a/csharp/ProtocolBuffers/ProtocolBuffers.csproj +++ b/csharp/ProtocolBuffers/ProtocolBuffers.csproj @@ -48,7 +48,9 @@ + + diff --git a/csharp/ProtocolBuffers/UnknownField.cs b/csharp/ProtocolBuffers/UnknownField.cs index 1866cbba..e1c12b72 100644 --- a/csharp/ProtocolBuffers/UnknownField.cs +++ b/csharp/ProtocolBuffers/UnknownField.cs @@ -24,14 +24,14 @@ namespace Google.ProtocolBuffers { private static readonly UnknownField defaultInstance = CreateBuilder().Build(); private readonly ReadOnlyCollection varintList; - private readonly ReadOnlyCollection fixed32List; - private readonly ReadOnlyCollection fixed64List; + private readonly ReadOnlyCollection fixed32List; + private readonly ReadOnlyCollection fixed64List; private readonly ReadOnlyCollection lengthDelimitedList; private readonly ReadOnlyCollection groupList; private UnknownField(ReadOnlyCollection varintList, - ReadOnlyCollection fixed32List, - ReadOnlyCollection fixed64List, + ReadOnlyCollection fixed32List, + ReadOnlyCollection fixed64List, ReadOnlyCollection lengthDelimitedList, ReadOnlyCollection groupList) { this.varintList = varintList; @@ -55,14 +55,14 @@ namespace Google.ProtocolBuffers { /// /// The list of fixed32 values for this field. /// - public IList Fixed32List { + public IList Fixed32List { get { return fixed32List; } } /// /// The list of fixed64 values for this field. /// - public IList Fixed64List { + public IList Fixed64List { get { return fixed64List; } } @@ -104,10 +104,10 @@ namespace Google.ProtocolBuffers { foreach (ulong value in varintList) { output.WriteUInt64(fieldNumber, value); } - foreach (int value in fixed32List) { + foreach (uint value in fixed32List) { output.WriteFixed32(fieldNumber, value); } - foreach (long value in fixed64List) { + foreach (ulong value in fixed64List) { output.WriteFixed64(fieldNumber, value); } foreach (ByteString value in lengthDelimitedList) { @@ -172,8 +172,8 @@ namespace Google.ProtocolBuffers { public class Builder { private List varintList; - private List fixed32List; - private List fixed64List; + private List fixed32List; + private List fixed64List; private List lengthDelimitedList; private List groupList; @@ -246,7 +246,7 @@ namespace Google.ProtocolBuffers { /// /// Adds a fixed32 value. /// - public Builder AddFixed32(int value) { + public Builder AddFixed32(uint value) { fixed32List = Add(fixed32List, value); return this; } @@ -254,7 +254,7 @@ namespace Google.ProtocolBuffers { /// /// Adds a fixed64 value. /// - public Builder AddFixed64(long value) { + public Builder AddFixed64(ulong value) { fixed64List = Add(fixed64List, value); return this; } diff --git a/csharp/ProtocolBuffers/WireFormat.cs b/csharp/ProtocolBuffers/WireFormat.cs index 0045e369..2166db48 100644 --- a/csharp/ProtocolBuffers/WireFormat.cs +++ b/csharp/ProtocolBuffers/WireFormat.cs @@ -13,7 +13,21 @@ // 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.Reflection; +using Google.ProtocolBuffers.Descriptors; +using System.Collections.Generic; +using Google.ProtocolBuffers.Collections; namespace Google.ProtocolBuffers { + + /// + /// This class is used internally by the Protocol Buffer Library and generated + /// message implementations. It is public only for the sake of those generated + /// messages. Others should not use this class directly. + /// + /// This class contains constants and helper functions useful for dealing with + /// the Protocol Buffer wire format. + /// + /// public class WireFormat { public enum WireType : uint { Varint = 0, @@ -30,6 +44,13 @@ namespace Google.ProtocolBuffers { internal const int Message = 3; } + internal class MessageSetTag { + internal static readonly uint ItemStart = MakeTag(MessageSetField.Item, WireType.StartGroup); + internal static readonly uint ItemEnd = MakeTag(MessageSetField.Item, WireType.EndGroup); + internal static readonly uint TypeID = MakeTag(MessageSetField.TypeID, WireType.Varint); + internal static readonly uint Message = MakeTag(MessageSetField.Message, WireType.LengthDelimited); + } + private const int TagTypeBits = 3; private const uint TagTypeMask = (1 << TagTypeBits) - 1; @@ -49,9 +70,26 @@ namespace Google.ProtocolBuffers { /// /// Makes a tag value given a field number and wire type. + /// TODO(jonskeet): Should we just have a Tag structure? /// public static uint MakeTag(int fieldNumber, WireType wireType) { return (uint) (fieldNumber << TagTypeBits) | (uint) wireType; } + + /// + /// Immutable mapping from field type to wire type. Built using the attributes on + /// FieldType values. + /// + public static readonly IDictionary FieldTypeToWireFormatMap = MapFieldTypes(); + + private static IDictionary MapFieldTypes() { + var map = new Dictionary(); + foreach (FieldInfo field in typeof(FieldType).GetFields(BindingFlags.Static | BindingFlags.Public)) { + FieldType fieldType = (FieldType) field.GetValue(null); + FieldMappingAttribute mapping = (FieldMappingAttribute)field.GetCustomAttributes(typeof(FieldMappingAttribute), false)[0]; + map[fieldType] = mapping.WireType; + } + return Dictionaries.AsReadOnly(map); + } } } -- cgit v1.2.3