Commit 621bb698 authored by Jon Skeet's avatar Jon Skeet

Some work on AbstractBuilder, and complete implementation of UnknownField and UnknownFieldSet

parent a7246897
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
using System.Collections;
using System.IO;
namespace Google.ProtocolBuffers {
/// <summary>
/// Implementation of the non-generic IMessage interface as far as possible.
/// </summary>
public abstract class AbstractBuilder : IBuilder {
public bool Initialized {
get { throw new NotImplementedException(); }
}
public IDictionary<FieldDescriptor, object> AllFields {
get { throw new NotImplementedException(); }
}
public object this[FieldDescriptor field] {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public MessageDescriptor DescriptorForType {
get { throw new NotImplementedException(); }
}
public int GetRepeatedFieldCount(FieldDescriptor field) {
throw new NotImplementedException();
}
public object this[FieldDescriptor field, int index] {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public bool HasField(FieldDescriptor field) {
throw new NotImplementedException();
}
public IBuilder Clear() {
foreach(FieldDescriptor field in AllFields.Keys) {
ClearField(field);
}
return this;
}
public IBuilder MergeFrom(IMessage other) {
if (other.DescriptorForType != DescriptorForType) {
throw new ArgumentException("MergeFrom(Message) can only merge messages of the same type.");
}
// 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 Message 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 AllFields) {
FieldDescriptor field = entry.Key;
if (field.IsRepeated) {
// Concatenate repeated fields
foreach (object element in (IEnumerable) entry.Value) {
AddRepeatedField(field, element);
}
} else if (field.MappedType == MappedType.Message) {
// Merge singular messages
IMessage existingValue = (IMessage) this[field];
if (existingValue == existingValue.DefaultInstanceForType) {
this[field] = entry.Value;
} else {
this[field] = existingValue.CreateBuilderForType()
.MergeFrom(existingValue)
.MergeFrom((IMessage) entry.Value)
.Build();
}
} else {
// Overwrite simple values
this[field] = entry.Value;
}
}
return this;
}
public IMessage Build() {
throw new NotImplementedException();
}
public IMessage BuildPartial() {
throw new NotImplementedException();
}
public IBuilder Clone() {
throw new NotImplementedException();
}
public IBuilder MergeFrom(CodedInputStream input) {
return MergeFrom(input, ExtensionRegistry.Empty);
}
public IBuilder MergeFrom(CodedInputStream input, ExtensionRegistry extensionRegistry) {
UnknownFieldSet.Builder unknownFields = UnknownFieldSet.CreateBuilder(UnknownFields);
FieldSet.MergeFrom(input, unknownFields, extensionRegistry, this);
UnknownFields = unknownFields.Build();
return this;
}
public IMessage DefaultInstanceForType {
get { throw new NotImplementedException(); }
}
public IBuilder NewBuilderForField<TField>(FieldDescriptor field) {
throw new NotImplementedException();
}
public IBuilder ClearField(FieldDescriptor field) {
throw new NotImplementedException();
}
public IBuilder AddRepeatedField(FieldDescriptor field, object value) {
throw new NotImplementedException();
}
public IBuilder MergeUnknownFields(UnknownFieldSet unknownFields) {
UnknownFields = UnknownFieldSet.CreateBuilder(UnknownFields)
.MergeFrom(unknownFields)
.Build();
return this;
}
public UnknownFieldSet UnknownFields {
get {
throw new NotImplementedException();
}
set {
throw new NotImplementedException();
}
}
public IBuilder MergeFrom(ByteString data) {
CodedInputStream input = data.CreateCodedInput();
MergeFrom(input);
input.CheckLastTagWas(0);
return this;
}
public IBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry) {
CodedInputStream input = data.CreateCodedInput();
MergeFrom(input, extensionRegistry);
input.CheckLastTagWas(0);
return this;
}
public IBuilder MergeFrom(byte[] data) {
CodedInputStream input = CodedInputStream.CreateInstance(data);
MergeFrom(input);
input.CheckLastTagWas(0);
return this;
}
public IBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry) {
CodedInputStream input = CodedInputStream.CreateInstance(data);
MergeFrom(input, extensionRegistry);
input.CheckLastTagWas(0);
return this;
}
public IBuilder MergeFrom(Stream input) {
CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
MergeFrom(codedInput);
codedInput.CheckLastTagWas(0);
return this;
}
public IBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry) {
CodedInputStream codedInput = CodedInputStream.CreateInstance(input);
MergeFrom(codedInput, extensionRegistry);
codedInput.CheckLastTagWas(0);
return this;
}
}
}
......@@ -34,13 +34,15 @@ namespace Google.ProtocolBuffers {
#region Unimplemented members of IMessage
public abstract MessageDescriptor DescriptorForType { get; }
public abstract IDictionary<Descriptors.FieldDescriptor, object> AllFields { get; }
public abstract bool HasField(Descriptors.FieldDescriptor field);
public abstract object this[Descriptors.FieldDescriptor field] { get; }
public abstract int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
public abstract object this[Descriptors.FieldDescriptor field, int index] { get; }
public abstract IDictionary<FieldDescriptor, object> AllFields { get; }
public abstract bool HasField(FieldDescriptor field);
public abstract object this[FieldDescriptor field] { get; }
public abstract int GetRepeatedFieldCount(FieldDescriptor field);
public abstract object this[FieldDescriptor field, int index] { get; }
public abstract UnknownFieldSet UnknownFields { get; }
public abstract IMessage DefaultInstanceForType { get; }
// FIXME
IMessage IMessage.DefaultInstanceForType { get { return null; } }
IBuilder IMessage.CreateBuilderForType() { return null; }
#endregion
public bool IsInitialized {
......@@ -152,8 +154,6 @@ namespace Google.ProtocolBuffers {
codedOutput.Flush();
}
public abstract IBuilder CreateBuilderForType();
public override bool Equals(object other) {
if (other == this) {
return true;
......@@ -173,5 +173,73 @@ namespace Google.ProtocolBuffers {
hash = (53 * hash) + AllFields.GetHashCode();
return hash;
}
#region IMessage Members
MessageDescriptor IMessage.DescriptorForType {
get { throw new NotImplementedException(); }
}
IDictionary<FieldDescriptor, object> IMessage.AllFields {
get { throw new NotImplementedException(); }
}
bool IMessage.HasField(FieldDescriptor field) {
throw new NotImplementedException();
}
object IMessage.this[FieldDescriptor field] {
get { throw new NotImplementedException(); }
}
int IMessage.GetRepeatedFieldCount(FieldDescriptor field) {
throw new NotImplementedException();
}
object IMessage.this[FieldDescriptor field, int index] {
get { throw new NotImplementedException(); }
}
UnknownFieldSet IMessage.UnknownFields {
get { throw new NotImplementedException(); }
}
bool IMessage.IsInitialized {
get { throw new NotImplementedException(); }
}
void IMessage.WriteTo(CodedOutputStream output) {
throw new NotImplementedException();
}
int IMessage.SerializedSize {
get { throw new NotImplementedException(); }
}
bool IMessage.Equals(object other) {
throw new NotImplementedException();
}
int IMessage.GetHashCode() {
throw new NotImplementedException();
}
string IMessage.ToString() {
throw new NotImplementedException();
}
ByteString IMessage.ToByteString() {
throw new NotImplementedException();
}
byte[] IMessage.ToByteArray() {
throw new NotImplementedException();
}
void IMessage.WriteTo(Stream output) {
throw new NotImplementedException();
}
#endregion
}
}
......@@ -105,6 +105,15 @@ namespace Google.ProtocolBuffers {
return ToString(Encoding.UTF8);
}
/// <summary>
/// Creates a CodedInputStream from this ByteString's data.
/// </summary>
public CodedInputStream CreateCodedInput() {
// We trust CodedInputStream not to reveal the provided byte array or modify it
return CodedInputStream.CreateInstance(bytes);
}
// TODO(jonskeet): CopyTo, Equals, GetHashCode if they turn out to be required
/// <summary>
......
......@@ -321,7 +321,7 @@ namespace Google.ProtocolBuffers {
/// Reads a field of any primitive type. Enums, groups and embedded
/// messages are not handled by this method.
/// </summary>
public object readPrimitiveField(FieldType fieldType) {
public object ReadPrimitiveField(FieldType fieldType) {
switch (fieldType) {
case FieldType.Double: return ReadDouble();
case FieldType.Float: return ReadFloat();
......
......@@ -271,7 +271,7 @@ namespace Google.ProtocolBuffers {
case FieldType.SFixed64: WriteSFixed64(fieldNumber, (long)value); break;
case FieldType.SInt32: WriteSInt32(fieldNumber, (int)value); break;
case FieldType.SInt64: WriteSInt64(fieldNumber, (long)value); break;
case FieldType.Enum: WriteEnum(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
case FieldType.Enum: WriteEnum(fieldNumber, ((EnumValueDescriptor)value).Number);
break;
}
}
......@@ -615,7 +615,7 @@ namespace Google.ProtocolBuffers {
* @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(Descriptors.FieldDescriptor)} for
* {@link Message#getField(FieldDescriptor)} for
* this field.
*/
public static int ComputeFieldSize(FieldType fieldType, int fieldNumber, Object value) {
......@@ -637,7 +637,7 @@ namespace Google.ProtocolBuffers {
case FieldType.SFixed64: return ComputeSFixed64Size(fieldNumber, (long)value);
case FieldType.SInt32: return ComputeSInt32Size(fieldNumber, (int)value);
case FieldType.SInt64: return ComputeSInt64Size(fieldNumber, (long)value);
case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((Descriptors.EnumValueDescriptor)value).Number);
case FieldType.Enum: return ComputeEnumSize(fieldNumber, ((EnumValueDescriptor)value).Number);
default:
throw new ArgumentOutOfRangeException("Invalid field type " + fieldType);
}
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace Google.ProtocolBuffers.Collections {
/// <summary>
/// Utilities class for dealing with lists.
/// </summary>
static class Lists<T> {
static readonly ReadOnlyCollection<T> empty = new ReadOnlyCollection<T>(new T[0]);
/// <summary>
/// Returns an immutable empty list.
/// </summary>
internal static ReadOnlyCollection<T> Empty {
get { return empty; }
}
}
}
......@@ -13,5 +13,17 @@ namespace Google.ProtocolBuffers.Descriptors {
public FieldType FieldType { get; set; }
public int FieldNumber { get; set; }
public bool IsExtension { get; set; }
public MessageDescriptor ContainingType { get; set; }
public string FullName { get; set; }
public bool IsOptional { get; set; }
public MessageDescriptor MessageType { get; set; }
public MessageDescriptor ExtensionScope { get; set; }
}
}
......@@ -7,6 +7,14 @@ namespace Google.ProtocolBuffers.Descriptors {
/// Type as it's mapped onto a .NET type.
/// </summary>
public enum MappedType {
Message
Int32,
Int64,
Single,
Double,
Boolean,
String,
ByteString,
Message,
Enum
}
}
......@@ -5,5 +5,6 @@ namespace Google.ProtocolBuffers.Descriptors {
public class MessageDescriptor {
public IList<FieldDescriptor> Fields;
public DescriptorProtos.MessageOptions Options;
public string FullName;
}
}
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers
{
public sealed class ExtensionInfo {
/// <summary>
/// The extension's descriptor
/// </summary>
public FieldDescriptor Descriptor { get; private set; }
/// <summary>
/// A default instance of the extensions's type, if it has a message type,
/// or null otherwise.
/// </summary>
public IMessage DefaultInstance { get; private set; }
internal ExtensionInfo(FieldDescriptor descriptor) : this(descriptor, null) {
}
internal ExtensionInfo(FieldDescriptor descriptor, IMessage defaultInstance) {
Descriptor = descriptor;
DefaultInstance = defaultInstance;
}
}
}
\ No newline at end of file
......@@ -13,7 +13,167 @@
// 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.Collections.Generic;
using Google.ProtocolBuffers.Descriptors;
using System;
namespace Google.ProtocolBuffers {
public class ExtensionRegistry {
/// <summary>
/// TODO(jonskeet): Copy docs from Java
/// </summary>
public sealed class ExtensionRegistry {
private static readonly ExtensionRegistry empty = new ExtensionRegistry(
new Dictionary<string, ExtensionInfo>(),
new Dictionary<DescriptorIntPair, ExtensionInfo>(),
true);
private readonly IDictionary<string, ExtensionInfo> extensionsByName;
private readonly IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
private readonly bool readOnly;
private ExtensionRegistry(IDictionary<String, ExtensionInfo> extensionsByName,
IDictionary<DescriptorIntPair, ExtensionInfo> extensionsByNumber,
bool readOnly) {
this.extensionsByName = extensionsByName;
this.extensionsByNumber = extensionsByNumber;
this.readOnly = readOnly;
}
/// <summary>
/// Construct a new, empty instance.
/// </summary>
public static ExtensionRegistry CreateInstance() {
return new ExtensionRegistry(new Dictionary<string, ExtensionInfo>(),
new Dictionary<DescriptorIntPair, ExtensionInfo>(), false);
}
/// <summary>
/// Get the unmodifiable singleton empty instance.
/// </summary>
public static ExtensionRegistry Empty {
get { return empty; }
}
public ExtensionRegistry AsReadOnly() {
return new ExtensionRegistry(extensionsByName, extensionsByNumber, true);
}
/// <summary>
/// Finds an extension by fully-qualified field name, in the
/// proto namespace, i.e. result.Descriptor.FullName will match
/// <paramref name="fullName"/> if a match is found. A null
/// reference is returned if the extension can't be found.
/// </summary>
public ExtensionInfo this[string fullName] {
get {
ExtensionInfo ret;
extensionsByName.TryGetValue(fullName, out ret);
return ret;
}
}
/// <summary>
/// Finds an extension by containing type and field number.
/// A null reference is returned if the extension can't be found.
/// </summary>
public ExtensionInfo this[MessageDescriptor containingType, int fieldNumber] {
get {
ExtensionInfo ret;
extensionsByNumber.TryGetValue(new DescriptorIntPair(containingType, fieldNumber), out ret);
return ret;
}
}
/// <summary>
/// Add an extension from a generated file to the registry.
/// </summary>
public void Add<TContainer, TExtension> (GeneratedExtension<TContainer, TExtension> extension)
where TContainer : IMessage<TContainer> {
if (extension.Descriptor.MappedType == MappedType.Message) {
Add(new ExtensionInfo(extension.Descriptor, extension.MessageDefaultInstance));
} else {
Add(new ExtensionInfo(extension.Descriptor, null));
}
}
/// <summary>
/// Adds a non-message-type extension to the registry by descriptor.
/// </summary>
/// <param name="type"></param>
public void Add(FieldDescriptor type) {
if (type.MappedType == MappedType.Message) {
throw new ArgumentException("ExtensionRegistry.Add() must be provided a default instance "
+ "when adding an embedded message extension.");
}
Add(new ExtensionInfo(type, null));
}
/// <summary>
/// Adds a message-type-extension to the registry by descriptor.
/// </summary>
/// <param name="type"></param>
/// <param name="defaultInstance"></param>
public void Add(FieldDescriptor type, IMessage defaultInstance) {
if (type.MappedType != MappedType.Message) {
throw new ArgumentException("ExtensionRegistry.Add() provided a default instance for a "
+ "non-message extension.");
}
Add(new ExtensionInfo(type, defaultInstance));
}
private void Add(ExtensionInfo extension) {
if (readOnly) {
throw new InvalidOperationException("Cannot add entries to a read-only extension registry");
}
if (!extension.Descriptor.IsExtension) {
throw new ArgumentException("ExtensionRegistry.add() was given a FieldDescriptor for a "
+ "regular (non-extension) field.");
}
extensionsByName[extension.Descriptor.FullName] = extension;
extensionsByNumber[new DescriptorIntPair(extension.Descriptor.ContainingType,
extension.Descriptor.FieldNumber)] = extension;
FieldDescriptor field = extension.Descriptor;
if (field.ContainingType.Options.IsMessageSetWireFormat
&& field.FieldType == FieldType.Message
&& field.IsOptional
&& field.ExtensionScope == field.MessageType) {
// This is an extension of a MessageSet type defined within the extension
// type's own scope. For backwards-compatibility, allow it to be looked
// up by type name.
extensionsByName[field.MessageType.FullName] = extension;
}
}
/// <summary>
/// Nested type just used to represent a pair of MessageDescriptor and int, as
/// the key into the "by number" map.
/// </summary>
private struct DescriptorIntPair : IEquatable<DescriptorIntPair> {
readonly MessageDescriptor descriptor;
readonly int number;
internal DescriptorIntPair(MessageDescriptor descriptor, int number) {
this.descriptor = descriptor;
this.number = number;
}
public override int GetHashCode() {
return descriptor.GetHashCode() * ((1 << 16) - 1) + number;
}
public override bool Equals(object obj) {
if (!(obj is DescriptorIntPair)) {
return false;
}
return Equals((DescriptorIntPair)obj);
}
public bool Equals(DescriptorIntPair other) {
return descriptor == other.descriptor && number == other.number;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers.FieldAccess {
/// <summary>
/// Declarations of delegate types used for field access. Can't
/// use Func and Action (other than one parameter) as we can't guarantee .NET 3.5.
/// </summary>
delegate bool HasFunction<TMessage>(TMessage message);
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.FieldAccess {
public class FieldAccessorTable<TMessage, TBuilder>
where TMessage : IMessage<TMessage>
where TBuilder : IBuilder<TMessage> {
readonly MessageDescriptor descriptor;
public MessageDescriptor Descriptor {
get { return descriptor; }
}
public FieldAccessorTable(MessageDescriptor descriptor, String[] pascalCaseNames) {
this.descriptor = descriptor;
}
internal IFieldAccessor<TMessage, TBuilder> this[FieldDescriptor field] {
get { return null; }
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers.FieldAccess {
internal interface IFieldAccessor<TMessage, TBuilder>
where TMessage : IMessage<TMessage>
where TBuilder : IBuilder<TMessage> {
void AddRepeated(IBuilder<TMessage> builder, object value);
bool Has(IMessage<TMessage> message);
int GetRepeatedCount(IMessage<TMessage> message);
void Clear(TBuilder builder);
TBuilder CreateBuilder();
/// <summary>
/// Accessor for single fields
/// </summary>
object this[IMessage<TMessage> message] { get; }
/// <summary>
/// Mutator for single fields
/// </summary>
object this[IBuilder<TMessage> builder] { set; }
/// <summary>
/// Accessor for repeated fields
/// </summary>
object this[IMessage<TMessage> message, int index] { get; }
/// <summary>
/// Mutator for repeated fields
/// </summary>
object this[IBuilder<TMessage> builder, int index] { set; }
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers.FieldAccess {
internal class SingularFieldAccessor<TMessage, TBuilder> : IFieldAccessor<TMessage, TBuilder>
where TMessage : IMessage<TMessage>
where TBuilder : IBuilder<TMessage> {
readonly HasFunction<TMessage> hasProxy;
readonly Action<TBuilder> clearProxy;
internal SingularFieldAccessor(FieldDescriptor descriptor, String pascalCaseName) {
/* Class<? extends GeneratedMessage> messageClass,
Class<? extends GeneratedMessage.Builder> builderClass) {
getMethod = getMethodOrDie(messageClass, "get" + camelCaseName);
type = getMethod.getReturnType();
setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type);
hasMethod =
getMethodOrDie(messageClass, "has" + camelCaseName);
clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); */
}
public bool Has(IMessage<TMessage> message) {
return false;// hasProxy(message);
}
public void Clear(TBuilder builder) {
// clearProxy(builder);
}
public TBuilder CreateBuilder() {
// return createBuilderProxy(builder);
return default(TBuilder);
}
public object this[IMessage<TMessage> message] {
get { return null;/* getProxy(message);*/ }
}
public object this[IBuilder<TMessage> builder] {
set { /*setProxy(builder, value);*/ }
}
#region Repeated operations (which just throw an exception)
public object this[IMessage<TMessage> message, int index] {
get { throw new InvalidOperationException("Repeated operation called on singular field"); }
}
public object this[IBuilder<TMessage> builder, int index] {
set { throw new InvalidOperationException("Repeated operation called on singular field"); }
}
public int GetRepeatedCount(IMessage<TMessage> message) {
throw new InvalidOperationException("Repeated operation called on singular field");
}
public void AddRepeated(IBuilder<TMessage> builder, object value) {
throw new InvalidOperationException("Repeated operation called on singular field");
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace Google.ProtocolBuffers {
public class FieldSet {
public static void MergeFrom(CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistry extensionRegistry,
IBuilder builder) {
while (true) {
uint tag = input.ReadTag();
if (tag == 0) {
break;
}
if (!MergeFieldFrom(input, unknownFields, extensionRegistry,
builder, tag)) {
// end group tag
break;
}
}
}
public static bool MergeFieldFrom(CodedInputStream input,
UnknownFieldSet.Builder unknownFields,
ExtensionRegistry extensionRegistry,
IBuilder builder,
uint tag) {
throw new NotImplementedException();
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
namespace Google.ProtocolBuffers {
/// <summary>
/// Base class for all generated extensions.
/// </summary>
/// <remarks>
/// The protocol compiler generates a static singleton instance of this
/// class for each extension. For exmaple, imagine a .proto file with:
/// <code>
/// message Foo {
/// extensions 1000 to max
/// }
///
/// extend Foo {
/// optional int32 bar;
/// }
/// </code>
/// Then MyProto.Foo.Bar has type GeneratedExtension&lt;MyProto.Foo,int&gt;.
/// <para />
/// In general, users should ignore the details of this type, and
/// simply use the static singletons as parmaeters to the extension accessors
/// in ExtendableMessage and ExtendableBuilder.
/// </remarks>
public class GeneratedExtension<TContainer, TExtension> where TContainer : IMessage<TContainer> {
public FieldDescriptor Descriptor;
public IMessage MessageDefaultInstance;
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Google.ProtocolBuffers.Descriptors;
using Google.ProtocolBuffers.FieldAccess;
namespace Google.ProtocolBuffers {
/// <summary>
/// All generated protocol message classes extend this class. It implements
/// most of the IMessage and IBuilder interfaces using reflection. Users
/// can ignore this class as an implementation detail.
/// </summary>
public abstract class GeneratedMessage<TMessage, TBuilder> : AbstractMessage, IMessage<TMessage>
where TMessage : GeneratedMessage<TMessage, TBuilder> where TBuilder : IBuilder<TMessage> {
private readonly UnknownFieldSet unknownFields = UnknownFieldSet.DefaultInstance;
protected abstract FieldAccessorTable<TMessage, TBuilder> InternalFieldAccessors { get; }
public override MessageDescriptor DescriptorForType {
get { return InternalFieldAccessors.Descriptor; }
}
public IMessage<TMessage> DefaultInstanceForType {
get { throw new System.NotImplementedException(); }
}
public IBuilder<TMessage> CreateBuilderForType() {
throw new System.NotImplementedException();
}
private IDictionary<FieldDescriptor, Object> GetMutableFieldMap() {
var ret = new Dictionary<FieldDescriptor, object>();
MessageDescriptor descriptor = DescriptorForType;
foreach (FieldDescriptor field in descriptor.Fields) {
IFieldAccessor<TMessage, TBuilder> accessor = InternalFieldAccessors[field];
if ((field.IsRepeated && accessor.GetRepeatedCount(this) != 0)
|| accessor.Has(this)) {
ret[field] = accessor[this];
}
}
return ret;
}
public override IDictionary<FieldDescriptor, object> AllFields {
// FIXME: Make it immutable
get { return GetMutableFieldMap(); }
}
public override bool HasField(FieldDescriptor field) {
return InternalFieldAccessors[field].Has(this);
}
public override int GetRepeatedFieldCount(FieldDescriptor field) {
return InternalFieldAccessors[field].GetRepeatedCount(this);
}
public override object this[FieldDescriptor field, int index] {
get { return InternalFieldAccessors[field][this, index]; }
}
public override object this[FieldDescriptor field] {
get { return InternalFieldAccessors[field][this]; }
}
public override UnknownFieldSet UnknownFields {
get { return unknownFields; }
}
}
}
......@@ -29,7 +29,6 @@ namespace Google.ProtocolBuffers {
/// how IEnumerable and IEnumerable&lt;T&gt; work.
/// </summary>
public interface IBuilder {
IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
/// <summary>
/// Returns true iff all required fields in the message and all
/// embedded messages are set.
......@@ -41,15 +40,15 @@ namespace Google.ProtocolBuffers {
/// The returned map may or may not reflect future changes to the builder.
/// Either way, the returned map is unmodifiable.
/// </summary>
IDictionary<ProtocolBuffers.Descriptors.FieldDescriptor, object> AllFields { get; }
IDictionary<FieldDescriptor, object> AllFields { get; }
/// <summary>
/// Allows getting and setting of a field.
/// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor)"/>
/// <see cref="IMessage{T}.Item(FieldDescriptor)"/>
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
object this[Descriptors.FieldDescriptor field] { get; set; }
object this[FieldDescriptor field] { get; set; }
/// <summary>
/// Get the message's type's descriptor.
......@@ -62,18 +61,44 @@ namespace Google.ProtocolBuffers {
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
int GetRepeatedFieldCount(FieldDescriptor field);
/// <summary>
/// Allows getting and setting of a repeated field value.
/// <see cref="IMessage{T}.Item(Descriptors.FieldDescriptor, int)"/>
/// <see cref="IMessage{T}.Item(FieldDescriptor, int)"/>
/// </summary>
object this[Descriptors.FieldDescriptor field, int index] { get; set; }
object this[FieldDescriptor field, int index] { get; set; }
/// <summary>
/// <see cref="IMessage{T}.HasField"/>
/// </summary>
bool HasField(Descriptors.FieldDescriptor field);
bool HasField(FieldDescriptor field);
/// <summary>
/// <see cref="IMessage{T}.UnknownFields"/>
/// </summary>
UnknownFieldSet UnknownFields { get; set; }
#region Non-generic versions of generic methods in IBuilder<T>
IBuilder Clear();
IBuilder MergeFrom(IMessage other);
IMessage Build();
IMessage BuildPartial();
IBuilder Clone();
IBuilder MergeFrom(CodedInputStream input);
IBuilder MergeFrom(CodedInputStream codedInputStream, ExtensionRegistry extensionRegistry);
IMessage DefaultInstanceForType { get; }
IBuilder NewBuilderForField<TField>(FieldDescriptor field);
IBuilder ClearField(FieldDescriptor field);
IBuilder AddRepeatedField(FieldDescriptor field, object value);
IBuilder MergeUnknownFields(UnknownFieldSet unknownFields);
IBuilder MergeFrom(ByteString data);
IBuilder MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
IBuilder MergeFrom(byte[] data);
IBuilder MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
IBuilder MergeFrom(Stream input);
IBuilder MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
#endregion
}
/// <summary>
......@@ -85,7 +110,7 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Resets all fields to their default values.
/// </summary>
IBuilder<T> Clear();
new IBuilder<T> Clear();
/// <summary>
/// Merge the specified other message into the message being
......@@ -111,20 +136,20 @@ namespace Google.ProtocolBuffers {
/// <exception cref="UninitializedMessageException">the message
/// is missing one or more required fields; use BuildPartial to bypass
/// this check</exception>
IMessage<T> Build();
new IMessage<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>
IMessage<T> BuildPartial();
new IMessage<T> BuildPartial();
/// <summary>
/// Clones this builder.
/// TODO(jonskeet): Explain depth of clone.
/// </summary>
IBuilder<T> Clone();
new IBuilder<T> Clone();
/// <summary>
/// Parses a message of this type from the input and merges it with this
......@@ -145,7 +170,7 @@ namespace Google.ProtocolBuffers {
/// Use BuildPartial to build, which ignores missing required fields.
/// </list>
/// </remarks>
IBuilder<T> MergeFrom(CodedInputStream input);
new IBuilder<T> MergeFrom(CodedInputStream input);
/// <summary>
/// Like MergeFrom(CodedInputStream), but also parses extensions.
......@@ -159,17 +184,14 @@ namespace Google.ProtocolBuffers {
/// Get's the message's type's default instance.
/// <see cref="IMessage{T}.DefaultInstanceForType" />
/// </summary>
IMessage<T> DefaultInstanceForType { get; }
new IMessage<T> DefaultInstanceForType { get; }
/// <summary>
/// Create a builder for messages of the appropriate type for the given field.
/// Messages built with this can then be passed to the various mutation properties
/// and methods.
/// </summary>
/// <typeparam name="TField"></typeparam>
/// <param name="field"></param>
/// <returns></returns>
IBuilder<TField> NewBuilderForField<TField>(Descriptors.FieldDescriptor field) where TField : IMessage<TField>;
new IBuilder<TField> NewBuilderForField<TField>(FieldDescriptor field) where TField : IMessage<TField>;
/// <summary>
/// Clears the field. This is exactly equivalent to calling the generated
......@@ -177,7 +199,7 @@ namespace Google.ProtocolBuffers {
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
IBuilder<T> ClearField(Descriptors.FieldDescriptor field);
new IBuilder<T> ClearField(FieldDescriptor field);
/// <summary>
/// Appends the given value as a new element for the specified repeated field.
......@@ -186,51 +208,45 @@ namespace Google.ProtocolBuffers {
/// the field does not belong to this builder's type, or the value is
/// of the incorrect type
/// </exception>
IBuilder<T> AddRepeatedField(Descriptors.FieldDescriptor field, object value);
/// <summary>
/// <see cref="IMessage{T}.UnknownFields"/>
/// </summary>
UnknownFieldSet UnknownFields { get; set; }
new IBuilder<T> AddRepeatedField(FieldDescriptor field, object value);
/// <summary>
/// Merge some unknown fields into the set for this message.
/// </summary>
IBuilder<T> MergeUnknownFields(UnknownFieldSet unknownFields);
new IBuilder<T> MergeUnknownFields(UnknownFieldSet unknownFields);
#region Convenience methods
// TODO(jonskeet): Implement these as extension methods?
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
/// </summary>
IBuilder<T> MergeFrom(ByteString data);
new IBuilder<T> MergeFrom(ByteString data);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
new IBuilder<T> MergeFrom(ByteString data, ExtensionRegistry extensionRegistry);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream).
/// </summary>
IBuilder<T> MergeFrom(byte[] data);
new IBuilder<T> MergeFrom(byte[] data);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
new IBuilder<T> MergeFrom(byte[] data, ExtensionRegistry extensionRegistry);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// Parse <paramref name="input"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream). Note that this method always reads
/// the entire input (unless it throws an exception). If you want it to
......@@ -238,14 +254,14 @@ namespace Google.ProtocolBuffers {
/// stream which limits reading. Despite usually reading the entire
/// stream, this method never closes the stream.
/// </summary>
IBuilder<T> MergeFrom(Stream input);
new IBuilder<T> MergeFrom(Stream input);
/// <summary>
/// Parse <paramref name="data"/> as a message of this type and merge
/// Parse <paramref name="input"/> as a message of this type and merge
/// it with the message being built. This is just a small wrapper around
/// MergeFrom(CodedInputStream, ExtensionRegistry).
/// </summary>
IBuilder<T> MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
new IBuilder<T> MergeFrom(Stream input, ExtensionRegistry extensionRegistry);
#endregion
}
}
......@@ -41,11 +41,11 @@ namespace Google.ProtocolBuffers {
/// field is set iff HasField() returns true for that field. A "repeated"
/// field is set iff GetRepeatedFieldSize() is greater than zero. The
/// values are exactly what would be returned by calling
/// GetField(Descriptors.FieldDescriptor) for each field. The map
/// GetField(FieldDescriptor) for each field. The map
/// is guaranteed to be a sorted map, so iterating over it will return fields
/// in order by field number.
/// </summary>
IDictionary<Descriptors.FieldDescriptor, object> AllFields { get; }
IDictionary<FieldDescriptor, object> AllFields { get; }
/// <summary>
/// Returns true if the given field is set. This is exactly equivalent
......@@ -53,7 +53,7 @@ namespace Google.ProtocolBuffers {
/// </summary>
/// <exception cref="ArgumentException">the field is a repeated field,
/// or it's not a field of this type</exception>
bool HasField(Descriptors.FieldDescriptor field);
bool HasField(FieldDescriptor field);
/// <summary>
/// Obtains the value of the given field, or the default value if
......@@ -61,7 +61,7 @@ namespace Google.ProtocolBuffers {
/// value is returned. For embedded message fields, the sub-message
/// is returned. For repeated fields, an IList&lt;T&gt; is returned.
/// </summary>
object this[Descriptors.FieldDescriptor field] { get; }
object this[FieldDescriptor field] { get; }
/// <summary>
/// Returns the number of elements of a repeated field. This is
......@@ -70,7 +70,7 @@ namespace Google.ProtocolBuffers {
/// </summary>
/// <exception cref="ArgumentException">the field is not a repeated field,
/// or it's not a field of this type</exception>
int GetRepeatedFieldCount(Descriptors.FieldDescriptor field);
int GetRepeatedFieldCount(FieldDescriptor field);
/// <summary>
/// Gets an element of a repeated field. For value type fields
......@@ -81,7 +81,7 @@ namespace Google.ProtocolBuffers {
/// or it's not a field of this type</exception>
/// <exception cref="ArgumentOutOfRangeException">the index is out of
/// range for the repeated field's value</exception>
object this[Descriptors.FieldDescriptor field, int index] { get; }
object this[FieldDescriptor field, int index] { get; }
/// <summary>
/// Returns the unknown fields for this message.
......
......@@ -36,23 +36,34 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AbstractBuilder.cs" />
<Compile Include="AbstractMessage.cs" />
<Compile Include="Autogenerated.cs" />
<Compile Include="ByteString.cs" />
<Compile Include="CodedInputStream.cs" />
<Compile Include="CodedOutputStream.cs" />
<Compile Include="Collections\Lists.cs" />
<Compile Include="Descriptors\EnumValueDescriptor.cs" />
<Compile Include="Descriptors\FieldDescriptor.cs" />
<Compile Include="Descriptors\FieldType.cs" />
<Compile Include="Descriptors\MappedType.cs" />
<Compile Include="Descriptors\MessageDescriptor.cs" />
<Compile Include="ExtensionInfo.cs" />
<Compile Include="ExtensionRegistry.cs" />
<Compile Include="FieldAccess\Delegates.cs" />
<Compile Include="FieldAccess\IFieldAccessor.cs" />
<Compile Include="FieldAccess\FieldAccessorTable.cs" />
<Compile Include="FieldAccess\SingularFieldAccessor.cs" />
<Compile Include="FieldSet.cs" />
<Compile Include="GeneratedExtension.cs" />
<Compile Include="GeneratedMessage.cs" />
<Compile Include="IBuilder.cs" />
<Compile Include="IMessage.cs" />
<Compile Include="InvalidProtocolBufferException.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TextFormat.cs" />
<Compile Include="UninitializedMessageException.cs" />
<Compile Include="UnknownField.cs" />
<Compile Include="UnknownFieldSet.cs" />
<Compile Include="WireFormat.cs" />
</ItemGroup>
......
......@@ -7,5 +7,9 @@ namespace Google.ProtocolBuffers {
public static string PrintToString(IMessage message) {
throw new NotImplementedException();
}
internal static string PrintToString(UnknownFieldSet unknownFieldSet) {
throw new NotImplementedException();
}
}
}
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Google.ProtocolBuffers.Collections;
namespace Google.ProtocolBuffers {
/// <summary>
/// Represents a single field in an UnknownFieldSet.
///
/// An UnknownField consists of five lists of values. The lists correspond
/// to the five "wire types" used in the protocol buffer binary format.
/// The wire type of each field can be determined from the encoded form alone,
/// without knowing the field's declared type. So, we are able to parse
/// unknown values at least this far and separate them. Normally, only one
/// of the five lists will contain any values, since it is impossible to
/// define a valid message type that declares two different types for the
/// same field number. However, the code is designed to allow for the case
/// where the same unknown field number is encountered using multiple different
/// wire types.
///
/// UnknownField is an immutable class. To construct one, you must use an
/// UnknownField.Builder.
/// </summary>
public sealed class UnknownField {
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<ByteString> lengthDelimitedList;
private readonly ReadOnlyCollection<UnknownFieldSet> groupList;
private UnknownField(ReadOnlyCollection<ulong> varintList,
ReadOnlyCollection<int> fixed32List,
ReadOnlyCollection<long> fixed64List,
ReadOnlyCollection<ByteString> lengthDelimitedList,
ReadOnlyCollection<UnknownFieldSet> groupList) {
this.varintList = varintList;
this.fixed32List = fixed32List;
this.fixed64List = fixed64List;
this.lengthDelimitedList = lengthDelimitedList;
this.groupList = groupList;
}
public static UnknownField DefaultInstance {
get { return defaultInstance; }
}
/// <summary>
/// The list of varint values for this field.
/// </summary>
public IList<ulong> VarintList {
get { return varintList; }
}
/// <summary>
/// The list of fixed32 values for this field.
/// </summary>
public IList<int> Fixed32List {
get { return fixed32List; }
}
/// <summary>
/// The list of fixed64 values for this field.
/// </summary>
public IList<long> Fixed64List {
get { return fixed64List; }
}
/// <summary>
/// The list of length-delimited values for this field.
/// </summary>
public IList<ByteString> LengthDelimitedList {
get { return lengthDelimitedList; }
}
/// <summary>
/// The list of embedded group values for this field. These
/// are represented using UnknownFieldSets rather than Messages
/// since the group's type is presumably unknown.
/// </summary>
public IList<UnknownFieldSet> GroupList {
get { return groupList; }
}
/// <summary>
/// Constructs a new Builder.
/// </summary>
public static Builder CreateBuilder() {
return new Builder();
}
/// <summary>
/// Constructs a new Builder and initializes it to a copy of <paramref name="copyFrom"/>.
/// </summary>
public static Builder CreateBuilder(UnknownField copyFrom) {
return new Builder().MergeFrom(copyFrom);
}
/// <summary>
/// Serializes the field, including the field number, and writes it to
/// <paramref name="output"/>.
/// </summary>
public void WriteTo(int fieldNumber, CodedOutputStream output) {
foreach (ulong value in varintList) {
output.WriteUInt64(fieldNumber, value);
}
foreach (int value in fixed32List) {
output.WriteFixed32(fieldNumber, value);
}
foreach (long value in fixed64List) {
output.WriteFixed64(fieldNumber, value);
}
foreach (ByteString value in lengthDelimitedList) {
output.WriteBytes(fieldNumber, value);
}
foreach (UnknownFieldSet value in groupList) {
output.WriteUnknownGroup(fieldNumber, value);
}
}
/// <summary>
/// Computes the number of bytes required to encode this field, including field
/// number.
/// </summary>
public int GetSerializedSize(int fieldNumber) {
int result = 0;
foreach (ulong value in varintList) {
result += CodedOutputStream.ComputeUInt64Size(fieldNumber, value);
}
foreach (int value in fixed32List) {
result += CodedOutputStream.ComputeFixed32Size(fieldNumber, value);
}
foreach (long value in fixed64List) {
result += CodedOutputStream.ComputeFixed64Size(fieldNumber, value);
}
foreach (ByteString value in lengthDelimitedList) {
result += CodedOutputStream.ComputeBytesSize(fieldNumber, value);
}
foreach (UnknownFieldSet value in groupList) {
result += CodedOutputStream.ComputeUnknownGroupSize(fieldNumber, value);
}
return result;
}
/// <summary>
/// Serializes the length-delimited values of the field, including field
/// number, and writes them to <paramref name="output"/> using the MessageSet wire format.
/// </summary>
/// <param name="fieldNumber"></param>
/// <param name="output"></param>
public void WriteAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) {
foreach (ByteString value in lengthDelimitedList) {
output.WriteRawMessageSetExtension(fieldNumber, value);
}
}
/// <summary>
/// Get the number of bytes required to encode this field, incuding field number,
/// using the MessageSet wire format.
/// </summary>
public int GetSerializedSizeAsMessageSetExtension(int fieldNumber) {
int result = 0;
foreach (ByteString value in lengthDelimitedList) {
result += CodedOutputStream.ComputeRawMessageSetExtensionSize(fieldNumber, value);
}
return result;
}
/// <summary>
/// Used to build instances of UnknownField.
/// </summary>
public class Builder {
private List<ulong> varintList;
private List<int> fixed32List;
private List<long> fixed64List;
private List<ByteString> lengthDelimitedList;
private List<UnknownFieldSet> groupList;
/// <summary>
/// Builds the field. After building, the builder is reset to an empty
/// state. (This is actually easier than making it unusable.)
/// </summary>
public UnknownField Build() {
return new UnknownField(MakeReadOnly(ref varintList),
MakeReadOnly(ref fixed32List),
MakeReadOnly(ref fixed64List),
MakeReadOnly(ref lengthDelimitedList),
MakeReadOnly(ref groupList));
}
/// <summary>
/// Merge the values in <paramref name="other" /> into this field. For each list
/// of values, <paramref name="other"/>'s values are append to the ones in this
/// field.
/// </summary>
public Builder MergeFrom(UnknownField other) {
varintList = AddAll(varintList, other.VarintList);
fixed32List = AddAll(fixed32List, other.Fixed32List);
fixed64List = AddAll(fixed64List, other.Fixed64List);
lengthDelimitedList = AddAll(lengthDelimitedList, other.LengthDelimitedList);
groupList = AddAll(groupList, other.GroupList);
return this;
}
/// <summary>
/// Returns a new list containing all of the given specified values from
/// both the <paramref name="current"/> and <paramref name="extras"/> lists.
/// If <paramref name="current" /> is null and <paramref name="extras"/> is empty,
/// null is returned. Otherwise, either a new list is created (if <paramref name="current" />
/// is null) or the elements of <paramref name="extras"/> are added to <paramref name="current" />.
/// </summary>
private static List<T> AddAll<T>(List<T> current, IList<T> extras)
{
if (extras.Count == 0) {
return current;
}
if (current == null) {
current = new List<T>(extras);
} else {
current.AddRange(extras);
}
return current;
}
/// <summary>
/// Clears the contents of this builder.
/// </summary>
public Builder Clear() {
varintList = null;
fixed32List = null;
fixed64List = null;
lengthDelimitedList = null;
groupList = null;
return this;
}
/// <summary>
/// Adds a varint value.
/// </summary>
public Builder AddVarint(ulong value) {
varintList = Add(varintList, value);
return this;
}
/// <summary>
/// Adds a fixed32 value.
/// </summary>
public Builder AddFixed32(int value) {
fixed32List = Add(fixed32List, value);
return this;
}
/// <summary>
/// Adds a fixed64 value.
/// </summary>
public Builder AddFixed64(long value) {
fixed64List = Add(fixed64List, value);
return this;
}
/// <summary>
/// Adds a length-delimited value.
/// </summary>
public Builder AddLengthDelimited(ByteString value) {
lengthDelimitedList = Add(lengthDelimitedList, value);
return this;
}
/// <summary>
/// Adds an embedded group.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public Builder AddGroup(UnknownFieldSet value) {
groupList = Add(groupList, value);
return this;
}
/// <summary>
/// Adds <paramref name="value"/> to the <paramref name="list"/>, creating
/// a new list if <paramref name="list"/> is null. The list is returned - either
/// the original reference or the new list.
/// </summary>
private static List<T> Add<T>(List<T> list, T value) {
if (list == null) {
list = new List<T>();
}
list.Add(value);
return list;
}
/// <summary>
/// Returns a read-only version of the given IList, and clears
/// the field used for <paramref name="list"/>. If the value
/// is null, an empty list is produced using Lists.Empty.
/// </summary>
/// <returns></returns>
private static ReadOnlyCollection<T> MakeReadOnly<T>(ref List<T> list) {
ReadOnlyCollection<T> ret = list == null ? Lists<T>.Empty : new ReadOnlyCollection<T>(list);
list = null;
return ret;
}
}
}
}
......@@ -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();
}
}
}
......@@ -43,8 +43,8 @@ namespace Google.ProtocolBuffers {
/// <summary>
/// Given a tag value, determines the field number (the upper 29 bits).
/// </summary>
public static uint GetTagFieldNumber(uint tag) {
return tag >> TagTypeBits;
public static int GetTagFieldNumber(uint tag) {
return (int) tag >> TagTypeBits;
}
/// <summary>
......
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