Skip to content

Commit

Permalink
[feature request] add support for System.Collections.Immutable
Browse files Browse the repository at this point in the history
#4

- ImmutableArray
- ImmutableList
- ImmutableSortedSet
- ImmutableHashSet
- ImmutableDictionary
- ImmutableSortedDictionary
  • Loading branch information
Michael Catanzariti committed Aug 18, 2019
1 parent 77fbe4f commit 87c4da7
Show file tree
Hide file tree
Showing 9 changed files with 384 additions and 122 deletions.
126 changes: 126 additions & 0 deletions src/Dahomey.Cbor.Tests/ImmutableCollectionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Dahomey.Cbor.Tests
{
[TestClass]
public class ImmutableCollectionsTests
{
[TestMethod]
public void WriteImmutableArrayOfInt32()
{
ImmutableArray<int> value = ImmutableArray.CreateRange(new[] { 1, 2, 3 });
const string expectedHexBuffer = "83010203";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableArrayOfInt32()
{
ImmutableArray<int> expectedValue = ImmutableArray.CreateRange(new[] { 1, 2, 3 });
const string hexBuffer = "83010203";
Helper.TestRead(hexBuffer, expectedValue);
}

[TestMethod]
public void WriteImmutableListOfInt32()
{
ImmutableList<int> value = ImmutableList.CreateRange(new[] { 1, 2, 3 });
const string expectedHexBuffer = "83010203";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableListOfInt32()
{
ImmutableList<int> expectedValue = ImmutableList.CreateRange(new[] { 1, 2, 3 });
const string hexBuffer = "83010203";
Helper.TestRead(hexBuffer, expectedValue);
}

[TestMethod]
public void WriteImmutableHashSetOfInt32()
{
ImmutableHashSet<int> value = ImmutableHashSet.CreateRange(new[] { 1, 2, 3 });
const string expectedHexBuffer = "83010203";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableHashSetOfInt32()
{
ImmutableHashSet<int> expectedValue = ImmutableHashSet.CreateRange(new[] { 1, 2, 3 });
const string hexBuffer = "83010203";
Helper.TestRead(hexBuffer, expectedValue);
}

[TestMethod]
public void WriteImmutableSortedSetOfInt32()
{
ImmutableSortedSet<int> value = ImmutableSortedSet.CreateRange(new[] { 1, 2, 3 });
const string expectedHexBuffer = "83010203";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableSortedSetOfInt32()
{
ImmutableSortedSet<int> expectedValue = ImmutableSortedSet.CreateRange(new[] { 1, 2, 3 });
const string hexBuffer = "83010203";
Helper.TestRead(hexBuffer, expectedValue);
}

[TestMethod]
public void WriteImmutableDictionaryOfInt32()
{
ImmutableDictionary<int, int> value = ImmutableDictionary.CreateRange(new Dictionary<int, int>
{
{ 1, 1 },
{ 2, 2 },
{ 3, 3 }
});
const string expectedHexBuffer = "A3010102020303";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableDictionaryOfInt32()
{
ImmutableDictionary<int, int> expectedValue = ImmutableDictionary.CreateRange(new Dictionary<int, int>
{
{ 1, 1 },
{ 2, 2 },
{ 3, 3 }
});
const string hexBuffer = "A3010102020303";
Helper.TestRead(hexBuffer, expectedValue);
}

[TestMethod]
public void WriteImmutableSortedDictionaryOfInt32()
{
ImmutableSortedDictionary<int, int> value = ImmutableSortedDictionary.CreateRange(new SortedDictionary<int, int>
{
{ 1, 1 },
{ 2, 2 },
{ 3, 3 }
});
const string expectedHexBuffer = "A3010102020303";
Helper.TestWrite(value, expectedHexBuffer);
}

[TestMethod]
public void ReadImmutableSortedDictionaryOfInt32()
{
ImmutableSortedDictionary<int, int> expectedValue = ImmutableSortedDictionary.CreateRange(new SortedDictionary<int, int>
{
{ 1, 1 },
{ 2, 2 },
{ 3, 3 }
});
const string hexBuffer = "A3010102020303";
Helper.TestRead(hexBuffer, expectedValue);
}
}
}
1 change: 1 addition & 0 deletions src/Dahomey.Cbor/Dahomey.Cbor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

<ItemGroup>
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Collections.Immutable" Version="1.5.0" />
<PackageReference Include="System.Memory" Version="4.5.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.1" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System.Collections.Generic;

namespace Dahomey.Cbor.Serialization.Converters
{
public abstract class AbstractCollectionConverter<TC, TI> :
CborConverterBase<TC>,
ICborArrayReader<AbstractCollectionConverter<TC, TI>.ReaderContext>,
ICborArrayWriter<AbstractCollectionConverter<TC, TI>.WriterContext>
where TC : ICollection<TI>
{
public struct ReaderContext
{
public ICollection<TI> collection;
}

public struct WriterContext
{
public int count;
public IEnumerator<TI> enumerator;
}

private static readonly ICborConverter<TI> _itemConverter = CborConverter.Lookup<TI>();

protected abstract ICollection<TI> InstantiateTempCollection();
protected abstract TC InstantiateCollection(ICollection<TI> tempCollection);

public override TC Read(ref CborReader reader)
{
if (reader.ReadNull())
{
return default;
}

ReaderContext context = new ReaderContext();
reader.ReadArray(this, ref context);

return InstantiateCollection(context.collection);
}

public override void Write(ref CborWriter writer, TC value)
{
WriterContext context = new WriterContext
{
count = value.Count,
enumerator = value.GetEnumerator()
};
writer.WriteArray(this, ref context);
}

public void ReadBeginArray(int size, ref ReaderContext context)
{
context.collection = InstantiateTempCollection();

if (size != -1 && context.collection is List<TI> list)
{
list.Capacity = size;
}
}

public void ReadArrayItem(ref CborReader reader, ref ReaderContext context)
{
TI item = _itemConverter.Read(ref reader);
context.collection.Add(item);
}

public int GetArraySize(ref WriterContext context)
{
return context.count;
}

public void WriteArrayItem(ref CborWriter writer, ref WriterContext context)
{
if (context.enumerator.MoveNext())
{
_itemConverter.Write(ref writer, context.enumerator.Current);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Collections.Generic;

namespace Dahomey.Cbor.Serialization.Converters
{
public abstract class AbstractDictionaryConverter<TC, TK, TV> :
CborConverterBase<TC>,
ICborMapReader<AbstractDictionaryConverter<TC, TK, TV>.ReaderContext>,
ICborMapWriter<AbstractDictionaryConverter<TC, TK, TV>.WriterContext>
where TC : IDictionary<TK, TV>
{
public struct ReaderContext
{
public IDictionary<TK, TV> dict;
}

public struct WriterContext
{
public int count;
public IEnumerator<KeyValuePair<TK, TV>> enumerator;
}

private static readonly ICborConverter<TK> _keyConverter = CborConverter.Lookup<TK>();
private static readonly ICborConverter<TV> _valueConverter = CborConverter.Lookup<TV>();

protected abstract IDictionary<TK, TV> InstantiateTempCollection();
protected abstract TC InstantiateCollection(IDictionary<TK, TV> tempCollection);

public override TC Read(ref CborReader reader)
{
if (reader.ReadNull())
{
return default;
}

ReaderContext context = new ReaderContext();
reader.ReadMap(this, ref context);
return InstantiateCollection(context.dict);
}

public override void Write(ref CborWriter writer, TC value)
{
WriterContext context = new WriterContext
{
count = value.Count,
enumerator = value.GetEnumerator()
};
writer.WriteMap(this, ref context);
}

public void ReadBeginMap(int size, ref ReaderContext context)
{
context.dict = InstantiateTempCollection();
}

public void ReadMapItem(ref CborReader reader, ref ReaderContext context)
{
TK key = _keyConverter.Read(ref reader);
TV value = _valueConverter.Read(ref reader);

context.dict.Add(key, value);
}

public int GetMapSize(ref WriterContext context)
{
return context.count;
}

public void WriteMapItem(ref CborWriter writer, ref WriterContext context)
{
if (context.enumerator.MoveNext())
{
_keyConverter.Write(ref writer, context.enumerator.Current.Key);
_valueConverter.Write(ref writer, context.enumerator.Current.Value);
}
}
}
}
19 changes: 19 additions & 0 deletions src/Dahomey.Cbor/Serialization/Converters/CborConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;

Expand Down Expand Up @@ -139,13 +140,31 @@ private static ICborConverter Instantiate(Type type)
}
if (type.IsGenericType)
{
if (type.GetGenericTypeDefinition() == typeof(ImmutableDictionary<,>)
|| type.GetGenericTypeDefinition() == typeof(ImmutableSortedDictionary<,>))
{
Type keyType = type.GetGenericArguments()[0];
Type valueType = type.GetGenericArguments()[1];
return (ICborConverter)Activator.CreateInstance(typeof(ImmutableDictionaryConverter<,,>).MakeGenericType(type, keyType, valueType));
}

if (type.GetInterfaces()
.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
Type keyType = type.GetGenericArguments()[0];
Type valueType = type.GetGenericArguments()[1];
return (ICborConverter)Activator.CreateInstance(typeof(DictionaryConverter<,,>).MakeGenericType(type, keyType, valueType));
}

if (type.GetGenericTypeDefinition() == typeof(ImmutableArray<>)
|| type.GetGenericTypeDefinition() == typeof(ImmutableList<>)
|| type.GetGenericTypeDefinition() == typeof(ImmutableSortedSet<>)
|| type.GetGenericTypeDefinition() == typeof(ImmutableHashSet<>))
{
Type itemType = type.GetGenericArguments()[0];
return (ICborConverter)Activator.CreateInstance(typeof(ImmutableCollectionConverter<,>).MakeGenericType(type, itemType));
}

if (type.GetInterfaces()
.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(ICollection<>)))
{
Expand Down

0 comments on commit 87c4da7

Please sign in to comment.