Skip to content

Commit

Permalink
Merge branch 'compact-binary-v2'
Browse files Browse the repository at this point in the history
Adds support for Compact Binary v2 in C#.
Closes #70.
  • Loading branch information
chwarr committed Apr 28, 2016
2 parents eb19aef + c8aa255 commit e80c126
Show file tree
Hide file tree
Showing 16 changed files with 1,081 additions and 36 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ tag versions. The Bond compiler (`gbc`) and
different versioning scheme, following the Haskell community's
[package versioning policy](https://wiki.haskell.org/Package_versioning_policy).

## Yet to be released ##

* `gbc` & compiler library: TBD
* C# NuGet version: TBD

### C# ###

* Add support for Compact Binary v2 writing.
[Issue #70](https://github.com/Microsoft/bond/issues/70)

## 4.1.0: 2016-04-22

* `gbc` & compiler library: 0.4.0.2
Expand Down
12 changes: 2 additions & 10 deletions cpp/test/compat/compat.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,10 @@ if (${TEST} STREQUAL schema)
endif()

if (CSHARP_COMPAT)
set (TEST_CS ${TEST})

# C# implements reading of Compact Binary v2 but not writing
if (${TEST_CS} STREQUAL compact2)
set (TEST_CS compact)
endif()

run (${CSHARP_COMPAT} ${TEST} ${COMPAT_DATA}/compat.${TEST}.dat compat.${TEST}.cs.dat ${TEST_CS})
run (${BOND_COMPAT} ${TEST_CS} -d compat.${TEST}.cs.dat expected.cs.${TEST} deserialized.cs.${TEST})
run (${CSHARP_COMPAT} ${TEST} ${COMPAT_DATA}/compat.${TEST}.dat compat.${TEST}.cs.dat ${TEST})
run (${BOND_COMPAT} ${TEST} -d compat.${TEST}.cs.dat expected.cs.${TEST} deserialized.cs.${TEST})

if (${TEST} STREQUAL schema)
run (${CSHARP_COMPAT} ${TEST} compat.Compat.json compat.${TEST}.gbc.dat)
endif()
endif()

14 changes: 14 additions & 0 deletions cs/src/attributes/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,18 @@ public SerializerAttribute(Type type)

internal Type Type { get; private set; }
}

/// <summary>
/// Applied to 2-pass protocol writers to indicate the implementation of IProtocolWriter used to generate the first-pass serializer
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)]
public sealed class FirstPassWriterAttribute : Attribute
{
public FirstPassWriterAttribute(Type type)
{
Type = type;
}

internal Type Type { get; private set; }
}
}
1 change: 1 addition & 0 deletions cs/src/core/Bond.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<Compile Include="properties\AssemblyInfo.cs" />
<Compile Include="Property.cs" />
<Compile Include="protocols\CompactBinary.cs" />
<Compile Include="protocols\CompactBinaryCounter.cs" />
<Compile Include="protocols\Exceptions.cs" />
<Compile Include="protocols\FastBinary.cs" />
<Compile Include="protocols\IProtocolWriter.cs" />
Expand Down
77 changes: 71 additions & 6 deletions cs/src/core/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ namespace Bond
{
using System;
using System.Linq;
using System.Threading;

using Bond.Expressions;
using Bond.Protocols;

/// <summary>
/// Serialize objects
Expand Down Expand Up @@ -60,7 +62,26 @@ public static void To<W>(W writer, IBonded bonded)
/// <typeparam name="W">Protocol writer</typeparam>
public class Serializer<W>
{
readonly Action<object, W>[] serialize;
static readonly Type helperType;
readonly SerializerHelper helper;

static Serializer()
{
var firstPassAttribute = typeof(W).GetAttribute<FirstPassWriterAttribute>();
if (firstPassAttribute != null)
{
if (!typeof(ITwoPassProtocolWriter).IsAssignableFrom(typeof(W)))
{
throw new ArgumentException("Writers with FirstPassWriterAttribute must implement ITwoPassProtocolWriter");
}

helperType = typeof(TwoPassSerializerHelper<>).MakeGenericType(typeof(W), firstPassAttribute.Type);
}
else
{
helperType = typeof(SerializerHelper);
}
}

/// <summary>
/// Create a serializer for specified type
Expand Down Expand Up @@ -91,10 +112,9 @@ public Serializer(Type type, bool inlineNested) : this(type, null, inlineNested)
public Serializer(Type type, IParser parser, bool inlineNested)
{
parser = parser ?? new ObjectParser(type);
serialize = SerializerGeneratorFactory<object, W>.Create(
(o, w, i) => serialize[i](o, w), type, inlineNested)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();

helper = (SerializerHelper)Activator.CreateInstance(helperType, parser, type, inlineNested);

}

/// <summary>
Expand All @@ -107,7 +127,52 @@ public Serializer(Type type, IParser parser, bool inlineNested)
/// </remarks>
public void Serialize(object obj, W writer)
{
serialize[0](obj, writer);
helper.Serialize(obj, writer);
}

class SerializerHelper
{
readonly Action<object, W>[] serialize;

public SerializerHelper(ObjectParser parser, Type type, bool inlineNested)
{
serialize = SerializerGeneratorFactory<object, W>.Create(
(o, w, i) => serialize[i](o, w), type, inlineNested)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
}

public virtual void Serialize(object obj, W writer)
{
serialize[0](obj, writer);
}
}

class TwoPassSerializerHelper<FPW> : SerializerHelper
{
readonly Lazy<Action<object, FPW>[]> firstPassSerialize;

public TwoPassSerializerHelper(ObjectParser parser, Type type, bool inlineNested) :
base(parser, type, inlineNested)
{
firstPassSerialize = new Lazy<Action<object, FPW>[]>(() => {
return SerializerGeneratorFactory<object, FPW>.Create(
(o, w, i) => firstPassSerialize.Value[i](o, w), type, inlineNested)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
}, LazyThreadSafetyMode.PublicationOnly);
}

public override void Serialize(object obj, W writer)
{
var firstPassWriter = ((ITwoPassProtocolWriter)writer).GetFirstPassWriter();
if (firstPassWriter != null)
{
firstPassSerialize.Value[0](obj, (FPW)firstPassWriter);
}

base.Serialize(obj, writer);
}
}
}
}
105 changes: 95 additions & 10 deletions cs/src/core/Transcoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ namespace Bond
{
using System;
using System.Linq;
using System.Threading;
using Bond.Expressions;
using Bond.IO;
using Bond.Protocols;

/// <summary>
/// Transcode payload from one protocol into another
Expand Down Expand Up @@ -61,7 +64,26 @@ public static void FromTo<R, W>(R reader, W writer)
/// <typeparam name="W">Protocol writer</typeparam>
public class Transcoder<R, W>
{
readonly Action<R, W>[] transcode;
static readonly Type helperType;
readonly TranscoderHelper helper;

static Transcoder()
{
var firstPassAttribute = typeof(W).GetAttribute<FirstPassWriterAttribute>();
if (firstPassAttribute != null)
{
if (!typeof(ITwoPassProtocolWriter).IsAssignableFrom(typeof(W)))
{
throw new ArgumentException("Writers with FirstPassWriterAttribute must implement ITwoPassProtocolWriter");
}

helperType = typeof(TwoPassTranscoderHelper<>).MakeGenericType(typeof(R), typeof(W), firstPassAttribute.Type);
}
else
{
helperType = typeof(TranscoderHelper);
}
}

/// <summary>
/// Create a transcoder for payloads with specified runtime schema
Expand All @@ -86,7 +108,7 @@ public Transcoder(Type type)
/// <param name="parser">Custom <see cref="IParser"/> instance</param>
public Transcoder(RuntimeSchema schema, IParser parser)
{
transcode = Generate(schema, parser);
helper = (TranscoderHelper)Activator.CreateInstance(helperType, schema, parser);
}

/// <summary>
Expand All @@ -96,7 +118,7 @@ public Transcoder(RuntimeSchema schema, IParser parser)
/// <param name="parser">Custom <see cref="IParser"/> instance</param>
public Transcoder(Type type, IParser parser)
{
transcode = Generate(type, parser);
helper = (TranscoderHelper)Activator.CreateInstance(helperType, type, parser);
}

// Create a transcoder
Expand All @@ -111,16 +133,79 @@ public Transcoder()
/// <param name="writer">Writer instance</param>
public void Transcode(R reader, W writer)
{
transcode[0](reader, writer);
helper.Transcode(reader, writer);
}

Action<R, W>[] Generate<S>(S schema, IParser parser)
class TranscoderHelper
{
parser = parser ?? ParserFactory<R>.Create(schema);
return SerializerGeneratorFactory<R, W>.Create(
(r, w, i) => transcode[i](r, w), schema)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
readonly Action<R, W>[] transcode;

public TranscoderHelper(RuntimeSchema schema, IParser parser)
{
transcode = Generate(schema, parser);
}

public TranscoderHelper(Type type, IParser parser)
{
transcode = Generate(type, parser);
}

public virtual void Transcode(R reader, W writer)
{
transcode[0](reader, writer);
}

Action<R, W>[] Generate<S>(S schema, IParser parser)
{
parser = parser ?? ParserFactory<R>.Create(schema);
return SerializerGeneratorFactory<R, W>.Create(
(r, w, i) => transcode[i](r, w), schema)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
}
}

class TwoPassTranscoderHelper<FPW> : TranscoderHelper
{
readonly Lazy<Action<R, FPW>[]> firstPassTranscode;

public TwoPassTranscoderHelper(RuntimeSchema schema, IParser parser):
base(schema, parser)
{
firstPassTranscode = new Lazy<Action<R, FPW>[]>(() => GenerateFirstPass(schema, parser), LazyThreadSafetyMode.PublicationOnly);
}

public TwoPassTranscoderHelper(Type type, IParser parser):
base(type, parser)
{
firstPassTranscode = new Lazy<Action<R, FPW>[]>(() => GenerateFirstPass(type, parser), LazyThreadSafetyMode.PublicationOnly);
}

public override void Transcode(R reader, W writer)
{
var firstPassWriter = ((ITwoPassProtocolWriter)writer).GetFirstPassWriter();
if (firstPassWriter != null)
{
if (!typeof(ICloneable<R>).IsAssignableFrom(typeof(R)))
{
throw new ArgumentException("Two-pass transcoding requires a reader that implements ICloneable");
}

R clonedReader = ((ICloneable<R>)reader).Clone();
firstPassTranscode.Value[0](clonedReader, (FPW)firstPassWriter);
}

base.Transcode(reader, writer);
}

Action<R, FPW>[] GenerateFirstPass<S>(S schema, IParser parser)
{
parser = parser ?? ParserFactory<R>.Create(schema);
return SerializerGeneratorFactory<R, FPW>.Create(
(r, w, i) => firstPassTranscode.Value[i](r, w), schema)
.Generate(parser)
.Select(lambda => lambda.Compile()).ToArray();
}
}
}
}
Loading

0 comments on commit e80c126

Please sign in to comment.