Commit c0daf107 authored by Jon Skeet's avatar Jon Skeet

Changed fixed size methods to return unsigned integers. Finished FieldSet.…

Changed fixed size methods to return unsigned integers. Finished FieldSet. Introduced mapping from FieldType to WireType and MappedType.
parent 800f65e2
......@@ -118,7 +118,7 @@ namespace Google.ProtocolBuffers {
/// Parses the given bytes using ReadRawLittleEndian32() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertReadLittleEndian32(byte[] data, int value) {
private static void AssertReadLittleEndian32(byte[] data, uint value) {
CodedInputStream input = CodedInputStream.CreateInstance(data);
Assert.AreEqual(value, input.ReadRawLittleEndian32());
......@@ -134,7 +134,7 @@ namespace Google.ProtocolBuffers {
/// Parses the given bytes using ReadRawLittleEndian64() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertReadLittleEndian64(byte[] data, long value) {
private static void AssertReadLittleEndian64(byte[] data, ulong value) {
CodedInputStream input = CodedInputStream.CreateInstance(data);
Assert.AreEqual(value, input.ReadRawLittleEndian64());
......@@ -149,12 +149,12 @@ namespace Google.ProtocolBuffers {
[Test]
public void ReadLittleEndian() {
AssertReadLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), unchecked((int)0x9abcdef0));
AssertReadLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
AssertReadLittleEndian64(Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
0x123456789abcdef0L);
AssertReadLittleEndian64(
Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), unchecked((long)0x9abcdef012345678L));
Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef012345678UL);
}
[Test]
......
......@@ -106,7 +106,7 @@ namespace Google.ProtocolBuffers {
AssertWriteVarint(
Bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49),
(0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) |
(0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49));
(0x43UL << 28) | (0x49L << 35) | (0x24UL << 42) | (0x49UL << 49));
// 11964378330978735131
AssertWriteVarint(
Bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01),
......@@ -120,7 +120,7 @@ namespace Google.ProtocolBuffers {
/// Parses the given bytes using WriteRawLittleEndian32() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertWriteLittleEndian32(byte[] data, int value) {
private static void AssertWriteLittleEndian32(byte[] data, uint value) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawLittleEndian32(value);
......@@ -141,7 +141,7 @@ namespace Google.ProtocolBuffers {
/// Parses the given bytes using WriteRawLittleEndian64() and checks
/// that the result matches the given value.
/// </summary>
private static void AssertWriteLittleEndian64(byte[] data, long value) {
private static void AssertWriteLittleEndian64(byte[] data, ulong value) {
MemoryStream rawOutput = new MemoryStream();
CodedOutputStream output = CodedOutputStream.CreateInstance(rawOutput);
output.WriteRawLittleEndian64(value);
......@@ -164,14 +164,14 @@ namespace Google.ProtocolBuffers {
[Test]
public void WriteLittleEndian() {
AssertWriteLittleEndian32(Bytes(0x78, 0x56, 0x34, 0x12), 0x12345678);
AssertWriteLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), unchecked((int)0x9abcdef0));
AssertWriteLittleEndian32(Bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0);
AssertWriteLittleEndian64(
Bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12),
0x123456789abcdef0L);
AssertWriteLittleEndian64(
Bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a),
unchecked((long)0x9abcdef012345678L));
0x9abcdef012345678UL);
}
/* TODO(jonskeet): Put this back when we've got the rest working!
......
......@@ -48,6 +48,7 @@
<Compile Include="CodedInputStreamTest.cs" />
<Compile Include="CodedOutputStreamTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WireFormatTest.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
......
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
[TestFixture]
public class WireFormatTest {
[Test]
public void FieldTypeToWireTypeMapping() {
// Just test a few values
Assert.AreEqual(WireFormat.WireType.Fixed64, WireFormat.FieldTypeToWireFormatMap[FieldType.SFixed64]);
Assert.AreEqual(WireFormat.WireType.LengthDelimited, WireFormat.FieldTypeToWireFormatMap[FieldType.String]);
Assert.AreEqual(WireFormat.WireType.LengthDelimited, WireFormat.FieldTypeToWireFormatMap[FieldType.Message]);
}
}
}
......@@ -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) {
......
......@@ -5,5 +5,11 @@ namespace Google.ProtocolBuffers {
public class MessageOptions {
public bool IsMessageSetWireFormat;
}
public class EnumValueDescriptorProto {
}
public class FieldDescriptorProto {
}
}
}
......@@ -142,7 +142,7 @@ namespace Google.ProtocolBuffers {
/// Read a double field from the stream.
/// </summary>
public double ReadDouble() {
return BitConverter.Int64BitsToDouble(ReadRawLittleEndian64());
return BitConverter.Int64BitsToDouble((long) ReadRawLittleEndian64());
}
/// <summary>
......@@ -178,14 +178,14 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Read a fixed64 field from the stream.
/// </summary>
public long ReadFixed64() {
public ulong ReadFixed64() {
return ReadRawLittleEndian64();
}
/// <summary>
/// Read a fixed32 field from the stream.
/// </summary>
public int ReadFixed32() {
public uint ReadFixed32() {
return ReadRawLittleEndian32();
}
......@@ -293,14 +293,14 @@ namespace Google.ProtocolBuffers {
/// Reads an sfixed32 field value from the stream.
/// </summary>
public int ReadSFixed32() {
return ReadRawLittleEndian32();
return (int) ReadRawLittleEndian32();
}
/// <summary>
/// Reads an sfixed64 field value from the stream.
/// </summary>
public long ReadSFixed64() {
return ReadRawLittleEndian64();
return (long) ReadRawLittleEndian64();
}
/// <summary>
......@@ -410,26 +410,26 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Read a 32-bit little-endian integer from the stream.
/// </summary>
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);
}
/// <summary>
/// Read a 64-bit little-endian integer from the stream.
/// </summary>
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 {
}
}
/// <summary>
/// Reads and discards a single field, given its tag value.
/// </summary>
/// <returns>false if the tag is an end-group tag, in which case
/// nothing is skipped. Otherwise, returns true.</returns>
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();
}
}
/// <summary>
/// Reads and discards an entire message. This will read either until EOF
/// or until an endgroup tag, whichever comes first.
/// </summary>
public void SkipMessage() {
while (true) {
uint tag = ReadTag();
if (tag == 0 || !SkipField(tag)) {
return;
}
}
}
/// <summary>
/// Reads and discards <paramref name="size"/> bytes.
/// </summary>
......
......@@ -99,7 +99,7 @@ namespace Google.ProtocolBuffers {
/// </summary>
public void WriteDouble(int fieldNumber, double value) {
WriteTag(fieldNumber, WireFormat.WireType.Fixed64);
WriteRawLittleEndian64(BitConverter.DoubleToInt64Bits(value));
WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value));
}
/// <summary>
......@@ -143,7 +143,7 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Writes a fixed64 field value, including tag, to the stream.
/// </summary>
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 {
/// <summary>
/// Writes a fixed32 field value, including tag, to the stream.
/// </summary>
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));
......

namespace Google.ProtocolBuffers.Descriptors {
public class EnumDescriptor {
internal EnumValueDescriptor FindValueByNumber(int rawValue) {
throw new System.NotImplementedException();
}
}
}
......@@ -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 {
......

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.
/// </summary>
public object DefaultValue
{
public object DefaultValue {
get { throw new NotImplementedException(); }
}
public string Name
{
public string Name {
get { throw new NotImplementedException(); }
}
/// <summary>
/// Immutable mapping from field type to mapped type. Built using the attributes on
/// FieldType values.
/// </summary>
public static readonly IDictionary<FieldType, MappedType> FieldTypeToWireFormatMap = MapFieldTypes();
private static IDictionary<FieldType, MappedType> MapFieldTypes() {
var map = new Dictionary<FieldType, MappedType>();
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);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers.Descriptors {
/// <summary>
/// Defined specifically for the <see cref="FieldType" /> enumeration,
/// this allows each field type to specify the mapped type and wire type.
/// </summary>
[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; }
}
}

namespace Google.ProtocolBuffers.Descriptors {
/// <summary>
/// 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.
/// </summary>
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
}
}
namespace Google.ProtocolBuffers.Descriptors {
class FileDescriptor {
}
}
......@@ -6,5 +6,13 @@ namespace Google.ProtocolBuffers.Descriptors {
public IList<FieldDescriptor> 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();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.FieldAccess {
......
......@@ -104,14 +104,195 @@ namespace Google.ProtocolBuffers {
}
// TODO(jonskeet): Should this be in UnknownFieldSet.Builder really?
/// <summary>
/// Like <see cref="MergeFrom(CodedInputStream, UnknownFieldSet.Builder, ExtensionRegistry, IBuilder)" />
/// but parses a single field.
/// </summary>
/// <param name="input">The input to read the field from</param>
/// <param name="unknownFields">The set of unknown fields to add the newly-read field to</param>
/// <param name="extensionRegistry">Registry to use when an extension field is encountered</param>
/// <param name="builder">A builder (???)</param>
/// <param name="tag">The tag, which should already have been read from the input</param>
/// <returns>true unless the tag is an end-group tag</returns>
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?
/// <summary>
/// Called by MergeFieldFrom to parse a MessageSet extension.
/// </summary>
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();
}
}
/// <summary>
/// Clears all fields.
/// </summary>
......@@ -120,7 +301,7 @@ namespace Google.ProtocolBuffers {
}
/// <summary>
/// <see cref="IMessage.Item(FieldDescriptor)"/>
/// See <see cref="IMessage.Item(FieldDescriptor)"/>
/// </summary>
/// <remarks>
/// If the field is not set, the behaviour when fetching this property varies by field type:
......@@ -172,7 +353,7 @@ namespace Google.ProtocolBuffers {
}
/// <summary>
/// <see cref="IMessage.Item(FieldDescriptor,int)" />
/// See <see cref="IMessage.Item(FieldDescriptor,int)" />
/// </summary>
internal object this[FieldDescriptor field, int index] {
get {
......@@ -196,7 +377,7 @@ namespace Google.ProtocolBuffers {
}
/// <summary>
/// <see cref="IBuilder.AddRepeatedField" />
/// See <see cref="IBuilder.AddRepeatedField" />
/// </summary>
/// <param name="field"></param>
/// <param name="value"></param>
......@@ -214,7 +395,7 @@ namespace Google.ProtocolBuffers {
}
/// <summary>
/// <see cref="IMessage.IsInitialized" />
/// See <see cref="IMessage.IsInitialized" />
/// </summary>
/// <remarks>
/// Since FieldSet itself does not have any way of knowing about
......@@ -259,14 +440,14 @@ namespace Google.ProtocolBuffers {
}
/// <summary>
/// <see cref="IBuilder.ClearField" />
/// See <see cref="IBuilder.ClearField" />
/// </summary>
public void ClearField(FieldDescriptor field) {
fields.Remove(field);
}
/// <summary>
/// <see cref="IMessage.GetRepeatedFieldCount" />
/// See <see cref="IMessage.GetRepeatedFieldCount" />
/// </summary>
public int GetRepeatedFieldCount(FieldDescriptor field) {
if (!field.IsRepeated) {
......@@ -276,6 +457,60 @@ namespace Google.ProtocolBuffers {
return ((List<object>) this[field]).Count;
}
/// <summary>
/// Implementation of both <c>MergeFrom</c> methods.
/// </summary>
/// <param name="otherFields"></param>
private void MergeFields(IEnumerable<KeyValuePair<FieldDescriptor, object>> 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<FieldDescriptor, object> entry in otherFields) {
FieldDescriptor field = entry.Key;
object existingValue;
fields.TryGetValue(field, out existingValue);
if (field.IsRepeated) {
if (existingValue == null) {
existingValue = new List<object>();
fields[field] = existingValue;
}
List<object> list = (List<object>)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;
}
}
}
/// <summary>
/// See <see cref="IBuilder.MergeFrom(IMessage)" />
/// </summary>
public void MergeFrom(IMessage other) {
MergeFields(other.AllFields);
}
/// <summary>
/// Like <see cref="MergeFrom(IMessage)"/>, but merges from another <c>FieldSet</c>.
/// </summary>
public void MergeFrom(FieldSet other) {
MergeFields(other.fields);
}
/// <summary>
/// Verifies that the given object is of the correct type to be a valid
/// value for the given field.
......
......@@ -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<TMessage> Build();
public abstract TMessage Build();
public abstract IMessage<TMessage> BuildPartial();
public abstract TMessage BuildPartial();
public abstract IBuilder<TMessage> Clone();
......@@ -105,11 +105,11 @@ namespace Google.ProtocolBuffers {
throw new NotImplementedException();
}
public IMessage<TMessage> DefaultInstanceForType {
public TMessage DefaultInstanceForType {
get { throw new NotImplementedException(); }
}
public IBuilder NewBuilderForField(FieldDescriptor field) {
public IBuilder CreateBuilderForField(FieldDescriptor field) {
throw new NotImplementedException();
}
......
......@@ -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 {
/// <exception cref="UninitializedMessageException">the message
/// is missing one or more required fields; use BuildPartial to bypass
/// this check</exception>
new IMessage<T> Build();
new T Build();
/// <summary>
/// Like Build(), but does not throw an exception if the message is missing
/// required fields. Instead, a partial message is returned.
/// </summary>
/// <returns></returns>
new IMessage<T> BuildPartial();
new T BuildPartial();
/// <summary>
/// Clones this builder.
......@@ -184,7 +184,7 @@ namespace Google.ProtocolBuffers {
/// Get's the message's type's default instance.
/// <see cref="IMessage{T}.DefaultInstanceForType" />
/// </summary>
new IMessage<T> DefaultInstanceForType { get; }
new T DefaultInstanceForType { get; }
/// <summary>
/// Create a builder for messages of the appropriate type for the given field.
......
......@@ -48,7 +48,9 @@
<Compile Include="Descriptors\EnumDescriptor.cs" />
<Compile Include="Descriptors\EnumValueDescriptor.cs" />
<Compile Include="Descriptors\FieldDescriptor.cs" />
<Compile Include="Descriptors\FieldMappingAttribute.cs" />
<Compile Include="Descriptors\FieldType.cs" />
<Compile Include="Descriptors\FileDescriptor.cs" />
<Compile Include="Descriptors\MappedType.cs" />
<Compile Include="Descriptors\MessageDescriptor.cs" />
<Compile Include="ExtensionInfo.cs" />
......
......@@ -24,14 +24,14 @@ namespace Google.ProtocolBuffers {
private static readonly UnknownField defaultInstance = CreateBuilder().Build();
private readonly ReadOnlyCollection<ulong> varintList;
private readonly ReadOnlyCollection<int> fixed32List;
private readonly ReadOnlyCollection<long> fixed64List;
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<int> fixed32List,
ReadOnlyCollection<long> fixed64List,
ReadOnlyCollection<uint> fixed32List,
ReadOnlyCollection<ulong> fixed64List,
ReadOnlyCollection<ByteString> lengthDelimitedList,
ReadOnlyCollection<UnknownFieldSet> groupList) {
this.varintList = varintList;
......@@ -55,14 +55,14 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// The list of fixed32 values for this field.
/// </summary>
public IList<int> Fixed32List {
public IList<uint> Fixed32List {
get { return fixed32List; }
}
/// <summary>
/// The list of fixed64 values for this field.
/// </summary>
public IList<long> Fixed64List {
public IList<ulong> 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<ulong> varintList;
private List<int> fixed32List;
private List<long> fixed64List;
private List<uint> fixed32List;
private List<ulong> fixed64List;
private List<ByteString> lengthDelimitedList;
private List<UnknownFieldSet> groupList;
......@@ -246,7 +246,7 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Adds a fixed32 value.
/// </summary>
public Builder AddFixed32(int value) {
public Builder AddFixed32(uint value) {
fixed32List = Add(fixed32List, value);
return this;
}
......@@ -254,7 +254,7 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Adds a fixed64 value.
/// </summary>
public Builder AddFixed64(long value) {
public Builder AddFixed64(ulong value) {
fixed64List = Add(fixed64List, value);
return this;
}
......
......@@ -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 {
/// <summary>
/// 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.
/// <para>
/// This class contains constants and helper functions useful for dealing with
/// the Protocol Buffer wire format.
/// </para>
/// </summary>
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 {
/// <summary>
/// Makes a tag value given a field number and wire type.
/// TODO(jonskeet): Should we just have a Tag structure?
/// </summary>
public static uint MakeTag(int fieldNumber, WireType wireType) {
return (uint) (fieldNumber << TagTypeBits) | (uint) wireType;
}
/// <summary>
/// Immutable mapping from field type to wire type. Built using the attributes on
/// FieldType values.
/// </summary>
public static readonly IDictionary<FieldType, WireType> FieldTypeToWireFormatMap = MapFieldTypes();
private static IDictionary<FieldType, WireType> MapFieldTypes() {
var map = new Dictionary<FieldType, WireType>();
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);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment