Prohibit null values in map fields

On deserialization, missing values for message types
are replaced with a "default" message.
This commit is contained in:
Jon Skeet 2016-01-04 14:03:01 +00:00
parent 5700a1054b
commit 9e4f354f14
14 changed files with 168 additions and 176 deletions

View File

@ -56,7 +56,7 @@ namespace Google.Protobuf.Collections
}
[Test]
public void NullValues()
public void NullValuesProhibited()
{
TestNullValues<int?>(0);
TestNullValues("");
@ -65,19 +65,12 @@ namespace Google.Protobuf.Collections
private void TestNullValues<T>(T nonNullValue)
{
var map = new MapField<int, T>(false);
var map = new MapField<int, T>();
var nullValue = (T) (object) null;
Assert.Throws<ArgumentNullException>(() => map.Add(0, nullValue));
Assert.Throws<ArgumentNullException>(() => map[0] = nullValue);
map.Add(1, nonNullValue);
map[1] = nonNullValue;
// Doesn't throw...
map = new MapField<int, T>(true);
map.Add(0, nullValue);
map[0] = nullValue;
map.Add(1, nonNullValue);
map[1] = nonNullValue;
}
[Test]
@ -160,27 +153,6 @@ namespace Google.Protobuf.Collections
EqualityTester.AssertInequality(map1, map2);
}
[Test]
public void EqualityHandlesNullValues()
{
var map1 = new MapField<string, ForeignMessage>();
map1.Add("a", new ForeignMessage { C = 10 });
map1.Add("b", null);
var map2 = new MapField<string, ForeignMessage>();
map2.Add("a", new ForeignMessage { C = 10 });
map2.Add("b", null);
EqualityTester.AssertEquality(map1, map2);
// Check the null value isn't ignored entirely...
Assert.IsTrue(map1.Remove("b"));
EqualityTester.AssertInequality(map1, map2);
map1.Add("b", new ForeignMessage());
EqualityTester.AssertInequality(map1, map2);
map1["b"] = null;
EqualityTester.AssertEquality(map1, map2);
}
[Test]
public void Add_Dictionary()
{
@ -453,30 +425,6 @@ namespace Google.Protobuf.Collections
Assert.Throws<ArgumentNullException>(() => dictionary["x"] = null);
}
[Test]
public void AllowNullValues_Property()
{
// Non-message reference type values are non-nullable by default, but can be overridden
Assert.IsFalse(new MapField<int, string>().AllowsNullValues);
Assert.IsFalse(new MapField<int, string>(false).AllowsNullValues);
Assert.IsTrue(new MapField<int, string>(true).AllowsNullValues);
// Non-nullable value type values are never nullable
Assert.IsFalse(new MapField<int, int>().AllowsNullValues);
Assert.IsFalse(new MapField<int, int>(false).AllowsNullValues);
Assert.Throws<ArgumentException>(() => new MapField<int, int>(true));
// Message type values are nullable by default, but can be overridden
Assert.IsTrue(new MapField<int, TestAllTypes>().AllowsNullValues);
Assert.IsFalse(new MapField<int, TestAllTypes>(false).AllowsNullValues);
Assert.IsTrue(new MapField<int, TestAllTypes>(true).AllowsNullValues);
// Nullable value type values are nullable by default, but can be overridden
Assert.IsTrue(new MapField<int, int?>().AllowsNullValues);
Assert.IsFalse(new MapField<int, int?>(false).AllowsNullValues);
Assert.IsTrue(new MapField<int, int?>(true).AllowsNullValues);
}
[Test]
public void KeysReturnsLiveView()
{

View File

@ -221,7 +221,7 @@ namespace Google.Protobuf
},
MapInt32ForeignMessage = {
{ 0, new ForeignMessage { C = 10 } },
{ 5, null },
{ 5, new ForeignMessage() },
},
MapInt32Enum = {
{ 1, MapEnum.MAP_ENUM_BAR },
@ -268,6 +268,40 @@ namespace Google.Protobuf
Assert.AreEqual(nestedMessage, parsed.MapInt32ForeignMessage[0]);
}
[Test]
public void MapWithOnlyKey_PrimitiveValue()
{
// Hand-craft the stream to contain a single entry with just a key.
var memoryStream = new MemoryStream();
var output = new CodedOutputStream(memoryStream);
output.WriteTag(TestMap.MapInt32DoubleFieldNumber, WireFormat.WireType.LengthDelimited);
int key = 10;
output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(0.0, parsed.MapInt32Double[key]);
}
[Test]
public void MapWithOnlyKey_MessageValue()
{
// Hand-craft the stream to contain a single entry with just a key.
var memoryStream = new MemoryStream();
var output = new CodedOutputStream(memoryStream);
output.WriteTag(TestMap.MapInt32ForeignMessageFieldNumber, WireFormat.WireType.LengthDelimited);
int key = 10;
output.WriteLength(1 + CodedOutputStream.ComputeInt32Size(key));
output.WriteTag(1, WireFormat.WireType.Varint);
output.WriteInt32(key);
output.Flush();
var parsed = TestMap.Parser.ParseFrom(memoryStream.ToArray());
Assert.AreEqual(new ForeignMessage(), parsed.MapInt32ForeignMessage[key]);
}
[Test]
public void MapIgnoresExtraFieldsWithinEntryMessages()
{

View File

@ -195,13 +195,6 @@ namespace Google.Protobuf
AssertJson("{ 'repeatedForeignEnum': [ ] }", JsonFormatter.Default.Format(message));
}
[Test]
public void NullValueForMessage()
{
var message = new TestMap { MapInt32ForeignMessage = { { 10, null } } };
AssertJson("{ 'mapInt32ForeignMessage': { '10': null } }", JsonFormatter.Default.Format(message));
}
[Test]
[TestCase("a\u17b4b", "a\\u17b4b")] // Explicit
[TestCase("a\u0601b", "a\\u0601b")] // Ranged

View File

@ -2080,7 +2080,7 @@ namespace Google.Protobuf.TestProtos {
public const int DoubleFieldFieldNumber = 10;
private static readonly pbc::MapField<int, double?>.Codec _map_doubleField_codec
= new pbc::MapField<int, double?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<double>(18), 82);
private readonly pbc::MapField<int, double?> doubleField_ = new pbc::MapField<int, double?>(true);
private readonly pbc::MapField<int, double?> doubleField_ = new pbc::MapField<int, double?>();
public pbc::MapField<int, double?> DoubleField {
get { return doubleField_; }
}
@ -2089,7 +2089,7 @@ namespace Google.Protobuf.TestProtos {
public const int FloatFieldFieldNumber = 11;
private static readonly pbc::MapField<int, float?>.Codec _map_floatField_codec
= new pbc::MapField<int, float?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<float>(18), 90);
private readonly pbc::MapField<int, float?> floatField_ = new pbc::MapField<int, float?>(true);
private readonly pbc::MapField<int, float?> floatField_ = new pbc::MapField<int, float?>();
public pbc::MapField<int, float?> FloatField {
get { return floatField_; }
}
@ -2098,7 +2098,7 @@ namespace Google.Protobuf.TestProtos {
public const int Int64FieldFieldNumber = 12;
private static readonly pbc::MapField<int, long?>.Codec _map_int64Field_codec
= new pbc::MapField<int, long?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<long>(18), 98);
private readonly pbc::MapField<int, long?> int64Field_ = new pbc::MapField<int, long?>(true);
private readonly pbc::MapField<int, long?> int64Field_ = new pbc::MapField<int, long?>();
public pbc::MapField<int, long?> Int64Field {
get { return int64Field_; }
}
@ -2107,7 +2107,7 @@ namespace Google.Protobuf.TestProtos {
public const int Uint64FieldFieldNumber = 13;
private static readonly pbc::MapField<int, ulong?>.Codec _map_uint64Field_codec
= new pbc::MapField<int, ulong?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<ulong>(18), 106);
private readonly pbc::MapField<int, ulong?> uint64Field_ = new pbc::MapField<int, ulong?>(true);
private readonly pbc::MapField<int, ulong?> uint64Field_ = new pbc::MapField<int, ulong?>();
public pbc::MapField<int, ulong?> Uint64Field {
get { return uint64Field_; }
}
@ -2116,7 +2116,7 @@ namespace Google.Protobuf.TestProtos {
public const int Int32FieldFieldNumber = 14;
private static readonly pbc::MapField<int, int?>.Codec _map_int32Field_codec
= new pbc::MapField<int, int?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<int>(18), 114);
private readonly pbc::MapField<int, int?> int32Field_ = new pbc::MapField<int, int?>(true);
private readonly pbc::MapField<int, int?> int32Field_ = new pbc::MapField<int, int?>();
public pbc::MapField<int, int?> Int32Field {
get { return int32Field_; }
}
@ -2125,7 +2125,7 @@ namespace Google.Protobuf.TestProtos {
public const int Uint32FieldFieldNumber = 15;
private static readonly pbc::MapField<int, uint?>.Codec _map_uint32Field_codec
= new pbc::MapField<int, uint?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<uint>(18), 122);
private readonly pbc::MapField<int, uint?> uint32Field_ = new pbc::MapField<int, uint?>(true);
private readonly pbc::MapField<int, uint?> uint32Field_ = new pbc::MapField<int, uint?>();
public pbc::MapField<int, uint?> Uint32Field {
get { return uint32Field_; }
}
@ -2134,7 +2134,7 @@ namespace Google.Protobuf.TestProtos {
public const int BoolFieldFieldNumber = 16;
private static readonly pbc::MapField<int, bool?>.Codec _map_boolField_codec
= new pbc::MapField<int, bool?>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForStructWrapper<bool>(18), 130);
private readonly pbc::MapField<int, bool?> boolField_ = new pbc::MapField<int, bool?>(true);
private readonly pbc::MapField<int, bool?> boolField_ = new pbc::MapField<int, bool?>();
public pbc::MapField<int, bool?> BoolField {
get { return boolField_; }
}
@ -2143,7 +2143,7 @@ namespace Google.Protobuf.TestProtos {
public const int StringFieldFieldNumber = 17;
private static readonly pbc::MapField<int, string>.Codec _map_stringField_codec
= new pbc::MapField<int, string>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<string>(18), 138);
private readonly pbc::MapField<int, string> stringField_ = new pbc::MapField<int, string>(true);
private readonly pbc::MapField<int, string> stringField_ = new pbc::MapField<int, string>();
public pbc::MapField<int, string> StringField {
get { return stringField_; }
}
@ -2152,7 +2152,7 @@ namespace Google.Protobuf.TestProtos {
public const int BytesFieldFieldNumber = 18;
private static readonly pbc::MapField<int, pb::ByteString>.Codec _map_bytesField_codec
= new pbc::MapField<int, pb::ByteString>.Codec(pb::FieldCodec.ForInt32(8), pb::FieldCodec.ForClassWrapper<pb::ByteString>(18), 146);
private readonly pbc::MapField<int, pb::ByteString> bytesField_ = new pbc::MapField<int, pb::ByteString>(true);
private readonly pbc::MapField<int, pb::ByteString> bytesField_ = new pbc::MapField<int, pb::ByteString>();
public pbc::MapField<int, pb::ByteString> BytesField {
get { return bytesField_; }
}

View File

@ -151,6 +151,8 @@ namespace Google.Protobuf.WellKnownTypes
[Test]
public void MapWrappersSerializeDeserialize()
{
// Note: no null values here, as they are prohibited in map fields
// (despite being representable).
var message = new MapWellKnownTypes
{
BoolField = { { 10, false }, { 20, true } },
@ -158,13 +160,12 @@ namespace Google.Protobuf.WellKnownTypes
{ -1, ByteString.CopyFrom(1, 2, 3) },
{ 10, ByteString.CopyFrom(4, 5, 6) },
{ 1000, ByteString.Empty },
{ 10000, null }
},
DoubleField = { { 1, 12.5 }, { 10, -1.5 }, { 20, 0d } },
FloatField = { { 2, 123.25f }, { 3, -20f }, { 4, 0f } },
Int32Field = { { 5, int.MaxValue }, { 6, int.MinValue }, { 7, 0 } },
Int64Field = { { 8, long.MaxValue }, { 9, long.MinValue }, { 10, 0L } },
StringField = { { 11, "First" }, { 12, "Second" }, { 13, "" }, { 14, null } },
StringField = { { 11, "First" }, { 12, "Second" }, { 13, "" } },
Uint32Field = { { 15, uint.MaxValue }, { 16, uint.MinValue }, { 17, 0U } },
Uint64Field = { { 18, ulong.MaxValue }, { 19, ulong.MinValue }, { 20, 0UL } },
};
@ -224,13 +225,11 @@ namespace Google.Protobuf.WellKnownTypes
[Test]
public void Reflection_MapFields()
{
// Just a single example... note that we can't have a null value here
var message = new MapWellKnownTypes { Int32Field = { { 1, 2 }, { 3, null } } };
// Just a single example... note that we can't have a null value here despite the value type being int?
var message = new MapWellKnownTypes { Int32Field = { { 1, 2 } } };
var fields = MapWellKnownTypes.Descriptor.Fields;
var dictionary = (IDictionary) fields[MapWellKnownTypes.Int32FieldFieldNumber].Accessor.GetValue(message);
Assert.AreEqual(2, dictionary[1]);
Assert.IsNull(dictionary[3]);
Assert.IsTrue(dictionary.Contains(3));
}
[Test]

View File

@ -53,6 +53,13 @@ namespace Google.Protobuf.Collections
/// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal" />.
/// </para>
/// <para>
/// Null values are not permitted in the map, either for wrapper types or regular messages.
/// If a map is deserialized from a data stream and the value is missing from an entry, a default value
/// is created instead. For primitive types, that is the regular default value (0, the empty string and so
/// on); for message types, an empty instance of the message is created, as if the map entry contained a 0-length
/// encoded value for the field.
/// </para>
/// <para>
/// This implementation does not generally prohibit the use of key/value types which are not
/// supported by Protocol Buffers (e.g. using a key type of <code>byte</code>) but nor does it guarantee
/// that all operations will work in such cases.
@ -61,34 +68,10 @@ namespace Google.Protobuf.Collections
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary
{
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private readonly bool allowNullValues;
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();
private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<KeyValuePair<TKey, TValue>>();
/// <summary>
/// Constructs a new map field, defaulting the value nullability to only allow null values for message types
/// and non-nullable value types.
/// </summary>
public MapField() : this(typeof(IMessage).IsAssignableFrom(typeof(TValue)) || Nullable.GetUnderlyingType(typeof(TValue)) != null)
{
}
/// <summary>
/// Constructs a new map field, overriding the choice of whether null values are permitted in the map.
/// This is used by wrapper types, where maps with string and bytes wrappers as the value types
/// support null values.
/// </summary>
/// <param name="allowNullValues">Whether null values are permitted in the map or not.</param>
public MapField(bool allowNullValues)
{
if (allowNullValues && typeof(TValue).IsValueType() && Nullable.GetUnderlyingType(typeof(TValue)) == null)
{
throw new ArgumentException("allowNullValues", "Non-nullable value types do not support null values");
}
this.allowNullValues = allowNullValues;
}
/// <summary>
/// Creates a deep clone of this object.
/// </summary>
@ -97,13 +80,13 @@ namespace Google.Protobuf.Collections
/// </returns>
public MapField<TKey, TValue> Clone()
{
var clone = new MapField<TKey, TValue>(allowNullValues);
var clone = new MapField<TKey, TValue>();
// Keys are never cloneable. Values might be.
if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
{
foreach (var pair in list)
{
clone.Add(pair.Key, pair.Value == null ? pair.Value : ((IDeepCloneable<TValue>)pair.Value).Clone());
clone.Add(pair.Key, ((IDeepCloneable<TValue>)pair.Value).Clone());
}
}
else
@ -217,7 +200,7 @@ namespace Google.Protobuf.Collections
{
Preconditions.CheckNotNullUnconstrained(key, "key");
// value == null check here is redundant, but avoids boxing.
if (value == null && !allowNullValues)
if (value == null)
{
Preconditions.CheckNotNullUnconstrained(value, "value");
}
@ -246,7 +229,7 @@ namespace Google.Protobuf.Collections
public ICollection<TValue> Values { get { return new MapView<TValue>(this, pair => pair.Value, ContainsValue); } }
/// <summary>
/// Adds the specified entries to the map.
/// Adds the specified entries to the map. The keys and values are not automatically cloned.
/// </summary>
/// <param name="entries">The entries to add to the map.</param>
public void Add(IDictionary<TKey, TValue> entries)
@ -346,11 +329,6 @@ namespace Google.Protobuf.Collections
}
}
/// <summary>
/// Returns whether or not this map allows values to be null.
/// </summary>
public bool AllowsNullValues { get { return allowNullValues; } }
/// <summary>
/// Gets the number of elements contained in the map.
/// </summary>
@ -632,6 +610,8 @@ namespace Google.Protobuf.Collections
/// </summary>
internal class MessageAdapter : IMessage
{
private static readonly byte[] ZeroLengthMessageStreamData = new byte[] { 0 };
private readonly Codec codec;
internal TKey Key { get; set; }
internal TValue Value { get; set; }
@ -665,6 +645,13 @@ namespace Google.Protobuf.Collections
input.SkipLastField();
}
}
// Corner case: a map entry with a key but no value, where the value type is a message.
// Read it as if we'd seen an input stream with no data (i.e. create a "default" message).
if (Value == null)
{
Value = codec.valueCodec.Read(new CodedInputStream(ZeroLengthMessageStreamData));
}
}
public void WriteTo(CodedOutputStream output)

View File

@ -61,8 +61,8 @@ namespace Google.Protobuf.WellKnownTypes {
///
/// If the embedded message type is well-known and has a custom JSON
/// representation, that representation will be embedded adding a field
/// `value` which holds the custom JSON in addition to the the `@type`
/// field. Example (for message [google.protobuf.Duration][google.protobuf.Duration]):
/// `value` which holds the custom JSON in addition to the `@type`
/// field. Example (for message [google.protobuf.Duration][]):
///
/// {
/// "@type": "type.googleapis.com/google.protobuf.Duration",
@ -110,7 +110,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// * If no schema is provided, `https` is assumed.
/// * The last segment of the URL's path must represent the fully
/// qualified name of the type (as in `path/google.protobuf.Duration`).
/// * An HTTP GET on the URL must yield a [google.protobuf.Type][google.protobuf.Type]
/// * An HTTP GET on the URL must yield a [google.protobuf.Type][]
/// value in binary format, or produce an error.
/// * Applications are allowed to cache lookup results based on the
/// URL, or have them precompiled into a binary to avoid any

View File

@ -653,7 +653,6 @@ namespace Google.Protobuf.WellKnownTypes {
///
/// package google.storage.v2;
/// service Storage {
/// // (-- see AccessControl.GetAcl --)
/// rpc GetAcl(GetAclRequest) returns (Acl);
///
/// // Get a data record.

View File

@ -24,9 +24,9 @@ namespace Google.Protobuf.WellKnownTypes {
byte[] descriptorData = global::System.Convert.FromBase64String(
string.Concat(
"Chtnb29nbGUvcHJvdG9idWYvZW1wdHkucHJvdG8SD2dvb2dsZS5wcm90b2J1",
"ZiIHCgVFbXB0eUJNChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv",
"UAGgAQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNi",
"BnByb3RvMw=="));
"ZiIHCgVFbXB0eUJQChNjb20uZ29vZ2xlLnByb3RvYnVmQgpFbXB0eVByb3Rv",
"UAGgAQH4AQGiAgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlw",
"ZXNiBnByb3RvMw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {

View File

@ -69,7 +69,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// z: 8
///
/// The result will not contain specific values for fields x,y and z
/// (there value will be set to the default, and omitted in proto text
/// (their value will be set to the default, and omitted in proto text
/// output):
///
/// f {

View File

@ -25,9 +25,9 @@ namespace Google.Protobuf.WellKnownTypes {
string.Concat(
"Ch9nb29nbGUvcHJvdG9idWYvdGltZXN0YW1wLnByb3RvEg9nb29nbGUucHJv",
"dG9idWYiKwoJVGltZXN0YW1wEg8KB3NlY29uZHMYASABKAMSDQoFbmFub3MY",
"AiABKAVCUQoTY29tLmdvb2dsZS5wcm90b2J1ZkIOVGltZXN0YW1wUHJvdG9Q",
"AaABAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBlc2IG",
"cHJvdG8z"));
"AiABKAVCVAoTY29tLmdvb2dsZS5wcm90b2J1ZkIOVGltZXN0YW1wUHJvdG9Q",
"AaABAfgBAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25vd25UeXBl",
"c2IGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {

View File

@ -30,38 +30,39 @@ namespace Google.Protobuf.WellKnownTypes {
"b2ZzGAMgAygJEigKB29wdGlvbnMYBCADKAsyFy5nb29nbGUucHJvdG9idWYu",
"T3B0aW9uEjYKDnNvdXJjZV9jb250ZXh0GAUgASgLMh4uZ29vZ2xlLnByb3Rv",
"YnVmLlNvdXJjZUNvbnRleHQSJwoGc3ludGF4GAYgASgOMhcuZ29vZ2xlLnBy",
"b3RvYnVmLlN5bnRheCK+BQoFRmllbGQSKQoEa2luZBgBIAEoDjIbLmdvb2ds",
"b3RvYnVmLlN5bnRheCLVBQoFRmllbGQSKQoEa2luZBgBIAEoDjIbLmdvb2ds",
"ZS5wcm90b2J1Zi5GaWVsZC5LaW5kEjcKC2NhcmRpbmFsaXR5GAIgASgOMiIu",
"Z29vZ2xlLnByb3RvYnVmLkZpZWxkLkNhcmRpbmFsaXR5Eg4KBm51bWJlchgD",
"IAEoBRIMCgRuYW1lGAQgASgJEhAKCHR5cGVfdXJsGAYgASgJEhMKC29uZW9m",
"X2luZGV4GAcgASgFEg4KBnBhY2tlZBgIIAEoCBIoCgdvcHRpb25zGAkgAygL",
"MhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhIRCglqc29uX25hbWUYCiABKAki",
"yAIKBEtpbmQSEAoMVFlQRV9VTktOT1dOEAASDwoLVFlQRV9ET1VCTEUQARIO",
"CgpUWVBFX0ZMT0FUEAISDgoKVFlQRV9JTlQ2NBADEg8KC1RZUEVfVUlOVDY0",
"EAQSDgoKVFlQRV9JTlQzMhAFEhAKDFRZUEVfRklYRUQ2NBAGEhAKDFRZUEVf",
"RklYRUQzMhAHEg0KCVRZUEVfQk9PTBAIEg8KC1RZUEVfU1RSSU5HEAkSDgoK",
"VFlQRV9HUk9VUBAKEhAKDFRZUEVfTUVTU0FHRRALEg4KClRZUEVfQllURVMQ",
"DBIPCgtUWVBFX1VJTlQzMhANEg0KCVRZUEVfRU5VTRAOEhEKDVRZUEVfU0ZJ",
"WEVEMzIQDxIRCg1UWVBFX1NGSVhFRDY0EBASDwoLVFlQRV9TSU5UMzIQERIP",
"CgtUWVBFX1NJTlQ2NBASInQKC0NhcmRpbmFsaXR5EhcKE0NBUkRJTkFMSVRZ",
"X1VOS05PV04QABIYChRDQVJESU5BTElUWV9PUFRJT05BTBABEhgKFENBUkRJ",
"TkFMSVRZX1JFUVVJUkVEEAISGAoUQ0FSRElOQUxJVFlfUkVQRUFURUQQAyLO",
"AQoERW51bRIMCgRuYW1lGAEgASgJEi0KCWVudW12YWx1ZRgCIAMoCzIaLmdv",
"b2dsZS5wcm90b2J1Zi5FbnVtVmFsdWUSKAoHb3B0aW9ucxgDIAMoCzIXLmdv",
"b2dsZS5wcm90b2J1Zi5PcHRpb24SNgoOc291cmNlX2NvbnRleHQYBCABKAsy",
"Hi5nb29nbGUucHJvdG9idWYuU291cmNlQ29udGV4dBInCgZzeW50YXgYBSAB",
"KA4yFy5nb29nbGUucHJvdG9idWYuU3ludGF4IlMKCUVudW1WYWx1ZRIMCgRu",
"YW1lGAEgASgJEg4KBm51bWJlchgCIAEoBRIoCgdvcHRpb25zGAMgAygLMhcu",
"Z29vZ2xlLnByb3RvYnVmLk9wdGlvbiI7CgZPcHRpb24SDAoEbmFtZRgBIAEo",
"CRIjCgV2YWx1ZRgCIAEoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnkqLgoGU3lu",
"dGF4EhEKDVNZTlRBWF9QUk9UTzIQABIRCg1TWU5UQVhfUFJPVE8zEAFCTAoT",
"Y29tLmdvb2dsZS5wcm90b2J1ZkIJVHlwZVByb3RvUAGgAQGiAgNHUEKqAh5H",
"b29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
"MhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhIRCglqc29uX25hbWUYCiABKAkS",
"FQoNZGVmYXVsdF92YWx1ZRgLIAEoCSLIAgoES2luZBIQCgxUWVBFX1VOS05P",
"V04QABIPCgtUWVBFX0RPVUJMRRABEg4KClRZUEVfRkxPQVQQAhIOCgpUWVBF",
"X0lOVDY0EAMSDwoLVFlQRV9VSU5UNjQQBBIOCgpUWVBFX0lOVDMyEAUSEAoM",
"VFlQRV9GSVhFRDY0EAYSEAoMVFlQRV9GSVhFRDMyEAcSDQoJVFlQRV9CT09M",
"EAgSDwoLVFlQRV9TVFJJTkcQCRIOCgpUWVBFX0dST1VQEAoSEAoMVFlQRV9N",
"RVNTQUdFEAsSDgoKVFlQRV9CWVRFUxAMEg8KC1RZUEVfVUlOVDMyEA0SDQoJ",
"VFlQRV9FTlVNEA4SEQoNVFlQRV9TRklYRUQzMhAPEhEKDVRZUEVfU0ZJWEVE",
"NjQQEBIPCgtUWVBFX1NJTlQzMhAREg8KC1RZUEVfU0lOVDY0EBIidAoLQ2Fy",
"ZGluYWxpdHkSFwoTQ0FSRElOQUxJVFlfVU5LTk9XThAAEhgKFENBUkRJTkFM",
"SVRZX09QVElPTkFMEAESGAoUQ0FSRElOQUxJVFlfUkVRVUlSRUQQAhIYChRD",
"QVJESU5BTElUWV9SRVBFQVRFRBADIs4BCgRFbnVtEgwKBG5hbWUYASABKAkS",
"LQoJZW51bXZhbHVlGAIgAygLMhouZ29vZ2xlLnByb3RvYnVmLkVudW1WYWx1",
"ZRIoCgdvcHRpb25zGAMgAygLMhcuZ29vZ2xlLnByb3RvYnVmLk9wdGlvbhI2",
"Cg5zb3VyY2VfY29udGV4dBgEIAEoCzIeLmdvb2dsZS5wcm90b2J1Zi5Tb3Vy",
"Y2VDb250ZXh0EicKBnN5bnRheBgFIAEoDjIXLmdvb2dsZS5wcm90b2J1Zi5T",
"eW50YXgiUwoJRW51bVZhbHVlEgwKBG5hbWUYASABKAkSDgoGbnVtYmVyGAIg",
"ASgFEigKB29wdGlvbnMYAyADKAsyFy5nb29nbGUucHJvdG9idWYuT3B0aW9u",
"IjsKBk9wdGlvbhIMCgRuYW1lGAEgASgJEiMKBXZhbHVlGAIgASgLMhQuZ29v",
"Z2xlLnByb3RvYnVmLkFueSouCgZTeW50YXgSEQoNU1lOVEFYX1BST1RPMhAA",
"EhEKDVNZTlRBWF9QUk9UTzMQAUJMChNjb20uZ29vZ2xlLnByb3RvYnVmQglU",
"eXBlUHJvdG9QAaABAaICA0dQQqoCHkdvb2dsZS5Qcm90b2J1Zi5XZWxsS25v",
"d25UeXBlc2IGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.AnyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.SourceContextReflection.Descriptor, },
new pbr::GeneratedCodeInfo(new[] {typeof(global::Google.Protobuf.WellKnownTypes.Syntax), }, new pbr::GeneratedCodeInfo[] {
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Type), global::Google.Protobuf.WellKnownTypes.Type.Parser, new[]{ "Name", "Fields", "Oneofs", "Options", "SourceContext", "Syntax" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Field), global::Google.Protobuf.WellKnownTypes.Field.Parser, new[]{ "Kind", "Cardinality", "Number", "Name", "TypeUrl", "OneofIndex", "Packed", "Options", "JsonName", "DefaultValue" }, null, new[]{ typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Kind), typeof(global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality) }, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Enum), global::Google.Protobuf.WellKnownTypes.Enum.Parser, new[]{ "Name", "Enumvalue", "Options", "SourceContext", "Syntax" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.EnumValue), global::Google.Protobuf.WellKnownTypes.EnumValue.Parser, new[]{ "Name", "Number", "Options" }, null, null, null),
new pbr::GeneratedCodeInfo(typeof(global::Google.Protobuf.WellKnownTypes.Option), global::Google.Protobuf.WellKnownTypes.Option.Parser, new[]{ "Name", "Value" }, null, null, null)
@ -72,15 +73,15 @@ namespace Google.Protobuf.WellKnownTypes {
}
#region Enums
/// <summary>
/// Syntax specifies the syntax in which a service element was defined.
/// The syntax in which a protocol buffer element is defined.
/// </summary>
public enum Syntax {
/// <summary>
/// Syntax "proto2"
/// Syntax `proto2`.
/// </summary>
SYNTAX_PROTO2 = 0,
/// <summary>
/// Syntax "proto3"
/// Syntax `proto3`.
/// </summary>
SYNTAX_PROTO3 = 1,
}
@ -89,7 +90,7 @@ namespace Google.Protobuf.WellKnownTypes {
#region Messages
/// <summary>
/// A light-weight descriptor for a proto message type.
/// A protocol buffer message type.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Type : pb::IMessage<Type> {
@ -154,7 +155,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForString(26);
private readonly pbc::RepeatedField<string> oneofs_ = new pbc::RepeatedField<string>();
/// <summary>
/// The list of oneof definitions.
/// The list of types appearing in `oneof` definitions in this type.
/// </summary>
public pbc::RepeatedField<string> Oneofs {
get { return oneofs_; }
@ -166,7 +167,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(34, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
/// The proto options.
/// The protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@ -330,7 +331,7 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
/// Field represents a single field of a message type.
/// A single field of a message type.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Field : pb::IMessage<Field> {
@ -361,6 +362,7 @@ namespace Google.Protobuf.WellKnownTypes {
packed_ = other.packed_;
options_ = other.options_.Clone();
jsonName_ = other.jsonName_;
defaultValue_ = other.defaultValue_;
}
public Field Clone() {
@ -371,7 +373,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int KindFieldNumber = 1;
private global::Google.Protobuf.WellKnownTypes.Field.Types.Kind kind_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Kind.TYPE_UNKNOWN;
/// <summary>
/// The field kind.
/// The field type.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Field.Types.Kind Kind {
get { return kind_; }
@ -384,7 +386,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int CardinalityFieldNumber = 2;
private global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality cardinality_ = global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality.CARDINALITY_UNKNOWN;
/// <summary>
/// The field cardinality, i.e. optional/required/repeated.
/// The field cardinality.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Field.Types.Cardinality Cardinality {
get { return cardinality_; }
@ -397,7 +399,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int NumberFieldNumber = 3;
private int number_;
/// <summary>
/// The proto field number.
/// The field number.
/// </summary>
public int Number {
get { return number_; }
@ -423,8 +425,8 @@ namespace Google.Protobuf.WellKnownTypes {
public const int TypeUrlFieldNumber = 6;
private string typeUrl_ = "";
/// <summary>
/// The type URL (without the scheme) when the type is MESSAGE or ENUM,
/// such as `type.googleapis.com/google.protobuf.Empty`.
/// The field type URL, without the scheme, for message or enumeration
/// types. Example: `"type.googleapis.com/google.protobuf.Timestamp"`.
/// </summary>
public string TypeUrl {
get { return typeUrl_; }
@ -437,7 +439,8 @@ namespace Google.Protobuf.WellKnownTypes {
public const int OneofIndexFieldNumber = 7;
private int oneofIndex_;
/// <summary>
/// Index in Type.oneofs. Starts at 1. Zero means no oneof mapping.
/// The index of the field type in `Type.oneofs`, for message or enumeration
/// types. The first type has index 1; zero means the type is not in the list.
/// </summary>
public int OneofIndex {
get { return oneofIndex_; }
@ -465,7 +468,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(74, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
/// The proto options.
/// The protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@ -475,7 +478,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int JsonNameFieldNumber = 10;
private string jsonName_ = "";
/// <summary>
/// The JSON name for this field.
/// The field JSON name.
/// </summary>
public string JsonName {
get { return jsonName_; }
@ -484,6 +487,19 @@ namespace Google.Protobuf.WellKnownTypes {
}
}
/// <summary>Field number for the "default_value" field.</summary>
public const int DefaultValueFieldNumber = 11;
private string defaultValue_ = "";
/// <summary>
/// The string value of the default value of this field. Proto2 syntax only.
/// </summary>
public string DefaultValue {
get { return defaultValue_; }
set {
defaultValue_ = pb::Preconditions.CheckNotNull(value, "value");
}
}
public override bool Equals(object other) {
return Equals(other as Field);
}
@ -504,6 +520,7 @@ namespace Google.Protobuf.WellKnownTypes {
if (Packed != other.Packed) return false;
if(!options_.Equals(other.options_)) return false;
if (JsonName != other.JsonName) return false;
if (DefaultValue != other.DefaultValue) return false;
return true;
}
@ -518,6 +535,7 @@ namespace Google.Protobuf.WellKnownTypes {
if (Packed != false) hash ^= Packed.GetHashCode();
hash ^= options_.GetHashCode();
if (JsonName.Length != 0) hash ^= JsonName.GetHashCode();
if (DefaultValue.Length != 0) hash ^= DefaultValue.GetHashCode();
return hash;
}
@ -559,6 +577,10 @@ namespace Google.Protobuf.WellKnownTypes {
output.WriteRawTag(82);
output.WriteString(JsonName);
}
if (DefaultValue.Length != 0) {
output.WriteRawTag(90);
output.WriteString(DefaultValue);
}
}
public int CalculateSize() {
@ -588,6 +610,9 @@ namespace Google.Protobuf.WellKnownTypes {
if (JsonName.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(JsonName);
}
if (DefaultValue.Length != 0) {
size += 1 + pb::CodedOutputStream.ComputeStringSize(DefaultValue);
}
return size;
}
@ -620,6 +645,9 @@ namespace Google.Protobuf.WellKnownTypes {
if (other.JsonName.Length != 0) {
JsonName = other.JsonName;
}
if (other.DefaultValue.Length != 0) {
DefaultValue = other.DefaultValue;
}
}
public void MergeFrom(pb::CodedInputStream input) {
@ -665,6 +693,10 @@ namespace Google.Protobuf.WellKnownTypes {
JsonName = input.ReadString();
break;
}
case 90: {
DefaultValue = input.ReadString();
break;
}
}
}
}
@ -674,7 +706,7 @@ namespace Google.Protobuf.WellKnownTypes {
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static partial class Types {
/// <summary>
/// Kind represents a basic field type.
/// Basic field types.
/// </summary>
public enum Kind {
/// <summary>
@ -718,7 +750,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// </summary>
TYPE_STRING = 9,
/// <summary>
/// Field type group (deprecated proto2 type)
/// Field type group. Proto2 syntax only, and deprecated.
/// </summary>
TYPE_GROUP = 10,
/// <summary>
@ -756,12 +788,11 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
/// Cardinality represents whether a field is optional, required, or
/// repeated.
/// Whether a field is optional, required, or repeated.
/// </summary>
public enum Cardinality {
/// <summary>
/// The field cardinality is unknown. Typically an error condition.
/// For fields with unknown cardinality.
/// </summary>
CARDINALITY_UNKNOWN = 0,
/// <summary>
@ -769,7 +800,7 @@ namespace Google.Protobuf.WellKnownTypes {
/// </summary>
CARDINALITY_OPTIONAL = 1,
/// <summary>
/// For required fields. Not used for proto3.
/// For required fields. Proto2 syntax only.
/// </summary>
CARDINALITY_REQUIRED = 2,
/// <summary>
@ -848,7 +879,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
/// Proto options for the enum type.
/// Protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@ -1066,7 +1097,7 @@ namespace Google.Protobuf.WellKnownTypes {
= pb::FieldCodec.ForMessage(26, global::Google.Protobuf.WellKnownTypes.Option.Parser);
private readonly pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> options_ = new pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option>();
/// <summary>
/// Proto options for the enum value.
/// Protocol buffer options.
/// </summary>
public pbc::RepeatedField<global::Google.Protobuf.WellKnownTypes.Option> Options {
get { return options_; }
@ -1164,7 +1195,8 @@ namespace Google.Protobuf.WellKnownTypes {
}
/// <summary>
/// Proto option attached to messages/fields/enums etc.
/// A protocol buffer option, which can be attached to a message, field,
/// enumeration, etc.
/// </summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public sealed partial class Option : pb::IMessage<Option> {
@ -1198,7 +1230,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int NameFieldNumber = 1;
private string name_ = "";
/// <summary>
/// Proto option name.
/// The option's name. For example, `"java_package"`.
/// </summary>
public string Name {
get { return name_; }
@ -1211,7 +1243,7 @@ namespace Google.Protobuf.WellKnownTypes {
public const int ValueFieldNumber = 2;
private global::Google.Protobuf.WellKnownTypes.Any value_;
/// <summary>
/// Proto option value.
/// The option's value. For example, `"com.google.protobuf"`.
/// </summary>
public global::Google.Protobuf.WellKnownTypes.Any Value {
get { return value_; }

View File

@ -29,9 +29,10 @@ namespace Google.Protobuf.WellKnownTypes {
"KAMiHAoLVUludDY0VmFsdWUSDQoFdmFsdWUYASABKAQiGwoKSW50MzJWYWx1",
"ZRINCgV2YWx1ZRgBIAEoBSIcCgtVSW50MzJWYWx1ZRINCgV2YWx1ZRgBIAEo",
"DSIaCglCb29sVmFsdWUSDQoFdmFsdWUYASABKAgiHAoLU3RyaW5nVmFsdWUS",
"DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJQ",
"ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAGgAQGiAgNH",
"UEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3RvMw=="));
"DQoFdmFsdWUYASABKAkiGwoKQnl0ZXNWYWx1ZRINCgV2YWx1ZRgBIAEoDEJT",
"ChNjb20uZ29vZ2xlLnByb3RvYnVmQg1XcmFwcGVyc1Byb3RvUAGgAQH4AQGi",
"AgNHUEKqAh5Hb29nbGUuUHJvdG9idWYuV2VsbEtub3duVHlwZXNiBnByb3Rv",
"Mw=="));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { },
new pbr::GeneratedCodeInfo(null, new pbr::GeneratedCodeInfo[] {

View File

@ -62,7 +62,6 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
descriptor_->message_type()->FindFieldByName("value");
variables_["key_type_name"] = type_name(key_descriptor);
variables_["value_type_name"] = type_name(value_descriptor);
variables_["true_for_wrappers"] = IsWrapperType(value_descriptor) ? "true" : "";
scoped_ptr<FieldGeneratorBase> key_generator(CreateFieldGenerator(key_descriptor, 1));
scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2));
@ -76,7 +75,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
", $tag$);\n"
"private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>($true_for_wrappers$);\n");
"private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\n");
WritePropertyDocComment(printer, descriptor_);
AddDeprecatedFlag(printer);
printer->Print(