Commit f2a27cc2 authored by Jon Skeet's avatar Jon Skeet

First pass (not yet compiling) at removing all the array handling code from Coded*Stream.

Prod code works, but some tests are broken. Obviously those need fixing, then more tests,
and review benchmarks.
parent 241e17ba
......@@ -131,6 +131,8 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public const int PhoneFieldNumber = 4;
private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> _repeated_phone_codec
= pb::FieldCodec.ForMessage(34, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber>();
public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber> Phone {
get { return phone_; }
......@@ -176,9 +178,7 @@ namespace Google.Protobuf.Examples.AddressBook {
output.WriteRawTag(26);
output.WriteString(Email);
}
if (phone_.Count > 0) {
output.WriteMessageArray(4, phone_);
}
phone_.WriteTo(output, _repeated_phone_codec);
}
public int CalculateSize() {
......@@ -192,12 +192,7 @@ namespace Google.Protobuf.Examples.AddressBook {
if (Email.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(Email);
}
if (phone_.Count > 0) {
foreach (global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber element in phone_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * phone_.Count;
}
size += phone_.CalculateSize(_repeated_phone_codec);
return size;
}
......@@ -241,7 +236,7 @@ namespace Google.Protobuf.Examples.AddressBook {
break;
}
case 34: {
input.ReadMessageArray(phone_, global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber.Parser);
phone_.AddEntriesFrom(input, _repeated_phone_codec);
break;
}
}
......@@ -437,6 +432,8 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public const int PersonFieldNumber = 1;
private static readonly pb::FieldCodec<global::Google.Protobuf.Examples.AddressBook.Person> _repeated_person_codec
= pb::FieldCodec.ForMessage(10, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> person_ = new pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person>();
public pbc::RepeatedField<global::Google.Protobuf.Examples.AddressBook.Person> Person {
get { return person_; }
......@@ -464,19 +461,12 @@ namespace Google.Protobuf.Examples.AddressBook {
}
public void WriteTo(pb::CodedOutputStream output) {
if (person_.Count > 0) {
output.WriteMessageArray(1, person_);
}
person_.WriteTo(output, _repeated_person_codec);
}
public int CalculateSize() {
int size = 0;
if (person_.Count > 0) {
foreach (global::Google.Protobuf.Examples.AddressBook.Person element in person_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * person_.Count;
}
size += person_.CalculateSize(_repeated_person_codec);
return size;
}
......@@ -499,7 +489,7 @@ namespace Google.Protobuf.Examples.AddressBook {
}
break;
case 10: {
input.ReadMessageArray(person_, global::Google.Protobuf.Examples.AddressBook.Person.Parser);
person_.AddEntriesFrom(input, _repeated_person_codec);
break;
}
}
......
......@@ -422,13 +422,15 @@ namespace UnitTest.Issues.TestProtos {
}
public const int ValuesFieldNumber = 2;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_values_codec
= pb::FieldCodec.ForEnum(16, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> values_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> Values {
get { return values_; }
}
public const int PackedValuesFieldNumber = 3;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.NegativeEnum> _repeated_packedValues_codec
= pb::FieldCodec.ForEnum(26, x => (int) x, x => (global::UnitTest.Issues.TestProtos.NegativeEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> packedValues_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum>();
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.NegativeEnum> PackedValues {
get { return packedValues_; }
}
......@@ -463,13 +465,8 @@ namespace UnitTest.Issues.TestProtos {
output.WriteRawTag(8);
output.WriteEnum((int) Value);
}
if (values_.Count > 0) {
output.WriteEnumArray(2, values_);
}
if (packedValues_.Count > 0) {
output.WriteRawTag(26);
output.WritePackedEnumArray(packedValues_);
}
values_.WriteTo(output, _repeated_values_codec);
packedValues_.WriteTo(output, _repeated_packedValues_codec);
}
public int CalculateSize() {
......@@ -477,22 +474,8 @@ namespace UnitTest.Issues.TestProtos {
if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Value);
}
if (values_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in values_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 * values_.Count;
}
if (packedValues_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.NegativeEnum element in packedValues_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
}
size += values_.CalculateSize(_repeated_values_codec);
size += packedValues_.CalculateSize(_repeated_packedValues_codec);
return size;
}
......@@ -524,12 +507,12 @@ namespace UnitTest.Issues.TestProtos {
}
case 18:
case 16: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(values_);
values_.AddEntriesFrom(input, _repeated_values_codec);
break;
}
case 26:
case 24: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.NegativeEnum>(packedValues_);
packedValues_.AddEntriesFrom(input, _repeated_packedValues_codec);
break;
}
}
......@@ -678,6 +661,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int PrimitiveArrayFieldNumber = 2;
private static readonly pb::FieldCodec<int> _repeated_primitiveArray_codec
= pb::FieldCodec.ForInt32(18);
private readonly pbc::RepeatedField<int> primitiveArray_ = new pbc::RepeatedField<int>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<int> PrimitiveArray {
......@@ -696,6 +681,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int MessageArrayFieldNumber = 4;
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedChild> _repeated_messageArray_codec
= pb::FieldCodec.ForMessage(34, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> messageArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedChild> MessageArray {
......@@ -714,7 +701,8 @@ namespace UnitTest.Issues.TestProtos {
}
public const int EnumArrayFieldNumber = 6;
private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
private static readonly pb::FieldCodec<global::UnitTest.Issues.TestProtos.DeprecatedEnum> _repeated_enumArray_codec
= pb::FieldCodec.ForEnum(50, x => (int) x, x => (global::UnitTest.Issues.TestProtos.DeprecatedEnum) x);private readonly pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> enumArray_ = new pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum>();
[global::System.ObsoleteAttribute()]
public pbc::RepeatedField<global::UnitTest.Issues.TestProtos.DeprecatedEnum> EnumArray {
get { return enumArray_; }
......@@ -756,25 +744,17 @@ namespace UnitTest.Issues.TestProtos {
output.WriteRawTag(8);
output.WriteInt32(PrimitiveValue);
}
if (primitiveArray_.Count > 0) {
output.WriteRawTag(18);
output.WritePackedInt32Array(primitiveArray_);
}
primitiveArray_.WriteTo(output, _repeated_primitiveArray_codec);
if (messageValue_ != null) {
output.WriteRawTag(26);
output.WriteMessage(MessageValue);
}
if (messageArray_.Count > 0) {
output.WriteMessageArray(4, messageArray_);
}
messageArray_.WriteTo(output, _repeated_messageArray_codec);
if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
output.WriteRawTag(40);
output.WriteEnum((int) EnumValue);
}
if (enumArray_.Count > 0) {
output.WriteRawTag(50);
output.WritePackedEnumArray(enumArray_);
}
enumArray_.WriteTo(output, _repeated_enumArray_codec);
}
public int CalculateSize() {
......@@ -782,34 +762,15 @@ namespace UnitTest.Issues.TestProtos {
if (PrimitiveValue != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(PrimitiveValue);
}
if (primitiveArray_.Count > 0) {
int dataSize = 0;
foreach (int element in primitiveArray_) {
dataSize += pb::CodedOutputStream.ComputeInt32Size(element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeInt32Size(dataSize);
}
size += primitiveArray_.CalculateSize(_repeated_primitiveArray_codec);
if (messageValue_ != null) {
size += 1 + pb::CodedOutputStream.ComputeMessageSize(MessageValue);
}
if (messageArray_.Count > 0) {
foreach (global::UnitTest.Issues.TestProtos.DeprecatedChild element in messageArray_) {
size += pb::CodedOutputStream.ComputeMessageSize(element);
}
size += 1 * messageArray_.Count;
}
size += messageArray_.CalculateSize(_repeated_messageArray_codec);
if (EnumValue != global::UnitTest.Issues.TestProtos.DeprecatedEnum.DEPRECATED_ZERO) {
size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) EnumValue);
}
if (enumArray_.Count > 0) {
int dataSize = 0;
foreach (global::UnitTest.Issues.TestProtos.DeprecatedEnum element in enumArray_) {
dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);
}
size += dataSize;
size += 1 + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);
}
size += enumArray_.CalculateSize(_repeated_enumArray_codec);
return size;
}
......@@ -851,7 +812,7 @@ namespace UnitTest.Issues.TestProtos {
}
case 18:
case 16: {
input.ReadInt32Array(primitiveArray_);
primitiveArray_.AddEntriesFrom(input, _repeated_primitiveArray_codec);
break;
}
case 26: {
......@@ -862,7 +823,7 @@ namespace UnitTest.Issues.TestProtos {
break;
}
case 34: {
input.ReadMessageArray(messageArray_, global::UnitTest.Issues.TestProtos.DeprecatedChild.Parser);
messageArray_.AddEntriesFrom(input, _repeated_messageArray_codec);
break;
}
case 40: {
......@@ -871,7 +832,7 @@ namespace UnitTest.Issues.TestProtos {
}
case 50:
case 48: {
input.ReadEnumArray<global::UnitTest.Issues.TestProtos.DeprecatedEnum>(enumArray_);
enumArray_.AddEntriesFrom(input, _repeated_enumArray_codec);
break;
}
}
......
......@@ -172,6 +172,12 @@ namespace Google.Protobuf
}
}
/// <summary>
/// Returns the last tag read, or 0 if no tags have been read or we've read beyond
/// the end of the stream.
/// </summary>
internal uint LastTag { get { return lastTag; } }
#region Validation
/// <summary>
......@@ -435,26 +441,6 @@ namespace Google.Protobuf
return DecodeZigZag64(ReadRawVarint64());
}
private bool BeginArray(uint fieldTag, out bool isPacked, out int oldLimit)
{
isPacked = WireFormat.GetTagWireType(fieldTag) == WireFormat.WireType.LengthDelimited;
if (isPacked)
{
int length = (int) (ReadRawVarint32() & int.MaxValue);
if (length > 0)
{
oldLimit = PushLimit(length);
return true;
}
oldLimit = -1;
return false; //packed but empty
}
oldLimit = -1;
return true;
}
/// <summary>
/// Peeks at the next tag in the stream. If it matches <paramref name="tag"/>,
/// the tag is consumed and the method returns <c>true</c>; otherwise, the
......@@ -474,268 +460,6 @@ namespace Google.Protobuf
return false;
}
/// <summary>
/// Returns true if the next tag is also part of the same array, which may or may not be packed.
/// </summary>
private bool ContinueArray(uint currentTag, bool packed, int oldLimit)
{
if (packed)
{
if (ReachedLimit)
{
PopLimit(oldLimit);
return false;
}
return true;
}
return MaybeConsumeTag(currentTag);
}
/// <summary>
/// Reads a string array.
/// </summary>
/// <remarks>The stream is assumed to be positioned after a tag indicating the field
/// repeated string value. A string is read, and then if the next tag is the same,
/// the process is repeated, until the next tag is a different one.</remarks>
/// <param name="list"></param>
public void ReadStringArray(ICollection<string> list)
{
uint fieldTag = lastTag;
do
{
list.Add(ReadString());
} while (MaybeConsumeTag(fieldTag));
}
public void ReadBytesArray(ICollection<ByteString> list)
{
uint fieldTag = lastTag;
do
{
list.Add(ReadBytes());
} while (MaybeConsumeTag(fieldTag));
}
public void ReadBoolArray(ICollection<bool> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadBool());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadInt32Array(ICollection<int> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadInt32());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadSInt32Array(ICollection<int> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadSInt32());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadUInt32Array(ICollection<uint> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadUInt32());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadFixed32Array(ICollection<uint> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadFixed32());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadSFixed32Array(ICollection<int> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadSFixed32());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadInt64Array(ICollection<long> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadInt64());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadSInt64Array(ICollection<long> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadSInt64());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadUInt64Array(ICollection<ulong> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadUInt64());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadFixed64Array(ICollection<ulong> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadFixed64());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadSFixed64Array(ICollection<long> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadSFixed64());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadDoubleArray(ICollection<double> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadDouble());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadFloatArray(ICollection<float> list)
{
uint fieldTag = lastTag;
bool isPacked;
int holdLimit;
if (BeginArray(fieldTag, out isPacked, out holdLimit))
{
do
{
list.Add(ReadFloat());
} while (ContinueArray(fieldTag, isPacked, holdLimit));
}
}
public void ReadEnumArray<T>(RepeatedField<T> list)
where T : struct, IComparable, IFormattable
{
uint fieldTag = lastTag;
WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
// 2.3 allows packed form even if the field is not declared packed.
if (wformat == WireFormat.WireType.LengthDelimited)
{
int length = (int) (ReadRawVarint32() & int.MaxValue);
int limit = PushLimit(length);
while (!ReachedLimit)
{
// Ghastly hack, but it works...
list.AddInt32(ReadEnum());
}
PopLimit(limit);
}
else
{
do
{
list.Add((T)(object) ReadEnum());
} while (MaybeConsumeTag(fieldTag));
}
}
public void ReadMessageArray<T>(ICollection<T> list, MessageParser<T> messageParser)
where T : IMessage<T>
{
uint fieldTag = lastTag;
do
{
T message = messageParser.CreateTemplate();
ReadMessage(message);
list.Add(message);
} while (MaybeConsumeTag(fieldTag));
}
#endregion
#region Underlying reading primitives
......
......@@ -334,6 +334,10 @@ namespace Google.Protobuf.Collections
public int CalculateSize(Codec codec)
{
if (Count == 0)
{
return 0;
}
var message = new Codec.MessageAdapter(codec);
int size = 0;
foreach (var entry in list)
......@@ -419,13 +423,13 @@ namespace Google.Protobuf.Collections
public void WriteTo(CodedOutputStream output)
{
codec.keyCodec.Write(output, Key);
codec.valueCodec.Write(output, Value);
codec.keyCodec.WriteTagAndValue(output, Key);
codec.valueCodec.WriteTagAndValue(output, Value);
}
public int CalculateSize()
{
return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
return codec.keyCodec.CalculateSizeWithTag(Key) + codec.valueCodec.CalculateSizeWithTag(Value);
}
}
}
......
......@@ -49,6 +49,112 @@ namespace Google.Protobuf.Collections
return clone;
}
public void AddEntriesFrom(CodedInputStream input, FieldCodec<T> codec)
{
uint tag = input.LastTag;
var reader = codec.ValueReader;
// Value types can be packed or not.
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
int length = (int)(input.ReadRawVarint32() & int.MaxValue);
if (length > 0)
{
int oldLimit = input.PushLimit(length);
while (!input.ReachedLimit)
{
Add(reader(input));
}
input.PopLimit(oldLimit);
}
// Empty packed field. Odd, but valid - just ignore.
}
else
{
// Not packed... (possibly not packable)
do
{
Add(reader(input));
} while (input.MaybeConsumeTag(tag));
}
}
public int CalculateSize(FieldCodec<T> codec)
{
if (count == 0)
{
return 0;
}
uint tag = codec.Tag;
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
int dataSize = CalculatePackedDataSize(codec);
return CodedOutputStream.ComputeRawVarint32Size(tag) +
CodedOutputStream.ComputeRawVarint32Size((uint)dataSize) +
dataSize;
}
else
{
var sizeCalculator = codec.ValueSizeCalculator;
int size = count * CodedOutputStream.ComputeRawVarint32Size(tag);
for (int i = 0; i < count; i++)
{
size += sizeCalculator(array[i]);
}
return size;
}
}
private int CalculatePackedDataSize(FieldCodec<T> codec)
{
int fixedSize = codec.FixedSize;
if (fixedSize == 0)
{
var calculator = codec.ValueSizeCalculator;
int tmp = 0;
for (int i = 0; i < count; i++)
{
tmp += calculator(array[i]);
}
return tmp;
}
else
{
return fixedSize * Count;
}
}
public void WriteTo(CodedOutputStream output, FieldCodec<T> codec)
{
// TODO: Assert that T is a value type, and that codec.Tag is packed?
if (count == 0)
{
return;
}
var writer = codec.ValueWriter;
var tag = codec.Tag;
if (typeof(T).IsValueType && WireFormat.GetTagWireType(tag) == WireFormat.WireType.LengthDelimited)
{
// Packed primitive type
uint size = (uint)CalculatePackedDataSize(codec);
output.WriteTag(tag);
output.WriteRawVarint32(size);
for (int i = 0; i < count; i++)
{
writer(output, array[i]);
}
}
else
{
// Not packed: a simple tag/value pair for each value.
// Can't use codec.WriteTagAndValue, as that omits default values.
for (int i = 0; i < count; i++)
{
output.WriteTag(tag);
writer(output, array[i]);
}
}
}
public bool IsFrozen { get { return frozen; } }
public void Freeze()
......@@ -87,18 +193,6 @@ namespace Google.Protobuf.Collections
array[count++] = item;
}
/// <summary>
/// Hack to allow us to add enums easily... will only work with int-based types.
/// </summary>
/// <param name="readEnum"></param>
internal void AddInt32(int item)
{
this.CheckMutable();
EnsureSize(count + 1);
int[] castArray = (int[]) (object) array;
castArray[count++] = item;
}
public void Clear()
{
this.CheckMutable();
......@@ -180,16 +274,7 @@ namespace Google.Protobuf.Collections
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// Returns an enumerator of the values in this list as integers.
/// Used for enum types.
/// </summary>
internal Int32Enumerator GetInt32Enumerator()
{
return new Int32Enumerator((int[])(object)array, count);
}
}
public override int GetHashCode()
{
......@@ -297,17 +382,7 @@ namespace Google.Protobuf.Collections
array[index] = value;
}
}
internal uint CalculateSize(Func<T, int> sizeComputer)
{
int size = 0;
for (int i = 0; i < count; i++)
{
size += sizeComputer(array[i]);
}
return (uint)size;
}
public struct Enumerator : IEnumerator<T>
{
private int index;
......@@ -355,46 +430,5 @@ namespace Google.Protobuf.Collections
{
}
}
internal struct Int32Enumerator : IEnumerator<int>
{
private int index;
private readonly int[] array;
private readonly int count;
public Int32Enumerator(int[] array, int count)
{
this.array = array;
this.index = -1;
this.count = count;
}
public bool MoveNext()
{
if (index + 1 >= count)
{
return false;
}
index++;
return true;
}
public void Reset()
{
index = -1;
}
// No guard here, as we're only going to use this internally...
public int Current { get { return array[index]; } }
object IEnumerator.Current
{
get { return Current; }
}
public void Dispose()
{
}
}
}
}
......@@ -145,28 +145,68 @@ namespace Google.Protobuf
private readonly Func<CodedInputStream, T> reader;
private readonly Action<CodedOutputStream, T> writer;
private readonly Func<T, int> sizeComputer;
private readonly Func<T, int> sizeCalculator;
private readonly uint tag;
private readonly int tagSize;
private readonly int fixedSize;
internal FieldCodec(
Func<CodedInputStream, T> reader,
Action<CodedOutputStream, T> writer,
Func<T, int> sizeComputer,
Func<T, int> sizeCalculator,
uint tag)
{
this.reader = reader;
this.writer = writer;
this.sizeComputer = sizeComputer;
this.sizeCalculator = sizeCalculator;
this.fixedSize = 0;
this.tag = tag;
tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
}
internal FieldCodec(
Func<CodedInputStream, T> reader,
Action<CodedOutputStream, T> writer,
int fixedSize,
uint tag)
{
this.reader = reader;
this.writer = writer;
this.sizeCalculator = _ => fixedSize;
this.fixedSize = fixedSize;
this.tag = tag;
tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
}
/// <summary>
/// Returns the size calculator for just a value.
/// </summary>
internal Func<T, int> ValueSizeCalculator { get { return sizeCalculator; } }
/// <summary>
/// Returns a delegate to write a value (unconditionally) to a coded output stream.
/// </summary>
internal Action<CodedOutputStream, T> ValueWriter { get { return writer; } }
/// <summary>
/// Returns a delegate to read a value from a coded input stream. It is assumed that
/// the stream is already positioned on the appropriate tag.
/// </summary>
internal Func<CodedInputStream, T> ValueReader { get { return reader; } }
/// <summary>
/// Returns the fixed size for an entry, or 0 if sizes vary.
/// </summary>
internal int FixedSize { get { return fixedSize; } }
public uint Tag { get { return tag; } }
public T DefaultValue { get { return Default; } }
public void Write(CodedOutputStream output, T value)
/// <summary>
/// Write a tag and the given value, *if* the value is not the default.
/// </summary>
public void WriteTagAndValue(CodedOutputStream output, T value)
{
if (!IsDefault(value))
{
......@@ -180,9 +220,13 @@ namespace Google.Protobuf
return reader(input);
}
public int CalculateSize(T value)
/// <summary>
/// Calculates the size required to write the given value, with a tag,
/// if the value is not the default.
/// </summary>
public int CalculateSizeWithTag(T value)
{
return IsDefault(value) ? 0 : sizeComputer(value) + CodedOutputStream.ComputeRawVarint32Size(tag);
return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize;
}
}
}
......@@ -49,7 +49,6 @@ namespace csharp {
RepeatedEnumFieldGenerator::RepeatedEnumFieldGenerator(
const FieldDescriptor* descriptor, int fieldOrdinal)
: FieldGeneratorBase(descriptor, fieldOrdinal) {
variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
}
RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
......@@ -57,6 +56,10 @@ RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {
}
void RepeatedEnumFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
"private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
" = pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x);");
printer->Print(variables_,
"private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
AddDeprecatedFlag(printer);
......@@ -76,53 +79,19 @@ void RepeatedEnumFieldGenerator::GenerateMergingCode(io::Printer* printer) {
void RepeatedEnumFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"input.ReadEnumArray<$type_name$>($name$_);\n");
"$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
}
void RepeatedEnumFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($name$_.Count > 0) {\n");
printer->Indent();
if (descriptor_->is_packed()) {
printer->Print(
variables_,
"output.WriteRawTag($tag_bytes$);\n"
"output.WritePackedEnumArray($name$_);\n");
} else {
printer->Print(
variables_,
"output.Write$capitalized_type_name$Array($number$, $name$_);\n");
}
printer->Outdent();
printer->Print("}\n");
"$name$_.WriteTo(output, _repeated_$name$_codec);\n");
}
void RepeatedEnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
// TODO(jonskeet): Move all this code into CodedOutputStream? It's a lot to repeat everywhere...
printer->Print(
variables_,
"if ($name$_.Count > 0) {\n");
printer->Indent();
printer->Print("int dataSize = 0;\n");
void RepeatedEnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"foreach ($type_name$ element in $name$_) {\n"
" dataSize += pb::CodedOutputStream.ComputeEnumSize((int) element);\n"
"}\n"
"size += dataSize;\n");
int tagSize = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
if (descriptor_->is_packed()) {
printer->Print(
"size += $tag_size$ + pb::CodedOutputStream.ComputeRawVarint32Size((uint) dataSize);\n",
"tag_size", SimpleItoa(tagSize));
} else {
printer->Print(
"size += $tag_size$ * $name$_.Count;\n",
"tag_size", SimpleItoa(tagSize), "name", name());
}
printer->Outdent();
printer->Print("}\n");
"size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
}
void RepeatedEnumFieldGenerator::WriteHash(io::Printer* printer) {
......
......@@ -55,6 +55,10 @@ RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {
}
void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
"private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
" = pb::FieldCodec.ForMessage($tag$, $type_name$.Parser);\n");
printer->Print(
variables_,
"private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
......@@ -75,28 +79,19 @@ void RepeatedMessageFieldGenerator::GenerateMergingCode(io::Printer* printer) {
void RepeatedMessageFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"input.ReadMessageArray($name$_, $type_name$.Parser);\n");
"$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
}
void RepeatedMessageFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
// TODO(jonskeet): Bake the foreach loop into the generated code? We lose the
// advantage of knowing the tag bytes this way :(
printer->Print(
variables_,
"if ($name$_.Count > 0) {\n"
" output.WriteMessageArray($number$, $name$_);\n"
"}\n");
"$name$_.WriteTo(output, _repeated_$name$_codec);\n");
}
void RepeatedMessageFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($name$_.Count > 0) {\n"
" foreach ($type_name$ element in $name$_) {\n"
" size += pb::CodedOutputStream.ComputeMessageSize(element);\n"
" }\n"
" size += $tag_size$ * $name$_.Count;\n"
"}\n");
"size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
}
void RepeatedMessageFieldGenerator::WriteHash(io::Printer* printer) {
......
......@@ -49,7 +49,6 @@ namespace csharp {
RepeatedPrimitiveFieldGenerator::RepeatedPrimitiveFieldGenerator(
const FieldDescriptor* descriptor, int fieldOrdinal)
: FieldGeneratorBase(descriptor, fieldOrdinal) {
variables_["packed"] = descriptor->is_packed() ? "Packed" : "";
}
RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
......@@ -57,6 +56,10 @@ RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {
}
void RepeatedPrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
"private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
" = pb::FieldCodec.For$capitalized_type_name$($tag$);\n");
printer->Print(variables_,
"private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
AddDeprecatedFlag(printer);
......@@ -74,63 +77,21 @@ void RepeatedPrimitiveFieldGenerator::GenerateMergingCode(io::Printer* printer)
}
void RepeatedPrimitiveFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(variables_,
"input.Read$capitalized_type_name$Array($name$_);\n");
printer->Print(
variables_,
"$name$_.AddEntriesFrom(input, _repeated_$name$_codec);\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(
io::Printer* printer) {
void RepeatedPrimitiveFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($name$_.Count > 0) {\n");
printer->Indent();
if (descriptor_->is_packed()) {
printer->Print(
variables_,
"output.WriteRawTag($tag_bytes$);\n"
"output.WritePacked$capitalized_type_name$Array($name$_);\n");
} else {
printer->Print(
variables_,
"output.Write$capitalized_type_name$Array($number$, $name$_);\n");
}
printer->Outdent();
printer->Print("}\n");
"$name$_.WriteTo(output, _repeated_$name$_codec);\n");
}
void RepeatedPrimitiveFieldGenerator::GenerateSerializedSizeCode(
io::Printer* printer) {
// TODO(jonskeet): Do this in the runtime if possible. It's a pain, but it must be feasible...
void RepeatedPrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
"if ($name$_.Count > 0) {\n",
"name", name());
printer->Indent();
printer->Print("int dataSize = 0;\n");
int fixedSize = GetFixedSize(descriptor_->type());
if (fixedSize == -1) {
printer->Print(
variables_,
"foreach ($type_name$ element in $name$_) {\n"
" dataSize += pb::CodedOutputStream.Compute$capitalized_type_name$Size(element);\n"
"}\n");
} else {
printer->Print(
"dataSize = $size$ * $name$_.Count;\n",
"size", SimpleItoa(fixedSize), "name", name());
}
printer->Print("size += dataSize;\n");
int tagSize = internal::WireFormat::TagSize(descriptor_->number(), descriptor_->type());
if (descriptor_->is_packed()) {
printer->Print(
"size += $tag_size$ + pb::CodedOutputStream.ComputeInt32Size(dataSize);\n",
"tag_size", SimpleItoa(tagSize));
} else {
printer->Print(
"size += $tag_size$ * $name$_.Count;\n",
"tag_size", SimpleItoa(tagSize), "name", name());
}
printer->Outdent();
printer->Print("}\n");
variables_,
"size += $name$_.CalculateSize(_repeated_$name$_codec);\n");
}
void RepeatedPrimitiveFieldGenerator::WriteHash(io::Printer* printer) {
......
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