aboutsummaryrefslogtreecommitdiff
path: root/csharp/ProtocolBuffers/UnknownFieldSet.cs
diff options
context:
space:
mode:
authorJon Skeet <skeet@pobox.com>2008-08-14 20:33:33 +0100
committerJon Skeet <skeet@pobox.com>2008-08-14 20:33:33 +0100
commit621bb698e54a7e0d2fdf74ab1f380183461ce7fa (patch)
treef1750773eee92541401126c66c811a382b0b22a7 /csharp/ProtocolBuffers/UnknownFieldSet.cs
parenta7246897ab87bc52951cc208f3775c7dfc1d6e09 (diff)
downloadprotobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.tar.gz
protobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.tar.bz2
protobuf-621bb698e54a7e0d2fdf74ab1f380183461ce7fa.zip
Some work on AbstractBuilder, and complete implementation of UnknownField and UnknownFieldSet
Diffstat (limited to 'csharp/ProtocolBuffers/UnknownFieldSet.cs')
-rw-r--r--csharp/ProtocolBuffers/UnknownFieldSet.cs394
1 files changed, 386 insertions, 8 deletions
diff --git a/csharp/ProtocolBuffers/UnknownFieldSet.cs b/csharp/ProtocolBuffers/UnknownFieldSet.cs
index 787126ad..a0d84d40 100644
--- a/csharp/ProtocolBuffers/UnknownFieldSet.cs
+++ b/csharp/ProtocolBuffers/UnknownFieldSet.cs
@@ -14,26 +14,404 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
+using System.Collections.Generic;
+using System.IO;
namespace Google.ProtocolBuffers {
public class UnknownFieldSet {
- public int SerializedSizeAsMessageSet;
+ private static readonly UnknownFieldSet defaultInstance = new UnknownFieldSet(new Dictionary<int, UnknownField>());
+
+ private readonly IDictionary<int, UnknownField> fields;
+
+ private UnknownFieldSet(IDictionary<int, UnknownField> fields) {
+ this.fields = fields;
+ }
+
+ /// <summary>
+ /// Creates a new unknown field set builder.
+ /// </summary>
+ public static Builder CreateBuilder() {
+ return new Builder();
+ }
+
+ /// <summary>
+ /// Creates a new unknown field set builder
+ /// and initialize it from <paramref name="original"/>.
+ /// </summary>
+ public static Builder CreateBuilder(UnknownFieldSet original) {
+ return new Builder().MergeFrom(original);
+ }
+
+ public static UnknownFieldSet DefaultInstance {
+ get { return defaultInstance; }
+ }
+
+ /// <summary>
+ /// Creates and returns a copy of the mapping from field numbers to values.
+ /// </summary>
+ public IDictionary<int, UnknownField> FieldDictionary {
+ get { return new Dictionary<int, UnknownField>(fields); }
+ }
+
+ /// <summary>
+ /// Checks whether or not the given field number is present in the set.
+ /// </summary>
+ public bool HasField(int field) {
+ return fields.ContainsKey(field);
+ }
+
+ /// <summary>
+ /// Fetches a field by number, returning an empty field if not present.
+ /// Never returns null.
+ /// </summary>
+ public UnknownField this[int number] {
+ get {
+ UnknownField ret;
+ if (!fields.TryGetValue(number, out ret)) {
+ ret = UnknownField.DefaultInstance;
+ }
+ return ret;
+ }
+ }
+
+ /// <summary>
+ /// Serializes the set and writes it to <paramref name="output"/>.
+ /// </summary>
public void WriteTo(CodedOutputStream output) {
- throw new NotImplementedException();
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ entry.Value.WriteTo(entry.Key, output);
+ }
}
- public int SerializedSize { get { return 0; } }
+ /// <summary>
+ /// Gets the number of bytes required to encode this set.
+ /// </summary>
+ public int SerializedSize {
+ get {
+ int result = 0;
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ result += entry.Value.GetSerializedSize(entry.Key);
+ }
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Converts the set to a string in protocol buffer text format. This
+ /// is just a trivial wrapper around TextFormat.PrintToString.
+ /// </summary>
+ public override String ToString() {
+ return TextFormat.PrintToString(this);
+ }
+
+ /// <summary>
+ /// Serializes the message to a ByteString and returns it. This is
+ /// just a trivial wrapper around WriteTo(CodedOutputStream).
+ /// </summary>
+ /// <returns></returns>
+ public ByteString ToByteString() {
+ ByteString.CodedBuilder codedBuilder = new ByteString.CodedBuilder(SerializedSize);
+ WriteTo(codedBuilder.CodedOutput);
+ return codedBuilder.Build();
+ }
+
+ /// <summary>
+ /// Serializes the message to a byte array and returns it. This is
+ /// just a trivial wrapper around WriteTo(CodedOutputStream).
+ /// </summary>
+ /// <returns></returns>
+ public byte[] ToByteArray() {
+ byte[] data = new byte[SerializedSize];
+ CodedOutputStream output = CodedOutputStream.CreateInstance(data);
+ WriteTo(output);
+ output.CheckNoSpaceLeft();
+ return data;
+ }
+
+ /// <summary>
+ /// Serializes the message and writes it to <paramref name="output"/>. This is
+ /// just a trivial wrapper around WriteTo(CodedOutputStream).
+ /// </summary>
+ /// <param name="output"></param>
+ public void WriteTo(Stream output) {
+ CodedOutputStream codedOutput = CodedOutputStream.CreateInstance(output);
+ WriteTo(codedOutput);
+ codedOutput.Flush();
+ }
+
+ /// <summary>
+ /// Serializes the set and writes it to <paramref name="output"/> using
+ /// the MessageSet wire format.
+ /// </summary>
+ public void WriteAsMessageSetTo(CodedOutputStream output) {
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ entry.Value.WriteAsMessageSetExtensionTo(entry.Key, output);
+ }
+ }
+
+ /// <summary>
+ /// Gets the number of bytes required to encode this set using the MessageSet
+ /// wire format.
+ /// </summary>
+ public int SerializedSizeAsMessageSet {
+ get {
+ int result = 0;
+ foreach (KeyValuePair<int, UnknownField> entry in fields) {
+ result += entry.Value.GetSerializedSizeAsMessageSetExtension(entry.Key);
+ }
+ return result;
+ }
+ }
+
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given input.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(CodedInputStream input) {
+ return CreateBuilder().MergeFrom(input).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given data.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(ByteString data) {
+ return CreateBuilder().MergeFrom(data).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given data.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(byte[] data) {
+ return CreateBuilder().MergeFrom(data).Build();
+ }
+
+ /// <summary>
+ /// Parses an UnknownFieldSet from the given input.
+ /// </summary>
+ public static UnknownFieldSet ParseFrom(Stream input) {
+ return CreateBuilder().MergeFrom(input).Build();
+ }
public class Builder
{
- internal void MergeFrom(CodedInputStream codedInputStream) {
- throw new NotImplementedException();
+ private Dictionary<int, UnknownField> fields = new Dictionary<int, UnknownField>();
+
+ // Optimization: We keep around a builder for the last field that was
+ // modified so that we can efficiently add to it multiple times in a
+ // row (important when parsing an unknown repeated field).
+ int lastFieldNumber;
+ UnknownField.Builder lastField;
+
+ internal Builder() {
+ }
+
+ /// <summary>
+ /// Returns a field builder for the specified field number, including any values
+ /// which already exist.
+ /// </summary>
+ private UnknownField.Builder GetFieldBuilder(int number) {
+ if (lastField != null) {
+ if (number == lastFieldNumber) {
+ return lastField;
+ }
+ // Note: AddField() will reset lastField and lastFieldNumber.
+ AddField(lastFieldNumber, lastField.Build());
+ }
+ if (number == 0) {
+ return null;
+ }
+
+ lastField = UnknownField.CreateBuilder();
+ UnknownField existing;
+ if (fields.TryGetValue(number, out existing)) {
+ lastField.MergeFrom(existing);
+ }
+ lastFieldNumber = number;
+ return lastField;
+ }
+
+ /// <summary>
+ /// Build the UnknownFieldSet and return it. Once this method has been called,
+ /// this instance will no longer be usable. Calling any method after this
+ /// will throw a NullReferenceException.
+ /// </summary>
+ public UnknownFieldSet Build() {
+ GetFieldBuilder(0); // Force lastField to be built.
+ UnknownFieldSet result = fields.Count == 0 ? DefaultInstance : new UnknownFieldSet(fields);
+ fields = null;
+ return result;
+ }
+
+ /// <summary>
+ /// Adds a field to the set. If a field with the same number already exists, it
+ /// is replaced.
+ /// </summary>
+ public Builder AddField(int number, UnknownField field) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ if (lastField != null && lastFieldNumber == number) {
+ // Discard this.
+ lastField = null;
+ lastFieldNumber = 0;
+ }
+ fields[number] = field;
+ return this;
+ }
+
+ /// <summary>
+ /// Resets the builder to an empty set.
+ /// </summary>
+ public Builder Clear() {
+ fields.Clear();
+ lastFieldNumber = 0;
+ lastField = null;
+ return this;
+ }
+
+ /// <summary>
+ /// Parse an entire message from <paramref name="input"/> and merge
+ /// its fields into this set.
+ /// </summary>
+ public Builder MergeFrom(CodedInputStream input) {
+ while (true) {
+ uint tag = input.ReadTag();
+ if (tag == 0 || !MergeFieldFrom(tag, input)) {
+ break;
+ }
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// Parse a single field from <paramref name="input"/> and merge it
+ /// into this set.
+ /// </summary>
+ /// <param name="tag">The field's tag number, which was already parsed.</param>
+ /// <param name="input">The coded input stream containing the field</param>
+ /// <returns>false if the tag is an "end group" tag, true otherwise</returns>
+ public bool MergeFieldFrom(uint tag, CodedInputStream input) {
+ int number = WireFormat.GetTagFieldNumber(tag);
+ switch (WireFormat.GetTagWireType(tag)) {
+ case WireFormat.WireType.Varint:
+ // TODO(jonskeet): Check this is correct (different to Java)
+ GetFieldBuilder(number).AddVarint(input.ReadUInt64());
+ return true;
+ case WireFormat.WireType.Fixed64:
+ GetFieldBuilder(number).AddFixed64(input.ReadFixed64());
+ return true;
+ case WireFormat.WireType.LengthDelimited:
+ GetFieldBuilder(number).AddLengthDelimited(input.ReadBytes());
+ return true;
+ case WireFormat.WireType.StartGroup: {
+ Builder subBuilder = CreateBuilder();
+ input.ReadUnknownGroup(number, subBuilder);
+ GetFieldBuilder(number).AddGroup(subBuilder.Build());
+ return true;
+ }
+ case WireFormat.WireType.EndGroup:
+ return false;
+ case WireFormat.WireType.Fixed32:
+ GetFieldBuilder(number).AddFixed32(input.ReadFixed32());
+ return true;
+ default:
+ throw InvalidProtocolBufferException.InvalidWireType();
+ }
+ }
+
+ /// <summary>
+ /// Parses <paramref name="input"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(Stream input) {
+ CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
+ MergeFrom(codedInput);
+ codedInput.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(ByteString data) {
+ CodedInputStream input = data.CreateCodedInput();
+ MergeFrom(input);
+ input.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Parses <paramref name="data"/> as an UnknownFieldSet and merge it
+ /// with the set being built. This is just a small wrapper around
+ /// MergeFrom(CodedInputStream).
+ /// </summary>
+ public Builder MergeFrom(byte[] data) {
+ CodedInputStream input = CodedInputStream.CreateInstance(data);
+ MergeFrom(input);
+ input.CheckLastTagWas(0);
+ return this;
+ }
+
+ /// <summary>
+ /// Convenience method for merging a new field containing a single varint
+ /// value. This is used in particular when an unknown enum value is
+ /// encountered.
+ /// </summary>
+ public Builder MergeVarintField(int number, ulong value) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ GetFieldBuilder(number).AddVarint(value);
+ return this;
+ }
+
+ /// <summary>
+ /// Merges the fields from <paramref name="other"/> into this set.
+ /// If a field number exists in both sets, the values in <paramref name="other"/>
+ /// will be appended to the values in this set.
+ /// </summary>
+ public Builder MergeFrom(UnknownFieldSet other) {
+ if (other != DefaultInstance) {
+ foreach(KeyValuePair<int, UnknownField> entry in other.fields) {
+ MergeField(entry.Key, entry.Value);
+ }
+ }
+ return this;
+ }
+
+ /// <summary>
+ /// Checks if the given field number is present in the set.
+ /// </summary>
+ public bool HasField(int number) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ return number == lastFieldNumber || fields.ContainsKey(number);
+ }
+
+ /// <summary>
+ /// Adds a field to the unknown field set. If a field with the same
+ /// number already exists, the two are merged.
+ /// </summary>
+ public Builder MergeField(int number, UnknownField field) {
+ if (number == 0) {
+ throw new ArgumentOutOfRangeException("number", "Zero is not a valid field number.");
+ }
+ if (HasField(number)) {
+ GetFieldBuilder(number).MergeFrom(field);
+ } else {
+ // Optimization: We could call getFieldBuilder(number).mergeFrom(field)
+ // in this case, but that would create a copy of the Field object.
+ // We'd rather reuse the one passed to us, so call AddField() instead.
+ AddField(number, field);
+ }
+ return this;
}
- }
- internal void WriteAsMessageSetTo(CodedOutputStream output) {
- throw new NotImplementedException();
}
}
}