First pass at map support.
More tests required. Generated code in next commit.
This commit is contained in:
parent
0698aa9737
commit
0d684d3420
@ -19,6 +19,7 @@ set(libprotoc_files
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_field_base.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_generator.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_helpers.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_map_field.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_message.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_message_field.cc
|
||||
${protobuf_source_dir}/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
|
||||
|
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using Google.Protobuf.TestProtos;
|
||||
using NUnit.Framework;
|
||||
|
||||
@ -146,6 +148,65 @@ namespace Google.Protobuf
|
||||
Assert.AreEqual(message, parsed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RoundTrip_Maps()
|
||||
{
|
||||
var message = new TestAllTypes
|
||||
{
|
||||
MapBoolToEnum = {
|
||||
{ false, TestAllTypes.Types.NestedEnum.BAR},
|
||||
{ true, TestAllTypes.Types.NestedEnum.BAZ}
|
||||
},
|
||||
MapInt32ToBytes = {
|
||||
{ 5, ByteString.CopyFrom(6, 7, 8) },
|
||||
{ 25, ByteString.CopyFrom(1, 2, 3, 4, 5) },
|
||||
{ 10, ByteString.Empty }
|
||||
},
|
||||
MapStringToNestedMessage = {
|
||||
{ "", new TestAllTypes.Types.NestedMessage { Bb = 10 } },
|
||||
{ "null value", null },
|
||||
}
|
||||
};
|
||||
|
||||
byte[] bytes = message.ToByteArray();
|
||||
TestAllTypes parsed = TestAllTypes.Parser.ParseFrom(bytes);
|
||||
Assert.AreEqual(message, parsed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapWithEmptyEntry()
|
||||
{
|
||||
var message = new TestAllTypes
|
||||
{
|
||||
MapInt32ToBytes = { { 0, ByteString.Empty } }
|
||||
};
|
||||
|
||||
byte[] bytes = message.ToByteArray();
|
||||
Assert.AreEqual(3, bytes.Length); // Tag for field entry (2 bytes), length of entry (0; 1 byte)
|
||||
|
||||
var parsed = TestAllTypes.Parser.ParseFrom(bytes);
|
||||
Assert.AreEqual(1, parsed.MapInt32ToBytes.Count);
|
||||
Assert.AreEqual(ByteString.Empty, parsed.MapInt32ToBytes[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MapWithOnlyValue()
|
||||
{
|
||||
// Hand-craft the stream to contain a single entry with just a value.
|
||||
var memoryStream = new MemoryStream();
|
||||
var output = CodedOutputStream.CreateInstance(memoryStream);
|
||||
output.WriteTag(TestAllTypes.MapStringToNestedMessageFieldNumber, WireFormat.WireType.LengthDelimited);
|
||||
var nestedMessage = new TestAllTypes.Types.NestedMessage { Bb = 20 };
|
||||
// Size of the entry (tag, size written by WriteMessage, data written by WriteMessage)
|
||||
output.WriteRawVarint32((uint)(nestedMessage.CalculateSize() + 3));
|
||||
output.WriteTag(2, WireFormat.WireType.LengthDelimited);
|
||||
output.WriteMessage(nestedMessage);
|
||||
output.Flush();
|
||||
|
||||
var parsed = TestAllTypes.Parser.ParseFrom(memoryStream.ToArray());
|
||||
Assert.AreEqual(nestedMessage, parsed.MapStringToNestedMessage[""]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CloneSingleNonMessageValues()
|
||||
{
|
||||
|
@ -475,6 +475,14 @@ namespace Google.Protobuf
|
||||
WriteRawVarint32(WireFormat.MakeTag(fieldNumber, type));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an already-encoded tag.
|
||||
/// </summary>
|
||||
public void WriteTag(uint tag)
|
||||
{
|
||||
WriteRawVarint32(tag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the given single-byte tag directly to the stream.
|
||||
/// </summary>
|
||||
|
400
csharp/src/ProtocolBuffers/Collections/MapField.cs
Normal file
400
csharp/src/ProtocolBuffers/Collections/MapField.cs
Normal file
@ -0,0 +1,400 @@
|
||||
#region Copyright notice and license
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Google.Protobuf.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Representation of a map field in a Protocol Buffer message.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This implementation preserves insertion order for simplicity of testing
|
||||
/// code using maps fields. Overwriting an existing entry does not change the
|
||||
/// position of that entry within the map. Equality is not order-sensitive.
|
||||
/// For string keys, the equality comparison is provided by <see cref="StringComparer.Ordinal"/>.
|
||||
/// </remarks>
|
||||
/// <typeparam name="TKey">Key type in the map. Must be a type supported by Protocol Buffer map keys.</typeparam>
|
||||
/// <typeparam name="TValue">Value type in the map. Must be a type supported by Protocol Buffers.</typeparam>
|
||||
public sealed class MapField<TKey, TValue> : IDeepCloneable<MapField<TKey, TValue>>, IFreezable, IDictionary<TKey, TValue>, IEquatable<MapField<TKey, TValue>>
|
||||
{
|
||||
// TODO: Don't create the map/list until we have an entry. (Assume many maps will be empty.)
|
||||
private bool frozen;
|
||||
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>>();
|
||||
|
||||
public MapField<TKey, TValue> Clone()
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nothing is cloneable, so we don't need to worry.
|
||||
clone.Add(this);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
ThrowHelper.ThrowIfNull(key, "key");
|
||||
this.CheckMutable();
|
||||
if (ContainsKey(key))
|
||||
{
|
||||
throw new ArgumentException("Key already exists in map", "key");
|
||||
}
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return map.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
this.CheckMutable();
|
||||
LinkedListNode<KeyValuePair<TKey, TValue>> node;
|
||||
if (map.TryGetValue(key, out node))
|
||||
{
|
||||
map.Remove(key);
|
||||
node.List.Remove(node);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
LinkedListNode<KeyValuePair<TKey, TValue>> node;
|
||||
if (map.TryGetValue(key, out node))
|
||||
{
|
||||
value = node.Value.Value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get
|
||||
{
|
||||
TValue value;
|
||||
if (TryGetValue(key, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set
|
||||
{
|
||||
this.CheckMutable();
|
||||
LinkedListNode<KeyValuePair<TKey, TValue>> node;
|
||||
var pair = new KeyValuePair<TKey, TValue>(key, value);
|
||||
if (map.TryGetValue(key, out node))
|
||||
{
|
||||
node.Value = pair;
|
||||
}
|
||||
else
|
||||
{
|
||||
node = list.AddLast(pair);
|
||||
map[key] = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make these views?
|
||||
public ICollection<TKey> Keys { get { return list.Select(t => t.Key).ToList(); } }
|
||||
public ICollection<TValue> Values { get { return list.Select(t => t.Value).ToList(); } }
|
||||
|
||||
public void Add(IDictionary<TKey, TValue> entries)
|
||||
{
|
||||
foreach (var pair in entries)
|
||||
{
|
||||
Add(pair);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return list.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
this.CheckMutable();
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
this.CheckMutable();
|
||||
list.Clear();
|
||||
map.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value)
|
||||
&& EqualityComparer<TValue>.Default.Equals(item.Value, value);
|
||||
}
|
||||
|
||||
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
list.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
this.CheckMutable();
|
||||
return Remove(item.Key);
|
||||
}
|
||||
|
||||
public int Count { get { return list.Count; } }
|
||||
public bool IsReadOnly { get { return frozen; } }
|
||||
|
||||
public void Freeze()
|
||||
{
|
||||
if (IsFrozen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
frozen = true;
|
||||
// Only values can be frozen, as all the key types are simple.
|
||||
// Everything can be done in-place, as we're just freezing objects.
|
||||
if (typeof(IFreezable).IsAssignableFrom(typeof(TValue)))
|
||||
{
|
||||
for (var node = list.First; node != null; node = node.Next)
|
||||
{
|
||||
var pair = node.Value;
|
||||
IFreezable freezableValue = pair.Value as IFreezable;
|
||||
if (freezableValue != null)
|
||||
{
|
||||
freezableValue.Freeze();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsFrozen { get { return frozen; } }
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
return Equals(other as MapField<TKey, TValue>);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var valueComparer = EqualityComparer<TValue>.Default;
|
||||
int hash = 0;
|
||||
foreach (var pair in list)
|
||||
{
|
||||
hash ^= pair.Key.GetHashCode() * 31 + valueComparer.GetHashCode(pair.Value);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
public bool Equals(MapField<TKey, TValue> other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (other == this)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (other.Count != this.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var valueComparer = EqualityComparer<TValue>.Default;
|
||||
foreach (var pair in this)
|
||||
{
|
||||
TValue value;
|
||||
if (!other.TryGetValue(pair.Key, out value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!valueComparer.Equals(value, pair.Value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void AddEntriesFrom(CodedInputStream input, Codec codec)
|
||||
{
|
||||
// TODO: Peek at the next tag and see if it's the same. If it is, we can reuse the entry object...
|
||||
var adapter = new Codec.MessageAdapter(codec);
|
||||
adapter.Reset();
|
||||
input.ReadMessage(adapter);
|
||||
this[adapter.Key] = adapter.Value;
|
||||
}
|
||||
|
||||
public void WriteTo(CodedOutputStream output, Codec codec)
|
||||
{
|
||||
var message = new Codec.MessageAdapter(codec);
|
||||
foreach (var entry in list)
|
||||
{
|
||||
message.Key = entry.Key;
|
||||
message.Value = entry.Value;
|
||||
output.WriteTag(codec.MapTag);
|
||||
output.WriteMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public int CalculateSize(Codec codec)
|
||||
{
|
||||
var message = new Codec.MessageAdapter(codec);
|
||||
int size = 0;
|
||||
foreach (var entry in list)
|
||||
{
|
||||
message.Key = entry.Key;
|
||||
message.Value = entry.Value;
|
||||
size += CodedOutputStream.ComputeRawVarint32Size(codec.MapTag);
|
||||
size += CodedOutputStream.ComputeMessageSize(message);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A codec for a specific map field. This contains all the information required to encoded and
|
||||
/// decode the nested messages.
|
||||
/// </summary>
|
||||
public sealed class Codec
|
||||
{
|
||||
private readonly FieldCodec<TKey> keyCodec;
|
||||
private readonly FieldCodec<TValue> valueCodec;
|
||||
private readonly uint mapTag;
|
||||
|
||||
public Codec(FieldCodec<TKey> keyCodec, FieldCodec<TValue> valueCodec, uint mapTag)
|
||||
{
|
||||
this.keyCodec = keyCodec;
|
||||
this.valueCodec = valueCodec;
|
||||
this.mapTag = mapTag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tag used in the enclosing message to indicate map entries.
|
||||
/// </summary>
|
||||
internal uint MapTag { get { return mapTag; } }
|
||||
|
||||
/// <summary>
|
||||
/// A mutable message class, used for parsing and serializing. This
|
||||
/// delegates the work to a codec, but implements the <see cref="IMessage"/> interface
|
||||
/// for interop with <see cref="CodedInputStream"/> and <see cref="CodedOutputStream"/>.
|
||||
/// This is nested inside Codec as it's tightly coupled to the associated codec,
|
||||
/// and it's simpler if it has direct access to all its fields.
|
||||
/// </summary>
|
||||
internal class MessageAdapter : IMessage
|
||||
{
|
||||
private readonly Codec codec;
|
||||
internal TKey Key { get; set; }
|
||||
internal TValue Value { get; set; }
|
||||
internal int Size { get; set; }
|
||||
|
||||
internal MessageAdapter(Codec codec)
|
||||
{
|
||||
this.codec = codec;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
Key = codec.keyCodec.DefaultValue;
|
||||
Value = codec.valueCodec.DefaultValue;
|
||||
}
|
||||
|
||||
public void MergeFrom(CodedInputStream input)
|
||||
{
|
||||
uint tag;
|
||||
while (input.ReadTag(out tag))
|
||||
{
|
||||
if (tag == 0)
|
||||
{
|
||||
throw InvalidProtocolBufferException.InvalidTag();
|
||||
}
|
||||
if (tag == codec.keyCodec.Tag)
|
||||
{
|
||||
Key = codec.keyCodec.Read(input);
|
||||
}
|
||||
else if (tag == codec.valueCodec.Tag)
|
||||
{
|
||||
Value = codec.valueCodec.Read(input);
|
||||
}
|
||||
else if (WireFormat.IsEndGroupTag(tag))
|
||||
{
|
||||
// TODO(jonskeet): Do we need this? (Given that we don't support groups...)
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTo(CodedOutputStream output)
|
||||
{
|
||||
codec.keyCodec.Write(output, Key);
|
||||
codec.valueCodec.Write(output, Value);
|
||||
}
|
||||
|
||||
public int CalculateSize()
|
||||
{
|
||||
return codec.keyCodec.CalculateSize(Key) + codec.valueCodec.CalculateSize(Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
178
csharp/src/ProtocolBuffers/FieldCodec.cs
Normal file
178
csharp/src/ProtocolBuffers/FieldCodec.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Google.Protobuf
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory methods for <see cref="FieldCodec{T}"/>.
|
||||
/// </summary>
|
||||
public static class FieldCodec
|
||||
{
|
||||
public static FieldCodec<string> ForString(uint tag)
|
||||
{
|
||||
return new FieldCodec<string>(input => input.ReadString(), (output, value) => output.WriteString(value), CodedOutputStream.ComputeStringSize, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<ByteString> ForBytes(uint tag)
|
||||
{
|
||||
return new FieldCodec<ByteString>(input => input.ReadBytes(), (output, value) => output.WriteBytes(value), CodedOutputStream.ComputeBytesSize, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<bool> ForBool(uint tag)
|
||||
{
|
||||
return new FieldCodec<bool>(input => input.ReadBool(), (output, value) => output.WriteBool(value), CodedOutputStream.ComputeBoolSize, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<int> ForInt32(uint tag)
|
||||
{
|
||||
return new FieldCodec<int>(input => input.ReadInt32(), (output, value) => output.WriteInt32(value), CodedOutputStream.ComputeInt32Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<int> ForSInt32(uint tag)
|
||||
{
|
||||
return new FieldCodec<int>(input => input.ReadSInt32(), (output, value) => output.WriteSInt32(value), CodedOutputStream.ComputeSInt32Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<uint> ForFixedInt32(uint tag)
|
||||
{
|
||||
return new FieldCodec<uint>(input => input.ReadFixed32(), (output, value) => output.WriteFixed32(value), CodedOutputStream.ComputeFixed32Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<uint> ForUInt32(uint tag)
|
||||
{
|
||||
return new FieldCodec<uint>(input => input.ReadUInt32(), (output, value) => output.WriteUInt32(value), CodedOutputStream.ComputeUInt32Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<long> ForInt64(uint tag)
|
||||
{
|
||||
return new FieldCodec<long>(input => input.ReadInt64(), (output, value) => output.WriteInt64(value), CodedOutputStream.ComputeInt64Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<long> ForSInt64(uint tag)
|
||||
{
|
||||
return new FieldCodec<long>(input => input.ReadSInt64(), (output, value) => output.WriteSInt64(value), CodedOutputStream.ComputeSInt64Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<ulong> ForFixedInt64(uint tag)
|
||||
{
|
||||
return new FieldCodec<ulong>(input => input.ReadFixed64(), (output, value) => output.WriteFixed64(value), CodedOutputStream.ComputeFixed64Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<ulong> ForUInt64(uint tag)
|
||||
{
|
||||
return new FieldCodec<ulong>(input => input.ReadUInt64(), (output, value) => output.WriteUInt64(value), CodedOutputStream.ComputeUInt64Size, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<float> ForFloat(uint tag)
|
||||
{
|
||||
return new FieldCodec<float>(input => input.ReadFloat(), (output, value) => output.WriteFloat(value), CodedOutputStream.ComputeFloatSize, tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<double> ForDouble(uint tag)
|
||||
{
|
||||
return new FieldCodec<double>(input => input.ReadFloat(), (output, value) => output.WriteDouble(value), CodedOutputStream.ComputeDoubleSize, tag);
|
||||
}
|
||||
|
||||
// Enums are tricky. We can probably use expression trees to build these delegates automatically,
|
||||
// but it's easy to generate the code fdor it.
|
||||
public static FieldCodec<T> ForEnum<T>(uint tag, Func<T, int> toInt32, Func<int, T> fromInt32)
|
||||
{
|
||||
return new FieldCodec<T>(input => fromInt32(
|
||||
input.ReadEnum()),
|
||||
(output, value) => output.WriteEnum(toInt32(value)),
|
||||
value => CodedOutputStream.ComputeEnumSize(toInt32(value)), tag);
|
||||
}
|
||||
|
||||
public static FieldCodec<T> ForMessage<T>(uint tag, MessageParser<T> parser) where T : IMessage<T>
|
||||
{
|
||||
return new FieldCodec<T>(input => { T message = parser.CreateTemplate(); input.ReadMessage(message); return message; },
|
||||
(output, value) => output.WriteMessage(value), message => CodedOutputStream.ComputeMessageSize(message), tag);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An encode/decode pair for a single field. This effectively encapsulates
|
||||
/// all the information needed to read or write the field value from/to a coded
|
||||
/// stream.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This never writes default values to the stream, and is not currently designed
|
||||
/// to play well with packed arrays.
|
||||
/// </remarks>
|
||||
public sealed class FieldCodec<T>
|
||||
{
|
||||
private static readonly Func<T, bool> IsDefault;
|
||||
private static readonly T Default;
|
||||
|
||||
static FieldCodec()
|
||||
{
|
||||
if (typeof(T) == typeof(string))
|
||||
{
|
||||
Default = (T)(object)"";
|
||||
IsDefault = CreateDefaultValueCheck<string>(x => x.Length == 0);
|
||||
}
|
||||
else if (typeof(T) == typeof(ByteString))
|
||||
{
|
||||
Default = (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)));
|
||||
}
|
||||
}
|
||||
|
||||
private static Func<T, bool> CreateDefaultValueCheck<TTmp>(Func<TTmp, bool> check)
|
||||
{
|
||||
return (Func<T, bool>)(object)check;
|
||||
}
|
||||
|
||||
private readonly Func<CodedInputStream, T> reader;
|
||||
private readonly Action<CodedOutputStream, T> writer;
|
||||
private readonly Func<T, int> sizeComputer;
|
||||
private readonly uint tag;
|
||||
private readonly int tagSize;
|
||||
|
||||
internal FieldCodec(
|
||||
Func<CodedInputStream, T> reader,
|
||||
Action<CodedOutputStream, T> writer,
|
||||
Func<T, int> sizeComputer,
|
||||
uint tag)
|
||||
{
|
||||
this.reader = reader;
|
||||
this.writer = writer;
|
||||
this.sizeComputer = sizeComputer;
|
||||
this.tag = tag;
|
||||
tagSize = CodedOutputStream.ComputeRawVarint32Size(tag);
|
||||
}
|
||||
|
||||
public uint Tag { get { return tag; } }
|
||||
|
||||
public T DefaultValue { get { return Default; } }
|
||||
|
||||
public void Write(CodedOutputStream output, T value)
|
||||
{
|
||||
if (!IsDefault(value))
|
||||
{
|
||||
output.WriteTag(tag);
|
||||
writer(output, value);
|
||||
}
|
||||
}
|
||||
|
||||
public T Read(CodedInputStream input)
|
||||
{
|
||||
return reader(input);
|
||||
}
|
||||
|
||||
public int CalculateSize(T value)
|
||||
{
|
||||
return IsDefault(value) ? 0 : sizeComputer(value) + CodedOutputStream.ComputeRawVarint32Size(tag);
|
||||
}
|
||||
}
|
||||
}
|
@ -60,6 +60,7 @@
|
||||
<Compile Include="CodedOutputStream.cs" />
|
||||
<Compile Include="Collections\Dictionaries.cs" />
|
||||
<Compile Include="Collections\Lists.cs" />
|
||||
<Compile Include="Collections\MapField.cs" />
|
||||
<Compile Include="Collections\ReadOnlyDictionary.cs" />
|
||||
<Compile Include="Collections\RepeatedField.cs" />
|
||||
<Compile Include="Collections\RepeatedFieldExtensions.cs" />
|
||||
@ -84,6 +85,7 @@
|
||||
<Compile Include="Descriptors\MethodDescriptor.cs" />
|
||||
<Compile Include="Descriptors\PackageDescriptor.cs" />
|
||||
<Compile Include="Descriptors\ServiceDescriptor.cs" />
|
||||
<Compile Include="FieldCodec.cs" />
|
||||
<Compile Include="FrameworkPortability.cs" />
|
||||
<Compile Include="Freezable.cs" />
|
||||
<Compile Include="MessageExtensions.cs" />
|
||||
|
@ -425,6 +425,8 @@ libprotoc_la_SOURCES = \
|
||||
google/protobuf/compiler/csharp/csharp_generator.cc \
|
||||
google/protobuf/compiler/csharp/csharp_helpers.cc \
|
||||
google/protobuf/compiler/csharp/csharp_helpers.h \
|
||||
google/protobuf/compiler/csharp/csharp_map_field.cc \
|
||||
google/protobuf/compiler/csharp/csharp_map_field.h \
|
||||
google/protobuf/compiler/csharp/csharp_message.cc \
|
||||
google/protobuf/compiler/csharp/csharp_message.h \
|
||||
google/protobuf/compiler/csharp/csharp_message_field.cc \
|
||||
|
@ -74,6 +74,12 @@ void EnumFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void EnumFieldGenerator::GenerateCodecCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"pb::FieldCodec.ForEnum($tag$, x => (int) x, x => ($type_name$) x)");
|
||||
}
|
||||
|
||||
EnumOneofFieldGenerator::EnumOneofFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int fieldOrdinal)
|
||||
: PrimitiveOneofFieldGenerator(descriptor, fieldOrdinal) {
|
||||
|
@ -46,6 +46,7 @@ class EnumFieldGenerator : public PrimitiveFieldGenerator {
|
||||
EnumFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
|
||||
~EnumFieldGenerator();
|
||||
|
||||
virtual void GenerateCodecCode(io::Printer* printer);
|
||||
virtual void GenerateParsingCode(io::Printer* printer);
|
||||
virtual void GenerateSerializationCode(io::Printer* printer);
|
||||
virtual void GenerateSerializedSizeCode(io::Printer* printer);
|
||||
|
@ -65,6 +65,7 @@ void FieldGeneratorBase::SetCommonFieldVariables(
|
||||
tag_bytes += ", " + SimpleItoa(tag_array[i]);
|
||||
}
|
||||
|
||||
(*variables)["tag"] = SimpleItoa(tag);
|
||||
(*variables)["tag_size"] = SimpleItoa(tag_size);
|
||||
(*variables)["tag_bytes"] = tag_bytes;
|
||||
|
||||
@ -112,6 +113,11 @@ void FieldGeneratorBase::GenerateFreezingCode(io::Printer* printer) {
|
||||
// special handling for freezing, so default to not generating any code.
|
||||
}
|
||||
|
||||
void FieldGeneratorBase::GenerateCodecCode(io::Printer* printer) {
|
||||
// No-op: expect this to be overridden by appropriate types.
|
||||
// Could fail if we get called here though...
|
||||
}
|
||||
|
||||
void FieldGeneratorBase::AddDeprecatedFlag(io::Printer* printer) {
|
||||
if (descriptor_->options().deprecated())
|
||||
{
|
||||
@ -151,12 +157,16 @@ std::string FieldGeneratorBase::name() {
|
||||
}
|
||||
|
||||
std::string FieldGeneratorBase::type_name() {
|
||||
switch (descriptor_->type()) {
|
||||
return type_name(descriptor_);
|
||||
}
|
||||
|
||||
std::string FieldGeneratorBase::type_name(const FieldDescriptor* descriptor) {
|
||||
switch (descriptor->type()) {
|
||||
case FieldDescriptor::TYPE_ENUM:
|
||||
return GetClassName(descriptor_->enum_type());
|
||||
return GetClassName(descriptor->enum_type());
|
||||
case FieldDescriptor::TYPE_MESSAGE:
|
||||
case FieldDescriptor::TYPE_GROUP:
|
||||
return GetClassName(descriptor_->message_type());
|
||||
return GetClassName(descriptor->message_type());
|
||||
case FieldDescriptor::TYPE_DOUBLE:
|
||||
return "double";
|
||||
case FieldDescriptor::TYPE_FLOAT:
|
||||
|
@ -49,6 +49,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
|
||||
|
||||
virtual void GenerateCloningCode(io::Printer* printer) = 0;
|
||||
virtual void GenerateFreezingCode(io::Printer* printer);
|
||||
virtual void GenerateCodecCode(io::Printer* printer);
|
||||
virtual void GenerateMembers(io::Printer* printer) = 0;
|
||||
virtual void GenerateMergingCode(io::Printer* printer) = 0;
|
||||
virtual void GenerateParsingCode(io::Printer* printer) = 0;
|
||||
@ -76,6 +77,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
|
||||
std::string property_name();
|
||||
std::string name();
|
||||
std::string type_name();
|
||||
std::string type_name(const FieldDescriptor* descriptor);
|
||||
bool has_default_value();
|
||||
bool is_nullable_type();
|
||||
std::string default_value();
|
||||
|
@ -46,6 +46,7 @@
|
||||
|
||||
#include <google/protobuf/compiler/csharp/csharp_field_base.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_enum_field.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_message_field.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_primitive_field.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_repeated_enum_field.h>
|
||||
@ -355,7 +356,11 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor,
|
||||
case FieldDescriptor::TYPE_GROUP:
|
||||
case FieldDescriptor::TYPE_MESSAGE:
|
||||
if (descriptor->is_repeated()) {
|
||||
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
|
||||
if (descriptor->is_map()) {
|
||||
return new MapFieldGenerator(descriptor, fieldOrdinal);
|
||||
} else {
|
||||
return new RepeatedMessageFieldGenerator(descriptor, fieldOrdinal);
|
||||
}
|
||||
} else {
|
||||
if (descriptor->containing_oneof()) {
|
||||
return new MessageOneofFieldGenerator(descriptor, fieldOrdinal);
|
||||
|
141
src/google/protobuf/compiler/csharp/csharp_map_field.cc
Normal file
141
src/google/protobuf/compiler/csharp/csharp_map_field.cc
Normal file
@ -0,0 +1,141 @@
|
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2015 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/stubs/strutil.h>
|
||||
|
||||
#include <google/protobuf/compiler/csharp/csharp_helpers.h>
|
||||
#include <google/protobuf/compiler/csharp/csharp_map_field.h>
|
||||
|
||||
namespace google {
|
||||
namespace protobuf {
|
||||
namespace compiler {
|
||||
namespace csharp {
|
||||
|
||||
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int fieldOrdinal)
|
||||
: FieldGeneratorBase(descriptor, fieldOrdinal) {
|
||||
}
|
||||
|
||||
MapFieldGenerator::~MapFieldGenerator() {
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateMembers(io::Printer* printer) {
|
||||
const FieldDescriptor* key_descriptor =
|
||||
descriptor_->message_type()->FindFieldByName("key");
|
||||
const FieldDescriptor* value_descriptor =
|
||||
descriptor_->message_type()->FindFieldByName("value");
|
||||
variables_["key_type_name"] = type_name(key_descriptor);
|
||||
variables_["value_type_name"] = type_name(value_descriptor);
|
||||
scoped_ptr<FieldGeneratorBase> key_generator(CreateFieldGenerator(key_descriptor, 1));
|
||||
scoped_ptr<FieldGeneratorBase> value_generator(CreateFieldGenerator(value_descriptor, 2));
|
||||
|
||||
printer->Print(
|
||||
variables_,
|
||||
"private static readonly pbc::MapField<$key_type_name$, $value_type_name$>.Codec _map_$name$_codec\n"
|
||||
" = new pbc::MapField<$key_type_name$, $value_type_name$>.Codec(");
|
||||
key_generator->GenerateCodecCode(printer);
|
||||
printer->Print(", ");
|
||||
value_generator->GenerateCodecCode(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$>();\n");
|
||||
AddDeprecatedFlag(printer);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"public pbc::MapField<$key_type_name$, $value_type_name$> $property_name$ {\n"
|
||||
" get { return $name$_; }\n"
|
||||
"}\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"$name$_.Add(other.$name$_);\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateParsingCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"$name$_.AddEntriesFrom(input, _map_$name$_codec);\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateSerializationCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"$name$_.WriteTo(output, _map_$name$_codec);\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"size += $name$_.CalculateSize(_map_$name$_codec);\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::WriteHash(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"hash ^= $property_name$.GetHashCode();\n");
|
||||
}
|
||||
void MapFieldGenerator::WriteEquals(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"if (!$property_name$.Equals(other.$property_name$)) return false;\n");
|
||||
}
|
||||
void MapFieldGenerator::WriteToString(io::Printer* printer) {
|
||||
/*
|
||||
variables_["field_name"] = GetFieldName(descriptor_);
|
||||
printer->Print(
|
||||
variables_,
|
||||
"PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");*/
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) {
|
||||
printer->Print(variables_,
|
||||
"$name$_ = other.$name$_.Clone();\n");
|
||||
}
|
||||
|
||||
void MapFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
|
||||
printer->Print(variables_,
|
||||
"$name$_.Freeze();\n");
|
||||
}
|
||||
|
||||
} // namespace csharp
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
71
src/google/protobuf/compiler/csharp/csharp_map_field.h
Normal file
71
src/google/protobuf/compiler/csharp/csharp_map_field.h
Normal file
@ -0,0 +1,71 @@
|
||||
// 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_MAP_FIELD_H__
|
||||
#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_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 MapFieldGenerator : public FieldGeneratorBase {
|
||||
public:
|
||||
MapFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
|
||||
~MapFieldGenerator();
|
||||
|
||||
virtual void GenerateCloningCode(io::Printer* printer);
|
||||
virtual void GenerateFreezingCode(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:
|
||||
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
|
||||
};
|
||||
|
||||
} // namespace csharp
|
||||
} // namespace compiler
|
||||
} // namespace protobuf
|
||||
} // namespace google
|
||||
|
||||
#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MAP_FIELD_H__
|
||||
|
@ -268,8 +268,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
|
||||
"}\n\n");
|
||||
}
|
||||
|
||||
// TODO(jonskeet): Map properties
|
||||
|
||||
// Standard methods
|
||||
GenerateFrameworkMethods(printer);
|
||||
GenerateMessageSerializationMethods(printer);
|
||||
@ -299,7 +297,6 @@ void MessageGenerator::Generate(io::Printer* printer) {
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
printer->Print("\n");
|
||||
|
||||
}
|
||||
|
||||
void MessageGenerator::GenerateCloningCode(io::Printer* printer) {
|
||||
@ -451,7 +448,7 @@ void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer)
|
||||
}
|
||||
printer->Print("return size;\n");
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
printer->Print("}\n\n");
|
||||
}
|
||||
|
||||
void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
|
||||
@ -469,7 +466,6 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) {
|
||||
"if (other == null) {\n"
|
||||
" return;\n"
|
||||
"}\n");
|
||||
// TODO(jonskeet): Maps?
|
||||
// Merge non-oneof fields
|
||||
for (int i = 0; i < descriptor_->field_count(); i++) {
|
||||
if (!descriptor_->field(i)->containing_oneof()) {
|
||||
|
@ -138,6 +138,12 @@ void MessageFieldGenerator::GenerateFreezingCode(io::Printer* printer) {
|
||||
"if ($has_property_check$) $property_name$.Freeze();\n");
|
||||
}
|
||||
|
||||
void MessageFieldGenerator::GenerateCodecCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"pb::FieldCodec.ForMessage($tag$, $type_name$.Parser)");
|
||||
}
|
||||
|
||||
MessageOneofFieldGenerator::MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
|
||||
int fieldOrdinal)
|
||||
: MessageFieldGenerator(descriptor, fieldOrdinal) {
|
||||
|
@ -46,6 +46,7 @@ class MessageFieldGenerator : public FieldGeneratorBase {
|
||||
MessageFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
|
||||
~MessageFieldGenerator();
|
||||
|
||||
virtual void GenerateCodecCode(io::Printer* printer);
|
||||
virtual void GenerateCloningCode(io::Printer* printer);
|
||||
virtual void GenerateFreezingCode(io::Printer* printer);
|
||||
virtual void GenerateMembers(io::Printer* printer);
|
||||
|
@ -129,7 +129,7 @@ void PrimitiveFieldGenerator::GenerateSerializedSizeCode(io::Printer* printer) {
|
||||
"size += $tag_size$ + $fixed_size$;\n",
|
||||
"fixed_size", SimpleItoa(fixedSize),
|
||||
"tag_size", variables_["tag_size"]);
|
||||
}
|
||||
}
|
||||
printer->Outdent();
|
||||
printer->Print("}\n");
|
||||
}
|
||||
@ -155,6 +155,12 @@ void PrimitiveFieldGenerator::GenerateCloningCode(io::Printer* printer) {
|
||||
"$name$_ = other.$name$_;\n");
|
||||
}
|
||||
|
||||
void PrimitiveFieldGenerator::GenerateCodecCode(io::Printer* printer) {
|
||||
printer->Print(
|
||||
variables_,
|
||||
"pb::FieldCodec.For$capitalized_type_name$($tag$)");
|
||||
}
|
||||
|
||||
PrimitiveOneofFieldGenerator::PrimitiveOneofFieldGenerator(
|
||||
const FieldDescriptor* descriptor, int fieldOrdinal)
|
||||
: PrimitiveFieldGenerator(descriptor, fieldOrdinal) {
|
||||
|
@ -46,6 +46,7 @@ class PrimitiveFieldGenerator : public FieldGeneratorBase {
|
||||
PrimitiveFieldGenerator(const FieldDescriptor* descriptor, int fieldOrdinal);
|
||||
~PrimitiveFieldGenerator();
|
||||
|
||||
virtual void GenerateCodecCode(io::Printer* printer);
|
||||
virtual void GenerateCloningCode(io::Printer* printer);
|
||||
virtual void GenerateMembers(io::Printer* printer);
|
||||
virtual void GenerateMergingCode(io::Printer* printer);
|
||||
|
@ -140,6 +140,11 @@ message TestAllTypes {
|
||||
string oneof_string = 113;
|
||||
bytes oneof_bytes = 114;
|
||||
}
|
||||
|
||||
// Sample maps
|
||||
map<string, NestedMessage> map_string_to_nested_message = 200;
|
||||
map<int32, bytes> map_int32_to_bytes = 201;
|
||||
map<bool, NestedEnum> map_bool_to_enum = 202;
|
||||
}
|
||||
|
||||
// This proto includes a recusively nested message.
|
||||
|
Loading…
Reference in New Issue
Block a user