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

First pass at map support.

More tests required. Generated code in next commit.
parent 0698aa97
......@@ -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
......
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>
......
This diff is collapsed.
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);
......
// 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
// 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.
......
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