Commit 0f442a75 authored by Jon Skeet's avatar Jon Skeet

Merge pull request #611 from jskeet/csharp-wrappers

C# wrapper types
parents fa544f48 34878cb1
...@@ -162,10 +162,10 @@ cc_library( ...@@ -162,10 +162,10 @@ cc_library(
"src/google/protobuf/compiler/cpp/cpp_string_field.cc", "src/google/protobuf/compiler/cpp/cpp_string_field.cc",
"src/google/protobuf/compiler/csharp/csharp_enum.cc", "src/google/protobuf/compiler/csharp/csharp_enum.cc",
"src/google/protobuf/compiler/csharp/csharp_enum_field.cc", "src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
"src/google/protobuf/compiler/csharp/csharp_extension.cc",
"src/google/protobuf/compiler/csharp/csharp_field_base.cc", "src/google/protobuf/compiler/csharp/csharp_field_base.cc",
"src/google/protobuf/compiler/csharp/csharp_generator.cc", "src/google/protobuf/compiler/csharp/csharp_generator.cc",
"src/google/protobuf/compiler/csharp/csharp_helpers.cc", "src/google/protobuf/compiler/csharp/csharp_helpers.cc",
"src/google/protobuf/compiler/csharp/csharp_map_field.cc",
"src/google/protobuf/compiler/csharp/csharp_message.cc", "src/google/protobuf/compiler/csharp/csharp_message.cc",
"src/google/protobuf/compiler/csharp/csharp_message_field.cc", "src/google/protobuf/compiler/csharp/csharp_message_field.cc",
"src/google/protobuf/compiler/csharp/csharp_primitive_field.cc", "src/google/protobuf/compiler/csharp/csharp_primitive_field.cc",
...@@ -174,7 +174,7 @@ cc_library( ...@@ -174,7 +174,7 @@ cc_library(
"src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc", "src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
"src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc", "src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
"src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc", "src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc",
"src/google/protobuf/compiler/csharp/csharp_writer.cc", "src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
"src/google/protobuf/compiler/java/java_context.cc", "src/google/protobuf/compiler/java/java_context.cc",
"src/google/protobuf/compiler/java/java_doc_comment.cc", "src/google/protobuf/compiler/java/java_doc_comment.cc",
"src/google/protobuf/compiler/java/java_enum.cc", "src/google/protobuf/compiler/java/java_enum.cc",
......
...@@ -28,6 +28,7 @@ set(libprotoc_files ...@@ -28,6 +28,7 @@ set(libprotoc_files
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc ${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc ${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc ${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_umbrella_class.cc
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_context.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_context.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_doc_comment.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_doc_comment.cc
${protobuf_source_dir}/src/google/protobuf/compiler/java/java_enum.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_enum.cc
......
...@@ -58,7 +58,8 @@ $PROTOC -Isrc --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \ ...@@ -58,7 +58,8 @@ $PROTOC -Isrc --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \
src/google/protobuf/map_unittest_proto3.proto \ src/google/protobuf/map_unittest_proto3.proto \
src/google/protobuf/unittest_proto3.proto \ src/google/protobuf/unittest_proto3.proto \
src/google/protobuf/unittest_import_proto3.proto \ src/google/protobuf/unittest_import_proto3.proto \
src/google/protobuf/unittest_import_public_proto3.proto src/google/protobuf/unittest_import_public_proto3.proto \
src/google/protobuf/unittest_well_known_types.proto
$PROTOC -Icsharp/protos/extest --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \ $PROTOC -Icsharp/protos/extest --csharp_out=csharp/src/ProtocolBuffers.Test/TestProtos \
......
...@@ -38,6 +38,34 @@ namespace Google.Protobuf ...@@ -38,6 +38,34 @@ namespace Google.Protobuf
{ {
public class ByteStringTest public class ByteStringTest
{ {
[Test]
public void Equality()
{
ByteString b1 = ByteString.CopyFrom(1, 2, 3);
ByteString b2 = ByteString.CopyFrom(1, 2, 3);
ByteString b3 = ByteString.CopyFrom(1, 2, 4);
ByteString b4 = ByteString.CopyFrom(1, 2, 3, 4);
EqualityTester.AssertEquality(b1, b1);
EqualityTester.AssertEquality(b1, b2);
EqualityTester.AssertInequality(b1, b3);
EqualityTester.AssertInequality(b1, b4);
EqualityTester.AssertInequality(b1, null);
#pragma warning disable 1718 // Deliberately calling ==(b1, b1) and !=(b1, b1)
Assert.IsTrue(b1 == b1);
Assert.IsTrue(b1 == b2);
Assert.IsFalse(b1 == b3);
Assert.IsFalse(b1 == b4);
Assert.IsFalse(b1 == null);
Assert.IsTrue((ByteString) null == null);
Assert.IsFalse(b1 != b1);
Assert.IsFalse(b1 != b2);
#pragma warning disable 1718
Assert.IsTrue(b1 != b3);
Assert.IsTrue(b1 != b4);
Assert.IsTrue(b1 != null);
Assert.IsFalse((ByteString) null != null);
}
[Test] [Test]
public void EmptyByteStringHasZeroSize() public void EmptyByteStringHasZeroSize()
{ {
......
...@@ -103,32 +103,35 @@ namespace Google.Protobuf.Collections ...@@ -103,32 +103,35 @@ namespace Google.Protobuf.Collections
} }
[Test] [Test]
public void Add_ForbidsNullKeys() public void NullValues()
{ {
var map = new MapField<string, ForeignMessage>(); TestNullValues<int?>(0);
Assert.Throws<ArgumentNullException>(() => map.Add(null, new ForeignMessage())); TestNullValues("");
TestNullValues(new TestAllTypes());
} }
[Test] private void TestNullValues<T>(T nonNullValue)
public void Add_AcceptsNullMessageValues()
{ {
var map = new MapField<string, ForeignMessage>(); var map = new MapField<int, T>(false);
map.Add("missing", null); var nullValue = (T) (object) null;
Assert.IsNull(map["missing"]); Assert.Throws<ArgumentNullException>(() => map.Add(0, nullValue));
} Assert.Throws<ArgumentNullException>(() => map[0] = nullValue);
map.Add(1, nonNullValue);
map[1] = nonNullValue;
[Test] // Doesn't throw...
public void Add_ForbidsNullStringValues() map = new MapField<int, T>(true);
{ map.Add(0, nullValue);
var map = new MapField<string, string>(); map[0] = nullValue;
Assert.Throws<ArgumentNullException>(() => map.Add("missing", null)); map.Add(1, nonNullValue);
map[1] = nonNullValue;
} }
[Test] [Test]
public void Add_ForbidsNullByteStringValues() public void Add_ForbidsNullKeys()
{ {
var map = new MapField<string, ByteString>(); var map = new MapField<string, ForeignMessage>();
Assert.Throws<ArgumentNullException>(() => map.Add("missing", null)); Assert.Throws<ArgumentNullException>(() => map.Add(null, new ForeignMessage()));
} }
[Test] [Test]
...@@ -138,28 +141,6 @@ namespace Google.Protobuf.Collections ...@@ -138,28 +141,6 @@ namespace Google.Protobuf.Collections
Assert.Throws<ArgumentNullException>(() => map[null] = new ForeignMessage()); Assert.Throws<ArgumentNullException>(() => map[null] = new ForeignMessage());
} }
[Test]
public void Indexer_AcceptsNullMessageValues()
{
var map = new MapField<string, ForeignMessage>();
map["missing"] = null;
Assert.IsNull(map["missing"]);
}
[Test]
public void Indexer_ForbidsNullStringValues()
{
var map = new MapField<string, string>();
Assert.Throws<ArgumentNullException>(() => map["missing"] = null);
}
[Test]
public void Indexer_ForbidsNullByteStringValues()
{
var map = new MapField<string, ByteString>();
Assert.Throws<ArgumentNullException>(() => map["missing"] = null);
}
[Test] [Test]
public void AddPreservesInsertionOrder() public void AddPreservesInsertionOrder()
{ {
...@@ -528,6 +509,30 @@ namespace Google.Protobuf.Collections ...@@ -528,6 +509,30 @@ namespace Google.Protobuf.Collections
Assert.Throws<NotSupportedException>(() => dictionary["a"] = "c"); Assert.Throws<NotSupportedException>(() => dictionary["a"] = "c");
} }
[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);
}
private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value) private static KeyValuePair<TKey, TValue> NewKeyValuePair<TKey, TValue>(TKey key, TValue value)
{ {
return new KeyValuePair<TKey, TValue>(key, value); return new KeyValuePair<TKey, TValue>(key, value);
......
...@@ -93,6 +93,8 @@ ...@@ -93,6 +93,8 @@
<Compile Include="IssuesTest.cs" /> <Compile Include="IssuesTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TestCornerCases.cs" /> <Compile Include="TestCornerCases.cs" />
<Compile Include="TestProtos\UnittestWellKnownTypes.cs" />
<Compile Include="WellKnownTypes\WrappersTest.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj"> <ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -212,11 +212,22 @@ namespace Google.Protobuf ...@@ -212,11 +212,22 @@ namespace Google.Protobuf
{ {
return true; return true;
} }
if (ReferenceEquals(lhs, null)) if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null))
{ {
return false; return false;
} }
return lhs.Equals(rhs); if (lhs.bytes.Length != rhs.bytes.Length)
{
return false;
}
for (int i = 0; i < lhs.Length; i++)
{
if (rhs.bytes[i] != lhs.bytes[i])
{
return false;
}
}
return true;
} }
public static bool operator !=(ByteString lhs, ByteString rhs) public static bool operator !=(ByteString lhs, ByteString rhs)
...@@ -228,12 +239,7 @@ namespace Google.Protobuf ...@@ -228,12 +239,7 @@ namespace Google.Protobuf
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
ByteString other = obj as ByteString; return this == (obj as ByteString);
if (obj == null)
{
return false;
}
return Equals(other);
} }
public override int GetHashCode() public override int GetHashCode()
...@@ -248,18 +254,7 @@ namespace Google.Protobuf ...@@ -248,18 +254,7 @@ namespace Google.Protobuf
public bool Equals(ByteString other) public bool Equals(ByteString other)
{ {
if (other.bytes.Length != bytes.Length) return this == other;
{
return false;
}
for (int i = 0; i < bytes.Length; i++)
{
if (other.bytes[i] != bytes[i])
{
return false;
}
}
return true;
} }
/// <summary> /// <summary>
......
...@@ -129,8 +129,7 @@ namespace Google.Protobuf ...@@ -129,8 +129,7 @@ namespace Google.Protobuf
public static int ComputeStringSize(String value) public static int ComputeStringSize(String value)
{ {
int byteArraySize = Utf8Encoding.GetByteCount(value); int byteArraySize = Utf8Encoding.GetByteCount(value);
return ComputeRawVarint32Size((uint) byteArraySize) + return ComputeLengthSize(byteArraySize) + byteArraySize;
byteArraySize;
} }
/// <summary> /// <summary>
...@@ -149,7 +148,7 @@ namespace Google.Protobuf ...@@ -149,7 +148,7 @@ namespace Google.Protobuf
public static int ComputeMessageSize(IMessage value) public static int ComputeMessageSize(IMessage value)
{ {
int size = value.CalculateSize(); int size = value.CalculateSize();
return ComputeRawVarint32Size((uint) size) + size; return ComputeLengthSize(size) + size;
} }
/// <summary> /// <summary>
...@@ -158,8 +157,7 @@ namespace Google.Protobuf ...@@ -158,8 +157,7 @@ namespace Google.Protobuf
/// </summary> /// </summary>
public static int ComputeBytesSize(ByteString value) public static int ComputeBytesSize(ByteString value)
{ {
return ComputeRawVarint32Size((uint) value.Length) + return ComputeLengthSize(value.Length) + value.Length;
value.Length;
} }
/// <summary> /// <summary>
......
...@@ -274,7 +274,7 @@ namespace Google.Protobuf ...@@ -274,7 +274,7 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param> /// <param name="value">The value to write</param>
public void WriteMessage(IMessage value) public void WriteMessage(IMessage value)
{ {
WriteRawVarint32((uint) value.CalculateSize()); WriteLength(value.CalculateSize());
value.WriteTo(this); value.WriteTo(this);
} }
...@@ -285,7 +285,7 @@ namespace Google.Protobuf ...@@ -285,7 +285,7 @@ namespace Google.Protobuf
/// <param name="value">The value to write</param> /// <param name="value">The value to write</param>
public void WriteBytes(ByteString value) public void WriteBytes(ByteString value)
{ {
WriteRawVarint32((uint) value.Length); WriteLength(value.Length);
value.WriteRawBytesTo(this); value.WriteRawBytesTo(this);
} }
......
...@@ -51,14 +51,38 @@ namespace Google.Protobuf.Collections ...@@ -51,14 +51,38 @@ namespace Google.Protobuf.Collections
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>, IDictionary public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, 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.) // TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
private readonly bool allowNullValues;
private bool frozen; private bool frozen;
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map = private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> map =
new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>(); new Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>();
private readonly LinkedList<KeyValuePair<TKey, TValue>> list = new LinkedList<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;
}
public MapField<TKey, TValue> Clone() public MapField<TKey, TValue> Clone()
{ {
var clone = new MapField<TKey, TValue>(); var clone = new MapField<TKey, TValue>(allowNullValues);
// Keys are never cloneable. Values might be. // Keys are never cloneable. Values might be.
if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue))) if (typeof(IDeepCloneable<TValue>).IsAssignableFrom(typeof(TValue)))
{ {
...@@ -138,7 +162,8 @@ namespace Google.Protobuf.Collections ...@@ -138,7 +162,8 @@ namespace Google.Protobuf.Collections
set set
{ {
ThrowHelper.ThrowIfNull(key, "key"); ThrowHelper.ThrowIfNull(key, "key");
if (value == null && (typeof(TValue) == typeof(ByteString) || typeof(TValue) == typeof(string))) // value == null check here is redundant, but avoids boxing.
if (value == null && !allowNullValues)
{ {
ThrowHelper.ThrowIfNull(value, "value"); ThrowHelper.ThrowIfNull(value, "value");
} }
...@@ -225,6 +250,11 @@ namespace Google.Protobuf.Collections ...@@ -225,6 +250,11 @@ namespace Google.Protobuf.Collections
} }
} }
/// <summary>
/// Returns whether or not this map allows values to be null.
/// </summary>
public bool AllowsNullValues { get { return allowNullValues; } }
public int Count { get { return list.Count; } } public int Count { get { return list.Count; } }
public bool IsReadOnly { get { return frozen; } } public bool IsReadOnly { get { return frozen; } }
......
...@@ -123,7 +123,7 @@ namespace Google.Protobuf.Collections ...@@ -123,7 +123,7 @@ namespace Google.Protobuf.Collections
{ {
int dataSize = CalculatePackedDataSize(codec); int dataSize = CalculatePackedDataSize(codec);
return CodedOutputStream.ComputeRawVarint32Size(tag) + return CodedOutputStream.ComputeRawVarint32Size(tag) +
CodedOutputStream.ComputeRawVarint32Size((uint)dataSize) + CodedOutputStream.ComputeLengthSize(dataSize) +
dataSize; dataSize;
} }
else else
......
...@@ -131,6 +131,116 @@ namespace Google.Protobuf ...@@ -131,6 +131,116 @@ namespace Google.Protobuf
return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; }, return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
(output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag); (output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
} }
/// <summary>
/// Creates a codec for a wrapper type of a class - which must be string or ByteString.
/// </summary>
public static FieldCodec<T> ForClassWrapper<T>(uint tag) where T : class
{
var nestedCodec = WrapperCodecs.GetCodec<T>();
return new FieldCodec<T>(
input => WrapperCodecs.Read<T>(input, nestedCodec),
(output, value) => WrapperCodecs.Write<T>(output, value, nestedCodec),
value => WrapperCodecs.CalculateSize<T>(value, nestedCodec),
tag,
null); // Default value for the wrapper
}
/// <summary>
/// Creates a codec for a wrapper type of a struct - which must be Int32, Int64, UInt32, UInt64,
/// Bool, Single or Double.
/// </summary>
public static FieldCodec<T?> ForStructWrapper<T>(uint tag) where T : struct
{
var nestedCodec = WrapperCodecs.GetCodec<T>();
return new FieldCodec<T?>(
input => WrapperCodecs.Read<T>(input, nestedCodec),
(output, value) => WrapperCodecs.Write<T>(output, value.Value, nestedCodec),
value => value == null ? 0 : WrapperCodecs.CalculateSize<T>(value.Value, nestedCodec),
tag,
null); // Default value for the wrapper
}
/// <summary>
/// Helper code to create codecs for wrapper types.
/// </summary>
/// <remarks>
/// Somewhat ugly with all the static methods, but the conversions involved to/from nullable types make it
/// slightly tricky to improve. So long as we keep the public API (ForClassWrapper, ForStructWrapper) in place,
/// we can refactor later if we come up with something cleaner.
/// </remarks>
private static class WrapperCodecs
{
// All the field numbers are the same (1).
private const int WrapperValueFieldNumber = Google.Protobuf.WellKnownTypes.Int32Value.ValueFieldNumber;
private static readonly Dictionary<Type, object> Codecs = new Dictionary<Type, object>
{
{ typeof(bool), ForBool(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
{ typeof(int), ForInt32(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
{ typeof(long), ForInt64(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
{ typeof(uint), ForUInt32(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
{ typeof(ulong), ForUInt64(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Varint)) },
{ typeof(float), ForFloat(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed32)) },
{ typeof(double), ForDouble(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.Fixed64)) },
{ typeof(string), ForString(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) },
{ typeof(ByteString), ForBytes(WireFormat.MakeTag(WrapperValueFieldNumber, WireFormat.WireType.LengthDelimited)) }
};
/// <summary>
/// Returns a field codec which effectively wraps a value of type T in a message.
///
/// </summary>
internal static FieldCodec<T> GetCodec<T>()
{
object value;
if (!Codecs.TryGetValue(typeof(T), out value))
{
throw new InvalidOperationException("Invalid type argument requested for wrapper codec: " + typeof(T));
}
return (FieldCodec<T>) value;
}
internal static T Read<T>(CodedInputStream input, FieldCodec<T> codec)
{
int length = input.ReadLength();
int oldLimit = input.PushLimit(length);
uint tag;
T value = codec.DefaultValue;
while (input.ReadTag(out tag))
{
if (tag == 0)
{
throw InvalidProtocolBufferException.InvalidTag();
}
if (tag == codec.Tag)
{
value = codec.Read(input);
}
if (WireFormat.IsEndGroupTag(tag))
{
break;
}
}
input.CheckLastTagWas(0);
input.PopLimit(oldLimit);
return value;
}
internal static void Write<T>(CodedOutputStream output, T value, FieldCodec<T> codec)
{
output.WriteLength(codec.CalculateSizeWithTag(value));
codec.WriteTagAndValue(output, value);
}
internal static int CalculateSize<T>(T value, FieldCodec<T> codec)
{
int fieldLength = codec.CalculateSizeWithTag(value);
return CodedOutputStream.ComputeLengthSize(fieldLength) + fieldLength;
}
}
} }
/// <summary> /// <summary>
...@@ -144,31 +254,19 @@ namespace Google.Protobuf ...@@ -144,31 +254,19 @@ namespace Google.Protobuf
/// </remarks> /// </remarks>
public sealed class FieldCodec<T> public sealed class FieldCodec<T>
{ {
private static readonly Func<T, bool> IsDefault; private static readonly T DefaultDefault;
private static readonly T Default;
static FieldCodec() static FieldCodec()
{ {
if (typeof(T) == typeof(string)) if (typeof(T) == typeof(string))
{ {
Default = (T)(object)""; DefaultDefault = (T)(object)"";
IsDefault = CreateDefaultValueCheck<string>(x => x.Length == 0);
} }
else if (typeof(T) == typeof(ByteString)) else if (typeof(T) == typeof(ByteString))
{ {
Default = (T)(object)ByteString.Empty; DefaultDefault = (T)(object)ByteString.Empty;
IsDefault = CreateDefaultValueCheck<ByteString>(x => x.Length == 0);
}
else if (!typeof(T).IsValueType)
{
// Default default
IsDefault = CreateDefaultValueCheck<T>(x => x == null);
}
else
{
// Default default
IsDefault = CreateDefaultValueCheck<T>(x => EqualityComparer<T>.Default.Equals(x, default(T)));
} }
// Otherwise it's the default value of the CLR type
} }
private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check) private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
...@@ -182,18 +280,32 @@ namespace Google.Protobuf ...@@ -182,18 +280,32 @@ namespace Google.Protobuf
private readonly uint tag; private readonly uint tag;
private readonly int tagSize; private readonly int tagSize;
private readonly int fixedSize; private readonly int fixedSize;
// Default value for this codec. Usually the same for every instance of the same type, but
// for string/ByteString wrapper fields the codec's default value is null, whereas for
// other string/ByteString fields it's "" or ByteString.Empty.
private readonly T defaultValue;
internal FieldCodec( internal FieldCodec(
Func<CodedInputStream, T> reader, Func<CodedInputStream, T> reader,
Action<CodedOutputStream, T> writer, Action<CodedOutputStream, T> writer,
Func<T, int> sizeCalculator, Func<T, int> sizeCalculator,
uint tag) uint tag) : this(reader, writer, sizeCalculator, tag, DefaultDefault)
{
}
internal FieldCodec(
Func<CodedInputStream, T> reader,
Action<CodedOutputStream, T> writer,
Func<T, int> sizeCalculator,
uint tag,
T defaultValue)
{ {
this.reader = reader; this.reader = reader;
this.writer = writer; this.writer = writer;
this.sizeCalculator = sizeCalculator; this.sizeCalculator = sizeCalculator;
this.fixedSize = 0; this.fixedSize = 0;
this.tag = tag; this.tag = tag;
this.defaultValue = defaultValue;
tagSize = CodedOutputStream.ComputeRawVarint32Size(tag); tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
} }
...@@ -234,7 +346,7 @@ namespace Google.Protobuf ...@@ -234,7 +346,7 @@ namespace Google.Protobuf
public uint Tag { get { return tag; } } public uint Tag { get { return tag; } }
public T DefaultValue { get { return Default; } } public T DefaultValue { get { return defaultValue; } }
/// <summary> /// <summary>
/// Write a tag and the given value, *if* the value is not the default. /// Write a tag and the given value, *if* the value is not the default.
...@@ -261,5 +373,10 @@ namespace Google.Protobuf ...@@ -261,5 +373,10 @@ namespace Google.Protobuf
{ {
return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize; return IsDefault(value) ? 0 : sizeCalculator(value) + tagSize;
} }
private bool IsDefault(T value)
{
return EqualityComparer<T>.Default.Equals(value, defaultValue);
}
} }
} }
...@@ -442,7 +442,9 @@ libprotoc_la_SOURCES = \ ...@@ -442,7 +442,9 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/csharp/csharp_source_generator_base.cc \ google/protobuf/compiler/csharp/csharp_source_generator_base.cc \
google/protobuf/compiler/csharp/csharp_source_generator_base.h \ google/protobuf/compiler/csharp/csharp_source_generator_base.h \
google/protobuf/compiler/csharp/csharp_umbrella_class.cc \ google/protobuf/compiler/csharp/csharp_umbrella_class.cc \
google/protobuf/compiler/csharp/csharp_umbrella_class.h google/protobuf/compiler/csharp/csharp_umbrella_class.h \
google/protobuf/compiler/csharp/csharp_wrapper_field.cc \
google/protobuf/compiler/csharp/csharp_wrapper_field.h
bin_PROGRAMS = protoc bin_PROGRAMS = protoc
protoc_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la protoc_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la
......
...@@ -169,6 +169,18 @@ std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) { ...@@ -169,6 +169,18 @@ std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) {
return GetClassName(descriptor->enum_type()); return GetClassName(descriptor->enum_type());
case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_GROUP:
if (IsWrapperType(descriptor)) {
const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0);
string wrapped_field_type_name = type_name(wrapped_field);
// String and ByteString go to the same type; other wrapped types go to the
// nullable equivalent.
if (wrapped_field->type() == FieldDescriptor::TYPE_STRING ||
wrapped_field->type() == FieldDescriptor::TYPE_BYTES) {
return wrapped_field_type_name;
} else {
return wrapped_field_type_name + "?";
}
}
return GetClassName(descriptor->message_type()); return GetClassName(descriptor->message_type());
case FieldDescriptor::TYPE_DOUBLE: case FieldDescriptor::TYPE_DOUBLE:
return "double"; return "double";
...@@ -298,14 +310,23 @@ std::string FieldGeneratorBase::GetBytesDefaultValueInternal() { ...@@ -298,14 +310,23 @@ std::string FieldGeneratorBase::GetBytesDefaultValueInternal() {
} }
std::string FieldGeneratorBase::default_value() { std::string FieldGeneratorBase::default_value() {
switch (descriptor_->type()) { return default_value(descriptor_);
}
std::string FieldGeneratorBase::default_value(const FieldDescriptor* descriptor) {
switch (descriptor->type()) {
case FieldDescriptor::TYPE_ENUM: case FieldDescriptor::TYPE_ENUM:
return type_name() + "." + descriptor_->default_value_enum()->name(); return type_name() + "." + descriptor->default_value_enum()->name();
case FieldDescriptor::TYPE_MESSAGE: case FieldDescriptor::TYPE_MESSAGE:
case FieldDescriptor::TYPE_GROUP: case FieldDescriptor::TYPE_GROUP:
return type_name() + ".DefaultInstance"; if (IsWrapperType(descriptor)) {
const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0);
return default_value(wrapped_field);
} else {
return "null";
}
case FieldDescriptor::TYPE_DOUBLE: { case FieldDescriptor::TYPE_DOUBLE: {
double value = descriptor_->default_value_double(); double value = descriptor->default_value_double();
if (value == numeric_limits<double>::infinity()) { if (value == numeric_limits<double>::infinity()) {
return "double.PositiveInfinity"; return "double.PositiveInfinity";
} else if (value == -numeric_limits<double>::infinity()) { } else if (value == -numeric_limits<double>::infinity()) {
...@@ -316,7 +337,7 @@ std::string FieldGeneratorBase::default_value() { ...@@ -316,7 +337,7 @@ std::string FieldGeneratorBase::default_value() {
return SimpleDtoa(value) + "D"; return SimpleDtoa(value) + "D";
} }
case FieldDescriptor::TYPE_FLOAT: { case FieldDescriptor::TYPE_FLOAT: {
float value = descriptor_->default_value_float(); float value = descriptor->default_value_float();
if (value == numeric_limits<float>::infinity()) { if (value == numeric_limits<float>::infinity()) {
return "float.PositiveInfinity"; return "float.PositiveInfinity";
} else if (value == -numeric_limits<float>::infinity()) { } else if (value == -numeric_limits<float>::infinity()) {
...@@ -327,17 +348,17 @@ std::string FieldGeneratorBase::default_value() { ...@@ -327,17 +348,17 @@ std::string FieldGeneratorBase::default_value() {
return SimpleFtoa(value) + "F"; return SimpleFtoa(value) + "F";
} }
case FieldDescriptor::TYPE_INT64: case FieldDescriptor::TYPE_INT64:
return SimpleItoa(descriptor_->default_value_int64()) + "L"; return SimpleItoa(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_UINT64: case FieldDescriptor::TYPE_UINT64:
return SimpleItoa(descriptor_->default_value_uint64()) + "UL"; return SimpleItoa(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_INT32: case FieldDescriptor::TYPE_INT32:
return SimpleItoa(descriptor_->default_value_int32()); return SimpleItoa(descriptor->default_value_int32());
case FieldDescriptor::TYPE_FIXED64: case FieldDescriptor::TYPE_FIXED64:
return SimpleItoa(descriptor_->default_value_uint64()) + "UL"; return SimpleItoa(descriptor->default_value_uint64()) + "UL";
case FieldDescriptor::TYPE_FIXED32: case FieldDescriptor::TYPE_FIXED32:
return SimpleItoa(descriptor_->default_value_uint32()); return SimpleItoa(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_BOOL: case FieldDescriptor::TYPE_BOOL:
if (descriptor_->default_value_bool()) { if (descriptor->default_value_bool()) {
return "true"; return "true";
} else { } else {
return "false"; return "false";
...@@ -347,15 +368,15 @@ std::string FieldGeneratorBase::default_value() { ...@@ -347,15 +368,15 @@ std::string FieldGeneratorBase::default_value() {
case FieldDescriptor::TYPE_BYTES: case FieldDescriptor::TYPE_BYTES:
return GetBytesDefaultValueInternal(); return GetBytesDefaultValueInternal();
case FieldDescriptor::TYPE_UINT32: case FieldDescriptor::TYPE_UINT32:
return SimpleItoa(descriptor_->default_value_uint32()); return SimpleItoa(descriptor->default_value_uint32());
case FieldDescriptor::TYPE_SFIXED32: case FieldDescriptor::TYPE_SFIXED32:
return SimpleItoa(descriptor_->default_value_int32()); return SimpleItoa(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SFIXED64: case FieldDescriptor::TYPE_SFIXED64:
return SimpleItoa(descriptor_->default_value_int64()) + "L"; return SimpleItoa(descriptor->default_value_int64()) + "L";
case FieldDescriptor::TYPE_SINT32: case FieldDescriptor::TYPE_SINT32:
return SimpleItoa(descriptor_->default_value_int32()); return SimpleItoa(descriptor->default_value_int32());
case FieldDescriptor::TYPE_SINT64: case FieldDescriptor::TYPE_SINT64:
return SimpleItoa(descriptor_->default_value_int64()) + "L"; return SimpleItoa(descriptor->default_value_int64()) + "L";
default: default:
GOOGLE_LOG(FATAL)<< "Unknown field type."; GOOGLE_LOG(FATAL)<< "Unknown field type.";
return ""; return "";
......
...@@ -82,6 +82,7 @@ class FieldGeneratorBase : public SourceGeneratorBase { ...@@ -82,6 +82,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
bool has_default_value(); bool has_default_value();
bool is_nullable_type(); bool is_nullable_type();
std::string default_value(); std::string default_value();
std::string default_value(const FieldDescriptor* descriptor);
std::string number(); std::string number();
std::string capitalized_type_name(); std::string capitalized_type_name();
std::string field_ordinal(); std::string field_ordinal();
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_primitive_field.h>
#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
namespace google { namespace google {
namespace protobuf { namespace protobuf {
...@@ -365,6 +366,13 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, ...@@ -365,6 +366,13 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
} else { } else {
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal); return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
} }
} else {
if (IsWrapperType(descriptor)) {
if (descriptor->containing_oneof()) {
return new WrapperOneofFieldGenerator(descriptor, fieldOrdinal);
} else {
return new WrapperFieldGenerator(descriptor, fieldOrdinal);
}
} else { } else {
if (descriptor->containing_oneof()) { if (descriptor->containing_oneof()) {
return new MessageOneofFieldGenerator(descriptor, fieldOrdinal); return new MessageOneofFieldGenerator(descriptor, fieldOrdinal);
...@@ -372,6 +380,7 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, ...@@ -372,6 +380,7 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
return new MessageFieldGenerator(descriptor, fieldOrdinal); return new MessageFieldGenerator(descriptor, fieldOrdinal);
} }
} }
}
case FieldDescriptor::TYPE_ENUM: case FieldDescriptor::TYPE_ENUM:
if (descriptor->is_repeated()) { if (descriptor->is_repeated()) {
return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal); return new RepeatedEnumFieldGenerator(descriptor, fieldOrdinal);
......
...@@ -119,6 +119,15 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) { ...@@ -119,6 +119,15 @@ inline bool IsDescriptorProto(const FileDescriptor* descriptor) {
return descriptor->name() == "google/protobuf/descriptor_proto_file.proto"; return descriptor->name() == "google/protobuf/descriptor_proto_file.proto";
} }
inline bool IsMapEntry(const Descriptor* descriptor) {
return descriptor->options().map_entry();
}
inline bool IsWrapperType(const FieldDescriptor* descriptor) {
return descriptor->type() == FieldDescriptor::TYPE_MESSAGE &&
descriptor->message_type()->file()->name() == "google/protobuf/wrappers.proto";
}
} // namespace csharp } // namespace csharp
} // namespace compiler } // namespace compiler
} // namespace protobuf } // namespace protobuf
......
...@@ -61,6 +61,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -61,6 +61,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
descriptor_->message_type()->FindFieldByName("value"); descriptor_->message_type()->FindFieldByName("value");
variables_["key_type_name"] = type_name(key_descriptor); variables_["key_type_name"] = type_name(key_descriptor);
variables_["value_type_name"] = type_name(value_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> key_generator(CreateFieldGenerator(key_descriptor, 1));
scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2)); scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2));
...@@ -74,7 +75,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -74,7 +75,7 @@ void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print( printer->Print(
variables_, variables_,
", $tag$);\n" ", $tag$);\n"
"private readonly pbc::MapField<$key_type_name$, $value_type_name$> $name$_ = new pbc::MapField<$key_type_name$, $value_type_name$>();\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");
AddDeprecatedFlag(printer); AddDeprecatedFlag(printer);
printer->Print( printer->Print(
variables_, variables_,
......
...@@ -39,6 +39,8 @@ ...@@ -39,6 +39,8 @@
#include <google/protobuf/compiler/csharp/csharp_helpers.h> #include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h> #include <google/protobuf/compiler/csharp/csharp_repeated_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_message_field.h>
#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
namespace google { namespace google {
namespace protobuf { namespace protobuf {
...@@ -58,7 +60,18 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) { ...@@ -58,7 +60,18 @@ void RepeatedMessageFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print( printer->Print(
variables_, variables_,
"private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n" "private static readonly pb::FieldCodec<$type_name$> _repeated_$name$_codec\n"
" = pb::FieldCodec.ForMessage($tag$, $type_name$.Parser);\n"); " = ");
// Don't want to duplicate the codec code here... maybe we should have a
// "create single field generator for this repeated field"
// function, but it doesn't seem worth it for just this.
if (IsWrapperType(descriptor_)) {
scoped_ptr<FieldGeneratorBase> single_generator(new WrapperFieldGenerator(descriptor_, fieldOrdinal_));
single_generator->GenerateCodecCode(printer);
} else {
scoped_ptr<FieldGeneratorBase> single_generator(new MessageFieldGenerator(descriptor_, fieldOrdinal_));
single_generator->GenerateCodecCode(printer);
}
printer->Print(";\n");
printer->Print( printer->Print(
variables_, variables_,
"private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n"); "private readonly pbc::RepeatedField<$type_name$> $name$_ = new pbc::RepeatedField<$type_name$>();\n");
......
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/plugin.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
#include <google/protobuf/compiler/csharp/csharp_wrapper_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
WrapperFieldGenerator::WrapperFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal)
: FieldGeneratorBase(descriptor, fieldOrdinal) {
variables_["has_property_check"] = name() + "_ != null";
variables_["has_not_property_check"] = name() + "_ == null";
const FieldDescriptor* wrapped_field = descriptor->message_type()->field(0);
is_value_type = wrapped_field->type() != FieldDescriptor::TYPE_STRING &&
wrapped_field->type() != FieldDescriptor::TYPE_BYTES;
if (is_value_type) {
variables_["nonnullable_type_name"] = type_name(wrapped_field);
}
}
WrapperFieldGenerator::~WrapperFieldGenerator() {
}
void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) {
printer->Print(
variables_,
"private static readonly pb::FieldCodec<$type_name$> _single_$name$_codec = ");
GenerateCodecCode(printer);
printer->Print(
variables_,
";\n"
"private $type_name$ $name$_;\n");
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $name$_; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $name$_ = value;\n"
" }\n"
"}\n");
}
void WrapperFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
"if (other.$has_property_check$) {\n"
" if ($has_not_property_check$ || other.$property_name$ != $default_value$) {\n"
" $property_name$ = other.$property_name$;\n"
" }\n"
"}\n");
}
void WrapperFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"$type_name$ value = _single_$name$_codec.Read(input);\n"
"if ($has_not_property_check$ || value != $default_value$) {\n"
" $property_name$ = value;\n"
"}\n");
}
void WrapperFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" _single_$name$_codec.WriteTagAndValue(output, $property_name$);\n"
"}\n");
}
void WrapperFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += _single_$name$_codec.CalculateSizeWithTag($property_name$);\n"
"}\n");
}
void WrapperFieldGenerator::WriteHash(io::Printer* printer) {
printer->Print(
variables_,
"if ($has_property_check$) hash ^= $property_name$.GetHashCode();\n");
}
void WrapperFieldGenerator::WriteEquals(io::Printer* printer) {
printer->Print(
variables_,
"if ($property_name$ != other.$property_name$) return false;\n");
}
void WrapperFieldGenerator::WriteToString(io::Printer* printer) {
// TODO: Implement if we ever actually need it...
}
void WrapperFieldGenerator::GenerateCloningCode(io::Printer* printer) {
printer->Print(variables_,
"$property_name$ = other.$property_name$;\n");
}
void WrapperFieldGenerator::GenerateCodecCode(io::Printer* printer) {
if (is_value_type) {
printer->Print(
variables_,
"pb::FieldCodec.ForStructWrapper<$nonnullable_type_name$>($tag$)");
} else {
printer->Print(
variables_,
"pb::FieldCodec.ForClassWrapper<$type_name$>($tag$)");
}
}
WrapperOneofFieldGenerator::WrapperOneofFieldGenerator(const FieldDescriptor* descriptor,
int fieldOrdinal)
: WrapperFieldGenerator(descriptor, fieldOrdinal) {
SetCommonOneofFieldVariables(&variables_);
}
WrapperOneofFieldGenerator::~WrapperOneofFieldGenerator() {
}
void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) {
// Note: deliberately _oneof_$name$_codec, not _$oneof_name$_codec... we have one codec per field.
printer->Print(
variables_,
"private static readonly pb::FieldCodec<$type_name$> _oneof_$name$_codec = ");
GenerateCodecCode(printer);
printer->Print(";\n");
AddDeprecatedFlag(printer);
printer->Print(
variables_,
"$access_level$ $type_name$ $property_name$ {\n"
" get { return $has_property_check$ ? ($type_name$) $oneof_name$_ : ($type_name$) null; }\n"
" set {\n"
" pb::Freezable.CheckMutable(this);\n"
" $oneof_name$_ = value;\n"
" $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n"
" }\n"
"}\n");
}
void WrapperOneofFieldGenerator::GenerateParsingCode(io::Printer* printer) {
printer->Print(
variables_,
"$property_name$ = _oneof_$name$_codec.Read(input);\n");
}
void WrapperOneofFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
// TODO: I suspect this is wrong...
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" _oneof_$name$_codec.WriteTagAndValue(output, ($type_name$) $oneof_name$_);\n"
"}\n");
}
void WrapperOneofFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
// TODO: I suspect this is wrong...
printer->Print(
variables_,
"if ($has_property_check$) {\n"
" size += _oneof_$name$_codec.CalculateSizeWithTag($property_name$);\n"
"}\n");
}
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__
#include <string>
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace csharp {
class WrapperFieldGenerator : public FieldGeneratorBase {
public:
WrapperFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~WrapperFieldGenerator();
virtual void GenerateCodecCode(io::Printer* printer);
virtual void GenerateCloningCode(io::Printer* printer);
virtual void GenerateMembers(io::Printer* printer);
virtual void GenerateMergingCode(io::Printer* printer);
virtual void GenerateParsingCode(io::Printer* printer);
virtual void GenerateSerializationCode(io::Printer* printer);
virtual void GenerateSerializedSizeCode(io::Printer* printer);
virtual void WriteHash(io::Printer* printer);
virtual void WriteEquals(io::Printer* printer);
virtual void WriteToString(io::Printer* printer);
private:
bool is_value_type; // True for int32 etc; false for bytes and string
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WrapperFieldGenerator);
};
class WrapperOneofFieldGenerator : public WrapperFieldGenerator {
public:
WrapperOneofFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
~WrapperOneofFieldGenerator();
virtual void GenerateMembers(io::Printer* printer);
virtual void GenerateParsingCode(io::Printer* printer);
virtual void GenerateSerializationCode(io::Printer* printer);
virtual void GenerateSerializedSizeCode(io::Printer* printer);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WrapperOneofFieldGenerator);
};
} // namespace csharp
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_WRAPPER_FIELD_H__
...@@ -2,6 +2,7 @@ syntax = "proto3"; ...@@ -2,6 +2,7 @@ syntax = "proto3";
package protobuf_unittest; package protobuf_unittest;
option csharp_namespace = "Google.Protobuf.TestProtos";
option java_multiple_files = true; option java_multiple_files = true;
option java_package = "com.google.protobuf.test"; option java_package = "com.google.protobuf.test";
...@@ -17,6 +18,8 @@ import "google/protobuf/type.proto"; ...@@ -17,6 +18,8 @@ import "google/protobuf/type.proto";
import "google/protobuf/wrappers.proto"; import "google/protobuf/wrappers.proto";
// Test that we can include all well-known types. // Test that we can include all well-known types.
// Each wrapper type is included separately, as languages
// map handle different wrappers in different ways.
message TestWellKnownTypes { message TestWellKnownTypes {
google.protobuf.Any any_field = 1; google.protobuf.Any any_field = 1;
google.protobuf.Api api_field = 2; google.protobuf.Api api_field = 2;
...@@ -27,5 +30,83 @@ message TestWellKnownTypes { ...@@ -27,5 +30,83 @@ message TestWellKnownTypes {
google.protobuf.Struct struct_field = 7; google.protobuf.Struct struct_field = 7;
google.protobuf.Timestamp timestamp_field = 8; google.protobuf.Timestamp timestamp_field = 8;
google.protobuf.Type type_field = 9; google.protobuf.Type type_field = 9;
google.protobuf.Int32Value int32_field = 10; google.protobuf.DoubleValue double_field = 10;
google.protobuf.FloatValue float_field = 11;
google.protobuf.Int64Value int64_field = 12;
google.protobuf.UInt64Value uint64_field = 13;
google.protobuf.Int32Value int32_field = 14;
google.protobuf.UInt32Value uint32_field = 15;
google.protobuf.BoolValue bool_field = 16;
google.protobuf.StringValue string_field = 17;
google.protobuf.BytesValue bytes_field = 18;
}
// A repeated field for each well-known type.
message RepeatedWellKnownTypes {
repeated google.protobuf.Any any_field = 1;
repeated google.protobuf.Api api_field = 2;
repeated google.protobuf.Duration duration_field = 3;
repeated google.protobuf.Empty empty_field = 4;
repeated google.protobuf.FieldMask field_mask_field = 5;
repeated google.protobuf.SourceContext source_context_field = 6;
repeated google.protobuf.Struct struct_field = 7;
repeated google.protobuf.Timestamp timestamp_field = 8;
repeated google.protobuf.Type type_field = 9;
// These don't actually make a lot of sense, but they're not prohibited...
repeated google.protobuf.DoubleValue double_field = 10;
repeated google.protobuf.FloatValue float_field = 11;
repeated google.protobuf.Int64Value int64_field = 12;
repeated google.protobuf.UInt64Value uint64_field = 13;
repeated google.protobuf.Int32Value int32_field = 14;
repeated google.protobuf.UInt32Value uint32_field = 15;
repeated google.protobuf.BoolValue bool_field = 16;
repeated google.protobuf.StringValue string_field = 17;
repeated google.protobuf.BytesValue bytes_field = 18;
}
message OneofWellKnownTypes {
oneof oneof_field {
google.protobuf.Any any_field = 1;
google.protobuf.Api api_field = 2;
google.protobuf.Duration duration_field = 3;
google.protobuf.Empty empty_field = 4;
google.protobuf.FieldMask field_mask_field = 5;
google.protobuf.SourceContext source_context_field = 6;
google.protobuf.Struct struct_field = 7;
google.protobuf.Timestamp timestamp_field = 8;
google.protobuf.Type type_field = 9;
google.protobuf.DoubleValue double_field = 10;
google.protobuf.FloatValue float_field = 11;
google.protobuf.Int64Value int64_field = 12;
google.protobuf.UInt64Value uint64_field = 13;
google.protobuf.Int32Value int32_field = 14;
google.protobuf.UInt32Value uint32_field = 15;
google.protobuf.BoolValue bool_field = 16;
google.protobuf.StringValue string_field = 17;
google.protobuf.BytesValue bytes_field = 18;
}
}
// A map field for each well-known type. We only
// need to worry about the value part of the map being the
// well-known types, as messages can't be map keys.
message MapWellKnownTypes {
map<int32,google.protobuf.Any> any_field = 1;
map<int32,google.protobuf.Api> api_field = 2;
map<int32,google.protobuf.Duration> duration_field = 3;
map<int32,google.protobuf.Empty> empty_field = 4;
map<int32,google.protobuf.FieldMask> field_mask_field = 5;
map<int32,google.protobuf.SourceContext> source_context_field = 6;
map<int32,google.protobuf.Struct> struct_field = 7;
map<int32,google.protobuf.Timestamp> timestamp_field = 8;
map<int32,google.protobuf.Type> type_field = 9;
map<int32,google.protobuf.DoubleValue> double_field = 10;
map<int32,google.protobuf.FloatValue> float_field = 11;
map<int32,google.protobuf.Int64Value> int64_field = 12;
map<int32,google.protobuf.UInt64Value> uint64_field = 13;
map<int32,google.protobuf.Int32Value> int32_field = 14;
map<int32,google.protobuf.UInt32Value> uint32_field = 15;
map<int32,google.protobuf.BoolValue> bool_field = 16;
map<int32,google.protobuf.StringValue> string_field = 17;
map<int32,google.protobuf.BytesValue> bytes_field = 18;
} }
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