Commit 7532f025 authored by Jon Skeet's avatar Jon Skeet

Reimplement RepeatedField<T> using an array as the backing store.

This is effectively reimplementing List<T>, but with a few advantages:
- We know that an empty repeated field is common, so don't allocate an array until we need to
- With direct access to the array, we can easily convert enum values to int without boxing
- We can relax the restrictions over what happens if the repeated field is modified while iterating, avoiding so much checking

This is somewhat risky, in that reimplementing a building block like this is *always* risky, but hey...
(The performance benefits are significant...)
parent 5a33827e
......@@ -487,7 +487,7 @@ namespace Google.Protobuf
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
List<TestNegEnum> values = new List<TestNegEnum>();
RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
input.ReadEnumArray(tag, values);
Assert.AreEqual(6, values.Count);
......@@ -511,7 +511,7 @@ namespace Google.Protobuf
uint tag;
Assert.IsTrue(input.ReadTag(out tag));
List<TestNegEnum> values = new List<TestNegEnum>();
RepeatedField<TestNegEnum> values = new RepeatedField<TestNegEnum>();
input.ReadEnumArray(tag, values);
Assert.AreEqual(6, values.Count);
......
......@@ -40,11 +40,12 @@ namespace Google.Protobuf
[Test]
public void Add_RepeatedField()
{
var list = new RepeatedField<string>();
var list = new RepeatedField<string> { "original" };
list.Add(new RepeatedField<string> { "foo", "bar" });
Assert.AreEqual(2, list.Count);
Assert.AreEqual("foo", list[0]);
Assert.AreEqual("bar", list[1]);
Assert.AreEqual(3, list.Count);
Assert.AreEqual("original", list[0]);
Assert.AreEqual("foo", list[1]);
Assert.AreEqual("bar", list[2]);
}
}
}
......@@ -38,6 +38,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Google.Protobuf.Collections;
using Google.Protobuf.Descriptors;
namespace Google.Protobuf
......@@ -700,7 +701,7 @@ namespace Google.Protobuf
}
}
public void ReadEnumArray<T>(uint fieldTag, ICollection<T> list)
public void ReadEnumArray<T>(uint fieldTag, RepeatedField<T> list)
where T : struct, IComparable, IFormattable
{
WireFormat.WireType wformat = WireFormat.GetTagWireType(fieldTag);
......@@ -712,8 +713,8 @@ namespace Google.Protobuf
int limit = PushLimit(length);
while (!ReachedLimit)
{
// TODO(jonskeet): Avoid this horrible boxing!
list.Add((T)(object) ReadEnum());
// Ghastly hack, but it works...
list.AddInt32(ReadEnum());
}
PopLimit(limit);
}
......
......@@ -743,10 +743,11 @@ namespace Google.Protobuf
{
return;
}
// TODO(jonskeet): Avoid the Cast call here. Work out a better mass "T to int" conversion.
foreach (int value in list.Cast<int>())
// Bit of a hack, to access the values as ints
var iterator = list.GetInt32Enumerator();
while (iterator.MoveNext())
{
WriteEnum(fieldNumber, value);
WriteEnum(fieldNumber, iterator.Current);
}
}
......@@ -956,15 +957,19 @@ namespace Google.Protobuf
{
return;
}
// Obviously, we'll want to get rid of this hack...
var temporaryHack = new RepeatedField<int>();
temporaryHack.Add(list.Cast<int>());
uint size = temporaryHack.CalculateSize(ComputeEnumSizeNoTag);
// Bit of a hack, to access the values as ints
var iterator = list.GetInt32Enumerator();
uint size = 0;
while (iterator.MoveNext())
{
size += (uint) ComputeEnumSizeNoTag(iterator.Current);
}
iterator.Reset();
WriteTag(fieldNumber, WireFormat.WireType.LengthDelimited);
WriteRawVarint32(size);
foreach (int value in temporaryHack)
while (iterator.MoveNext())
{
WriteEnumNoTag(value);
WriteEnumNoTag(iterator.Current);
}
}
......
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