diff options
Diffstat (limited to 'src/ProtocolBuffers')
-rw-r--r-- | src/ProtocolBuffers/AbstractMessage.cs | 32 | ||||
-rw-r--r-- | src/ProtocolBuffers/CodedInputStream.cs | 14 | ||||
-rw-r--r-- | src/ProtocolBuffers/CodedOutputStream.cs | 363 | ||||
-rw-r--r-- | src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs | 99 | ||||
-rw-r--r-- | src/ProtocolBuffers/Descriptors/FieldDescriptor.cs | 4 | ||||
-rw-r--r-- | src/ProtocolBuffers/FieldSet.cs | 34 | ||||
-rw-r--r-- | src/ProtocolBuffers/UnknownFieldSet.cs | 93 | ||||
-rw-r--r-- | src/ProtocolBuffers/WireFormat.cs | 25 |
8 files changed, 578 insertions, 86 deletions
diff --git a/src/ProtocolBuffers/AbstractMessage.cs b/src/ProtocolBuffers/AbstractMessage.cs index 9787e159..0a69f294 100644 --- a/src/ProtocolBuffers/AbstractMessage.cs +++ b/src/ProtocolBuffers/AbstractMessage.cs @@ -110,8 +110,21 @@ namespace Google.ProtocolBuffers { if (field.IsRepeated) { // We know it's an IList<T>, but not the exact type - so // IEnumerable is the best we can do. (C# generics aren't covariant yet.) - foreach (object element in (IEnumerable)entry.Value) { - output.WriteField(field.FieldType, field.FieldNumber, element); + IEnumerable valueList = (IEnumerable) entry.Value; + if (field.IsPacked) { + output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited); + int dataSize = 0; + foreach (object element in valueList) { + dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element); + } + output.WriteRawVarint32((uint)dataSize); + foreach (object element in valueList) { + output.WriteFieldNoTag(field.FieldType, element); + } + } else { + foreach (object element in valueList) { + output.WriteField(field.FieldType, field.FieldNumber, element); + } } } else { output.WriteField(field.FieldType, field.FieldNumber, entry.Value); @@ -136,8 +149,19 @@ namespace Google.ProtocolBuffers { foreach (KeyValuePair<FieldDescriptor, object> entry in AllFields) { FieldDescriptor field = entry.Key; if (field.IsRepeated) { - foreach (object element in (IEnumerable) entry.Value) { - size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element); + IEnumerable valueList = (IEnumerable) entry.Value; + if (field.IsPacked) { + int dataSize = 0; + foreach (object element in valueList) { + dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element); + } + size += dataSize; + size += CodedOutputStream.ComputeTagSize(field.FieldNumber); + size += CodedOutputStream.ComputeRawVarint32Size((uint)dataSize); + } else { + foreach (object element in valueList) { + size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element); + } } } else { size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, entry.Value); diff --git a/src/ProtocolBuffers/CodedInputStream.cs b/src/ProtocolBuffers/CodedInputStream.cs index 78d92519..59d51061 100644 --- a/src/ProtocolBuffers/CodedInputStream.cs +++ b/src/ProtocolBuffers/CodedInputStream.cs @@ -609,6 +609,20 @@ namespace Google.ProtocolBuffers { } /// <summary> + /// Returns whether or not all the data before the limit has been read. + /// </summary> + /// <returns></returns> + public bool ReachedLimit { + get { + if (currentLimit == int.MaxValue) { + return false; + } + int currentAbsolutePosition = totalBytesRetired + bufferPos; + return currentAbsolutePosition >= currentLimit; + } + } + + /// <summary> /// Called when buffer is empty to read more bytes from the /// input. If <paramref name="mustSucceed"/> is true, RefillBuffer() gurantees that /// either there will be at least one byte in the buffer when it returns diff --git a/src/ProtocolBuffers/CodedOutputStream.cs b/src/ProtocolBuffers/CodedOutputStream.cs index df80780a..7cc5ff46 100644 --- a/src/ProtocolBuffers/CodedOutputStream.cs +++ b/src/ProtocolBuffers/CodedOutputStream.cs @@ -298,6 +298,156 @@ namespace Google.ProtocolBuffers { } } + public void WriteFieldNoTag(FieldType fieldType, object value) { + switch (fieldType) { + case FieldType.Double: WriteDoubleNoTag((double)value); break; + case FieldType.Float: WriteFloatNoTag((float)value); break; + case FieldType.Int64: WriteInt64NoTag((long)value); break; + case FieldType.UInt64: WriteUInt64NoTag((ulong)value); break; + case FieldType.Int32: WriteInt32NoTag((int)value); break; + case FieldType.Fixed64: WriteFixed64NoTag((ulong)value); break; + case FieldType.Fixed32: WriteFixed32NoTag((uint)value); break; + case FieldType.Bool: WriteBoolNoTag((bool)value); break; + case FieldType.String: WriteStringNoTag((string)value); break; + case FieldType.Group: WriteGroupNoTag((IMessage)value); break; + case FieldType.Message: WriteMessageNoTag((IMessage)value); break; + case FieldType.Bytes: WriteBytesNoTag((ByteString)value); break; + case FieldType.UInt32: WriteUInt32NoTag((uint)value); break; + case FieldType.SFixed32: WriteSFixed32NoTag((int)value); break; + case FieldType.SFixed64: WriteSFixed64NoTag((long)value); break; + case FieldType.SInt32: WriteSInt32NoTag((int)value); break; + case FieldType.SInt64: WriteSInt64NoTag((long)value); break; + case FieldType.Enum: WriteEnumNoTag(((EnumValueDescriptor)value).Number); + break; + } + } + #endregion + + #region Writing of values without tags + /// <summary> + /// Writes a double field value, including tag, to the stream. + /// </summary> + public void WriteDoubleNoTag(double value) { + WriteRawLittleEndian64((ulong)BitConverter.DoubleToInt64Bits(value)); + } + + /// <summary> + /// Writes a float field value, without a tag, to the stream. + /// </summary> + public void WriteFloatNoTag(float value) { + // TODO(jonskeet): Test this on different endiannesses + byte[] rawBytes = BitConverter.GetBytes(value); + uint asInteger = BitConverter.ToUInt32(rawBytes, 0); + WriteRawLittleEndian32(asInteger); + } + + /// <summary> + /// Writes a uint64 field value, without a tag, to the stream. + /// </summary> + public void WriteUInt64NoTag(ulong value) { + WriteRawVarint64(value); + } + + /// <summary> + /// Writes an int64 field value, without a tag, to the stream. + /// </summary> + public void WriteInt64NoTag(long value) { + WriteRawVarint64((ulong)value); + } + + /// <summary> + /// Writes an int32 field value, without a tag, to the stream. + /// </summary> + public void WriteInt32NoTag(int value) { + if (value >= 0) { + WriteRawVarint32((uint)value); + } else { + // Must sign-extend. + WriteRawVarint64((ulong)value); + } + } + + /// <summary> + /// Writes a fixed64 field value, without a tag, to the stream. + /// </summary> + public void WriteFixed64NoTag(ulong value) { + WriteRawLittleEndian64(value); + } + + /// <summary> + /// Writes a fixed32 field value, without a tag, to the stream. + /// </summary> + public void WriteFixed32NoTag(uint value) { + WriteRawLittleEndian32(value); + } + + /// <summary> + /// Writes a bool field value, without a tag, to the stream. + /// </summary> + public void WriteBoolNoTag(bool value) { + WriteRawByte(value ? (byte)1 : (byte)0); + } + + /// <summary> + /// Writes a string field value, without a tag, to the stream. + /// </summary> + public void WriteStringNoTag(string value) { + // Optimise the case where we have enough space to write + // the string directly to the buffer, which should be common. + int length = Encoding.UTF8.GetByteCount(value); + WriteRawVarint32((uint)length); + if (limit - position >= length) { + Encoding.UTF8.GetBytes(value, 0, value.Length, buffer, position); + position += length; + } else { + byte[] bytes = Encoding.UTF8.GetBytes(value); + WriteRawBytes(bytes); + } + } + + /// <summary> + /// Writes a group field value, without a tag, to the stream. + /// </summary> + public void WriteGroupNoTag(IMessage value) { + value.WriteTo(this); + } + + public void WriteMessageNoTag(IMessage value) { + WriteRawVarint32((uint)value.SerializedSize); + value.WriteTo(this); + } + + public void WriteBytesNoTag(ByteString value) { + // TODO(jonskeet): Optimise this! (No need to copy the bytes twice.) + byte[] bytes = value.ToByteArray(); + WriteRawVarint32((uint)bytes.Length); + WriteRawBytes(bytes); + } + + public void WriteUInt32NoTag(uint value) { + WriteRawVarint32(value); + } + + public void WriteEnumNoTag(int value) { + WriteRawVarint32((uint)value); + } + + public void WriteSFixed32NoTag(int value) { + WriteRawLittleEndian32((uint)value); + } + + public void WriteSFixed64NoTag(long value) { + WriteRawLittleEndian64((ulong)value); + } + + public void WriteSInt32NoTag(int value) { + WriteRawVarint32(EncodeZigZag32(value)); + } + + public void WriteSInt64NoTag(long value) { + WriteRawVarint64(EncodeZigZag64(value)); + } + #endregion #region Underlying writing primitives @@ -583,8 +733,7 @@ namespace Google.ProtocolBuffers { /// sint32 field, including the tag. /// </summary> public static int ComputeSInt32Size(int fieldNumber, int value) { - return ComputeTagSize(fieldNumber) + - ComputeRawVarint32Size(EncodeZigZag32(value)); + return ComputeTagSize(fieldNumber) + ComputeRawVarint32Size(EncodeZigZag32(value)); } /// <summary> @@ -592,8 +741,169 @@ namespace Google.ProtocolBuffers { /// sint64 field, including the tag. /// </summary> public static int ComputeSInt64Size(int fieldNumber, long value) { - return ComputeTagSize(fieldNumber) + - ComputeRawVarint64Size(EncodeZigZag64(value)); + return ComputeTagSize(fieldNumber) + ComputeRawVarint64Size(EncodeZigZag64(value)); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// double field, including the tag. + /// </summary> + public static int ComputeDoubleSizeNoTag(double value) { + return LittleEndian64Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// float field, including the tag. + /// </summary> + public static int ComputeFloatSizeNoTag(float value) { + return LittleEndian32Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// uint64 field, including the tag. + /// </summary> + public static int ComputeUInt64SizeNoTag(ulong value) { + return ComputeRawVarint64Size(value); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// int64 field, including the tag. + /// </summary> + public static int ComputeInt64SizeNoTag(long value) { + return ComputeRawVarint64Size((ulong)value); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// int32 field, including the tag. + /// </summary> + public static int ComputeInt32SizeNoTag(int value) { + if (value >= 0) { + return ComputeRawVarint32Size((uint)value); + } else { + // Must sign-extend. + return 10; + } + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// fixed64 field, including the tag. + /// </summary> + public static int ComputeFixed64SizeNoTag(ulong value) { + return LittleEndian64Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// fixed32 field, including the tag. + /// </summary> + public static int ComputeFixed32SizeNoTag(uint value) { + return LittleEndian32Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// bool field, including the tag. + /// </summary> + public static int ComputeBoolSizeNoTag(bool value) { + return 1; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// string field, including the tag. + /// </summary> + public static int ComputeStringSizeNoTag(String value) { + int byteArraySize = Encoding.UTF8.GetByteCount(value); + return ComputeRawVarint32Size((uint)byteArraySize) + + byteArraySize; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// group field, including the tag. + /// </summary> + public static int ComputeGroupSizeNoTag(IMessage value) { + return value.SerializedSize; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// group field represented by an UnknownFieldSet, including the tag. + /// </summary> + public static int ComputeUnknownGroupSizeNoTag(UnknownFieldSet value) { + return value.SerializedSize; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// embedded message field, including the tag. + /// </summary> + public static int ComputeMessageSizeNoTag(IMessage value) { + int size = value.SerializedSize; + return ComputeRawVarint32Size((uint)size) + size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// bytes field, including the tag. + /// </summary> + public static int ComputeBytesSizeNoTag(ByteString value) { + return ComputeRawVarint32Size((uint)value.Length) + + value.Length; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// uint32 field, including the tag. + /// </summary> + public static int ComputeUInt32SizeNoTag(uint value) { + return ComputeRawVarint32Size(value); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// enum field, including the tag. The caller is responsible for + /// converting the enum value to its numeric value. + /// </summary> + public static int ComputeEnumSizeNoTag(int value) { + return ComputeRawVarint32Size((uint)value); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// sfixed32 field, including the tag. + /// </summary> + public static int ComputeSFixed32SizeNoTag(int value) { + return LittleEndian32Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// sfixed64 field, including the tag. + /// </summary> + public static int ComputeSFixed64SizeNoTag(long value) { + return LittleEndian64Size; + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// sint32 field, including the tag. + /// </summary> + public static int ComputeSInt32SizeNoTag(int value) { + return ComputeRawVarint32Size(EncodeZigZag32(value)); + } + + /// <summary> + /// Compute the number of bytes that would be needed to encode an + /// sint64 field, including the tag. + /// </summary> + public static int ComputeSInt64SizeNoTag(long value) { + return ComputeRawVarint64Size(EncodeZigZag64(value)); } /* @@ -650,18 +960,10 @@ namespace Google.ProtocolBuffers { return 10; } - - /* - * Compute the number of bytes that would be needed to encode a - * field of arbitrary type, including tag, to the stream. - * - * @param type The field's type. - * @param number The field's number. - * @param value Object representing the field's value. Must be of the exact - * type which would be returned by - * {@link Message#getField(FieldDescriptor)} for - * this field. - */ + /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// field of arbitrary type, including the tag, to the stream. + /// </summary> public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) { switch (fieldType) { case FieldType.Double: return ComputeDoubleSize(fieldNumber, (double)value); @@ -688,6 +990,35 @@ namespace Google.ProtocolBuffers { } /// <summary> + /// Compute the number of bytes that would be needed to encode a + /// field of arbitrary type, excluding the tag, to the stream. + /// </summary> + public static int ComputeFieldSizeNoTag(FieldType fieldType, Object value) { + switch (fieldType) { + case FieldType.Double: return ComputeDoubleSizeNoTag((double)value); + case FieldType.Float: return ComputeFloatSizeNoTag((float)value); + case FieldType.Int64: return ComputeInt64SizeNoTag((long)value); + case FieldType.UInt64: return ComputeUInt64SizeNoTag((ulong)value); + case FieldType.Int32: return ComputeInt32SizeNoTag((int)value); + case FieldType.Fixed64: return ComputeFixed64SizeNoTag((ulong)value); + case FieldType.Fixed32: return ComputeFixed32SizeNoTag((uint)value); + case FieldType.Bool: return ComputeBoolSizeNoTag((bool)value); + case FieldType.String: return ComputeStringSizeNoTag((string)value); + case FieldType.Group: return ComputeGroupSizeNoTag((IMessage)value); + case FieldType.Message: return ComputeMessageSizeNoTag((IMessage)value); + case FieldType.Bytes: return ComputeBytesSizeNoTag((ByteString)value); + case FieldType.UInt32: return ComputeUInt32SizeNoTag((uint)value); + case FieldType.SFixed32: return ComputeSFixed32SizeNoTag((int)value); + case FieldType.SFixed64: return ComputeSFixed64SizeNoTag((long)value); + case FieldType.SInt32: return ComputeSInt32SizeNoTag((int)value); + case FieldType.SInt64: return ComputeSInt64SizeNoTag((long)value); + case FieldType.Enum: return ComputeEnumSizeNoTag(((EnumValueDescriptor)value).Number); + default: + throw new ArgumentOutOfRangeException("Invalid field type " + fieldType); + } + } + + /// <summary> /// Compute the number of bytes that would be needed to encode a tag. /// </summary> public static int ComputeTagSize(int fieldNumber) { diff --git a/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs b/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs index 4206f261..1fe7928c 100644 --- a/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs +++ b/src/ProtocolBuffers/DescriptorProtos/DescriptorProtoFile.cs @@ -70,27 +70,27 @@ namespace Google.ProtocolBuffers.DescriptorProtos { "AioJCOgHEICAgIACIogBCg5NZXNzYWdlT3B0aW9ucxImChdtZXNzYWdlX3Nl" + "dF93aXJlX2Zvcm1hdBgBIAEoCDoFZmFsc2USQwoUdW5pbnRlcnByZXRlZF9v" + "cHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRP" + - "cHRpb24qCQjoBxCAgICAAiLVAQoMRmllbGRPcHRpb25zEjIKBWN0eXBlGAEg" + - "ASgOMiMuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5DVHlwZRIcChRl" + - "eHBlcmltZW50YWxfbWFwX2tleRgJIAEoCRJDChR1bmludGVycHJldGVkX29w" + - "dGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9w" + - "dGlvbiIjCgVDVHlwZRIICgRDT1JEEAESEAoMU1RSSU5HX1BJRUNFEAIqCQjo" + - "BxCAgICAAiJdCgtFbnVtT3B0aW9ucxJDChR1bmludGVycHJldGVkX29wdGlv" + - "bhjnByADKAsyJC5nb29nbGUucHJvdG9idWYuVW5pbnRlcnByZXRlZE9wdGlv" + - "bioJCOgHEICAgIACImIKEEVudW1WYWx1ZU9wdGlvbnMSQwoUdW5pbnRlcnBy" + - "ZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVuaW50ZXJw" + - "cmV0ZWRPcHRpb24qCQjoBxCAgICAAiJgCg5TZXJ2aWNlT3B0aW9ucxJDChR1" + - "bmludGVycHJldGVkX29wdGlvbhjnByADKAsyJC5nb29nbGUucHJvdG9idWYu" + - "VW5pbnRlcnByZXRlZE9wdGlvbioJCOgHEICAgIACIl8KDU1ldGhvZE9wdGlv" + - "bnMSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnBy" + - "b3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiKFAgoTVW5p" + - "bnRlcnByZXRlZE9wdGlvbhI7CgRuYW1lGAIgAygLMi0uZ29vZ2xlLnByb3Rv" + - "YnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24uTmFtZVBhcnQSGAoQaWRlbnRpZmll" + - "cl92YWx1ZRgDIAEoCRIaChJwb3NpdGl2ZV9pbnRfdmFsdWUYBCABKAQSGgoS" + - "bmVnYXRpdmVfaW50X3ZhbHVlGAUgASgDEhQKDGRvdWJsZV92YWx1ZRgGIAEo" + - "ARIUCgxzdHJpbmdfdmFsdWUYByABKAwaMwoITmFtZVBhcnQSEQoJbmFtZV9w" + - "YXJ0GAEgAigJEhQKDGlzX2V4dGVuc2lvbhgCIAIoCEIpChNjb20uZ29vZ2xl" + - "LnByb3RvYnVmQhBEZXNjcmlwdG9yUHJvdG9zSAE="), + "cHRpb24qCQjoBxCAgICAAiLlAQoMRmllbGRPcHRpb25zEjIKBWN0eXBlGAEg" + + "ASgOMiMuZ29vZ2xlLnByb3RvYnVmLkZpZWxkT3B0aW9ucy5DVHlwZRIOCgZw" + + "YWNrZWQYAiABKAgSHAoUZXhwZXJpbWVudGFsX21hcF9rZXkYCSABKAkSQwoU" + + "dW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVm" + + "LlVuaW50ZXJwcmV0ZWRPcHRpb24iIwoFQ1R5cGUSCAoEQ09SRBABEhAKDFNU" + + "UklOR19QSUVDRRACKgkI6AcQgICAgAIiXQoLRW51bU9wdGlvbnMSQwoUdW5p" + + "bnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQuZ29vZ2xlLnByb3RvYnVmLlVu" + + "aW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICAAiJiChBFbnVtVmFsdWVPcHRp" + + "b25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcHIAMoCzIkLmdvb2dsZS5w" + + "cm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI6AcQgICAgAIiYAoOU2Vy" + + "dmljZU9wdGlvbnMSQwoUdW5pbnRlcnByZXRlZF9vcHRpb24Y5wcgAygLMiQu" + + "Z29vZ2xlLnByb3RvYnVmLlVuaW50ZXJwcmV0ZWRPcHRpb24qCQjoBxCAgICA" + + "AiJfCg1NZXRob2RPcHRpb25zEkMKFHVuaW50ZXJwcmV0ZWRfb3B0aW9uGOcH" + + "IAMoCzIkLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uKgkI" + + "6AcQgICAgAIihQIKE1VuaW50ZXJwcmV0ZWRPcHRpb24SOwoEbmFtZRgCIAMo" + + "CzItLmdvb2dsZS5wcm90b2J1Zi5VbmludGVycHJldGVkT3B0aW9uLk5hbWVQ" + + "YXJ0EhgKEGlkZW50aWZpZXJfdmFsdWUYAyABKAkSGgoScG9zaXRpdmVfaW50" + + "X3ZhbHVlGAQgASgEEhoKEm5lZ2F0aXZlX2ludF92YWx1ZRgFIAEoAxIUCgxk" + + "b3VibGVfdmFsdWUYBiABKAESFAoMc3RyaW5nX3ZhbHVlGAcgASgMGjMKCE5h" + + "bWVQYXJ0EhEKCW5hbWVfcGFydBgBIAIoCRIUCgxpc19leHRlbnNpb24YAiAC" + + "KAhCKQoTY29tLmdvb2dsZS5wcm90b2J1ZkIQRGVzY3JpcHRvclByb3Rvc0gB"), new pbd::FileDescriptor[] { }); #endregion @@ -155,7 +155,7 @@ namespace Google.ProtocolBuffers.DescriptorProtos { = Descriptor.MessageTypes[10]; internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions, global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Builder> internal__static_google_protobuf_FieldOptions__FieldAccessorTable = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions, global::Google.ProtocolBuffers.DescriptorProtos.FieldOptions.Builder>(internal__static_google_protobuf_FieldOptions__Descriptor, - new string[] { "Ctype", "ExperimentalMapKey", "UninterpretedOption", }); + new string[] { "Ctype", "Packed", "ExperimentalMapKey", "UninterpretedOption", }); internal static readonly pbd::MessageDescriptor internal__static_google_protobuf_EnumOptions__Descriptor = Descriptor.MessageTypes[11]; internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.DescriptorProtos.EnumOptions, global::Google.ProtocolBuffers.DescriptorProtos.EnumOptions.Builder> internal__static_google_protobuf_EnumOptions__FieldAccessorTable @@ -541,8 +541,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasPackage) { output.WriteString(2, Package); } - foreach (string element in DependencyList) { - output.WriteString(3, element); + if (dependency_.Count > 0) { + foreach (string element in dependency_) { + output.WriteString(3, element); + } } foreach (global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProto element in MessageTypeList) { output.WriteMessage(4, element); @@ -575,8 +577,13 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasPackage) { size += pb::CodedOutputStream.ComputeStringSize(2, Package); } - foreach (string element in DependencyList) { - size += pb::CodedOutputStream.ComputeStringSize(3, element); + { + int dataSize = 0; + foreach (string element in DependencyList) { + dataSize += pb::CodedOutputStream.ComputeStringSizeNoTag(element); + } + size += dataSize; + size += 1 * dependency_.Count; } foreach (global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProto element in MessageTypeList) { size += pb::CodedOutputStream.ComputeMessageSize(4, element); @@ -4413,6 +4420,15 @@ namespace Google.ProtocolBuffers.DescriptorProtos { get { return ctype_; } } + private bool hasPacked; + private bool packed_ = false; + public bool HasPacked { + get { return hasPacked; } + } + public bool Packed { + get { return packed_; } + } + private bool hasExperimentalMapKey; private string experimentalMapKey_ = ""; public bool HasExperimentalMapKey { @@ -4448,6 +4464,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasCtype) { output.WriteEnum(1, (int) Ctype); } + if (HasPacked) { + output.WriteBool(2, Packed); + } if (HasExperimentalMapKey) { output.WriteString(9, ExperimentalMapKey); } @@ -4468,6 +4487,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (HasCtype) { size += pb::CodedOutputStream.ComputeEnumSize(1, (int) Ctype); } + if (HasPacked) { + size += pb::CodedOutputStream.ComputeBoolSize(2, Packed); + } if (HasExperimentalMapKey) { size += pb::CodedOutputStream.ComputeStringSize(9, ExperimentalMapKey); } @@ -4561,6 +4583,9 @@ namespace Google.ProtocolBuffers.DescriptorProtos { if (other.HasCtype) { Ctype = other.Ctype; } + if (other.HasPacked) { + Packed = other.Packed; + } if (other.HasExperimentalMapKey) { ExperimentalMapKey = other.ExperimentalMapKey; } @@ -4601,6 +4626,10 @@ namespace Google.ProtocolBuffers.DescriptorProtos { } break; } + case 16: { + Packed = input.ReadBool(); + break; + } case 74: { ExperimentalMapKey = input.ReadString(); break; @@ -4634,6 +4663,24 @@ namespace Google.ProtocolBuffers.DescriptorProtos { return this; } + public bool HasPacked { + get { return result.HasPacked; } + } + public bool Packed { + get { return result.Packed; } + set { SetPacked(value); } + } + public Builder SetPacked(bool value) { + result.hasPacked = true; + result.packed_ = value; + return this; + } + public Builder ClearPacked() { + result.hasPacked = false; + result.packed_ = false; + return this; + } + public bool HasExperimentalMapKey { get { return result.HasExperimentalMapKey; } } diff --git a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs index 0d0fad76..eca3d6d1 100644 --- a/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs +++ b/src/ProtocolBuffers/Descriptors/FieldDescriptor.cs @@ -176,6 +176,10 @@ namespace Google.ProtocolBuffers.Descriptors { get { return Proto.Label == FieldDescriptorProto.Types.Label.LABEL_REPEATED; } } + public bool IsPacked { + get { return Proto.Options.Packed; } + } + /// <valule> /// Indicates whether or not the field had an explicitly-defined default value. /// </value> diff --git a/src/ProtocolBuffers/FieldSet.cs b/src/ProtocolBuffers/FieldSet.cs index bc2e2e70..913dede2 100644 --- a/src/ProtocolBuffers/FieldSet.cs +++ b/src/ProtocolBuffers/FieldSet.cs @@ -368,8 +368,23 @@ namespace Google.ProtocolBuffers { output.WriteMessageSetExtension(field.FieldNumber, (IMessage) value); } else { if (field.IsRepeated) { - foreach (object element in (IEnumerable) value) { - output.WriteField(field.FieldType, field.FieldNumber, element); + IEnumerable valueList = (IEnumerable) value; + if (field.IsPacked) { + output.WriteTag(field.FieldNumber, WireFormat.WireType.LengthDelimited); + // Compute the total data size so the length can be written. + int dataSize = 0; + foreach (object element in valueList) { + dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element); + } + output.WriteRawVarint32((uint)dataSize); + // Write the data itself, without any tags. + foreach (object element in valueList) { + output.WriteFieldNoTag(field.FieldType, element); + } + } else { + foreach (object element in valueList) { + output.WriteField(field.FieldType, field.FieldNumber, element); + } } } else { output.WriteField(field.FieldType, field.FieldNumber, value); @@ -389,11 +404,20 @@ namespace Google.ProtocolBuffers { object value = entry.Value; if (field.IsExtension && field.ContainingType.Options.MessageSetWireFormat) { - size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage) value); + size += CodedOutputStream.ComputeMessageSetExtensionSize(field.FieldNumber, (IMessage)value); } else { if (field.IsRepeated) { - foreach (object element in (IEnumerable) value) { - size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element); + IEnumerable valueList = (IEnumerable)value; + if (field.IsPacked) { + int dataSize = 0; + foreach (object element in valueList) { + dataSize += CodedOutputStream.ComputeFieldSizeNoTag(field.FieldType, element); + } + size += dataSize + CodedOutputStream.ComputeTagSize(field.FieldNumber) + CodedOutputStream.ComputeRawVarint32Size((uint)dataSize); + } else { + foreach (object element in valueList) { + size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, element); + } } } else { size += CodedOutputStream.ComputeFieldSize(field.FieldType, field.FieldNumber, value); diff --git a/src/ProtocolBuffers/UnknownFieldSet.cs b/src/ProtocolBuffers/UnknownFieldSet.cs index ecae53ce..aa371fad 100644 --- a/src/ProtocolBuffers/UnknownFieldSet.cs +++ b/src/ProtocolBuffers/UnknownFieldSet.cs @@ -497,50 +497,73 @@ namespace Google.ProtocolBuffers { } // Unknown field or wrong wire type. Skip. - if (field == null || wireType != WireFormat.GetWireType(field.FieldType)) { + if (field == null || wireType != WireFormat.GetWireType(field)) { 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: { + if (field.IsPacked) { + int length = (int)input.ReadRawVarint32(); + int limit = input.PushLimit(length); + if (field.FieldType == FieldType.Enum) { + while (!input.ReachedLimit) { int rawValue = input.ReadEnum(); - value = field.EnumType.FindValueByNumber(rawValue); - // If the number isn't recognized as a valid value for this enum, - // drop it. + object value = field.EnumType.FindValueByNumber(rawValue); if (value == null) { - MergeVarintField(fieldNumber, (ulong)rawValue); + // If the number isn't recognized as a valid value for this + // enum, drop it (don't even add it to unknownFields). return true; } - break; + builder.WeakAddRepeatedField(field, value); } - default: - value = input.ReadPrimitiveField(field.FieldType); - break; - } - if (field.IsRepeated) { - builder.WeakAddRepeatedField(field, value); + } else { + while (!input.ReachedLimit) { + Object value = input.ReadPrimitiveField(field.FieldType); + builder.WeakAddRepeatedField(field, value); + } + } + input.PopLimit(limit); } else { - builder[field] = value; + 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; } diff --git a/src/ProtocolBuffers/WireFormat.cs b/src/ProtocolBuffers/WireFormat.cs index abf47169..2bd0a5aa 100644 --- a/src/ProtocolBuffers/WireFormat.cs +++ b/src/ProtocolBuffers/WireFormat.cs @@ -46,6 +46,19 @@ namespace Google.ProtocolBuffers { /// </para> /// </summary> public static class WireFormat { + +#region Fixed sizes. + // TODO(jonskeet): Move these somewhere else. They're messy. Consider making FieldType a smarter kind of enum + internal const int Fixed32Size = 4; + internal const int Fixed64Size = 8; + internal const int SFixed32Size = 4; + internal const int SFixed64Size = 8; + internal const int FloatSize = 4; + internal const int DoubleSize = 8; + internal const int BoolSize = 1; +#endregion + + public enum WireType : uint { Varint = 0, Fixed64 = 1, @@ -93,6 +106,18 @@ namespace Google.ProtocolBuffers { return (uint) (fieldNumber << TagTypeBits) | (uint) wireType; } + public static uint MakeTag(FieldDescriptor field) { + return MakeTag(field.FieldNumber, GetWireType(field)); + } + + /// <summary> + /// Returns the wire type for the given field descriptor. This differs + /// from GetWireType(FieldType) for packed repeated fields. + /// </summary> + internal static WireType GetWireType(FieldDescriptor descriptor) { + return descriptor.IsPacked ? WireType.LengthDelimited : GetWireType(descriptor.FieldType); + } + /// <summary> /// Converts a field type to its wire type. Done with a switch for the sake /// of speed - this is significantly faster than a dictionary lookup. |