Skip to content

Commit

Permalink
Improve type checks (#347)
Browse files Browse the repository at this point in the history
  • Loading branch information
CoryCharlton authored Sep 30, 2024
1 parent 0df6475 commit efddd3f
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public abstract class BaseIterationBenchmark

public void RunInIteration(Action methodToRun)
{
for (int i = 0; i < IterationCount; i++)
for (var i = 0; i < IterationCount; i++)
{
methodToRun();
}
Expand Down
13 changes: 10 additions & 3 deletions nanoFramework.Json.Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@
// See LICENSE file in the project root for full license information.
//

using System;
using nanoFramework.Benchmark;
using System.Diagnostics;
using System.Threading;
using nanoFramework.Json.Benchmark.DeserializationBenchmarks;
using nanoFramework.Json.Benchmark.SerializationBenchmarks;

namespace nanoFramework.Json.Benchmark
{
public static class Program
{
public static void Main()
{
Debug.WriteLine("Hello from nanoFramework JSON benchmark!");
BenchmarkRunner.Run(typeof(IAssemblyHandler).Assembly);
Console.WriteLine("********** Starting benchmarks **********");
BenchmarkRunner.RunClass(typeof(ReferenceTypesDeserializationBenchmark));
BenchmarkRunner.RunClass(typeof(ValueTypesDeserializationBenchmark));
BenchmarkRunner.RunClass(typeof(ReferenceTypesSerializationBenchmark));
BenchmarkRunner.RunClass(typeof(ValueTypesSerializationBenchmark));
BenchmarkRunner.RunClass(typeof(TypeBenchmarks));
Console.WriteLine("********** Completed benchmarks **********");
Thread.Sleep(Timeout.Infinite);
}
}
Expand Down
71 changes: 71 additions & 0 deletions nanoFramework.Json.Benchmark/TypeBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//

using System;
using System.Collections;
using nanoFramework.Benchmark;
using nanoFramework.Benchmark.Attributes;
using nanoFramework.Json.Benchmark.Base;

namespace nanoFramework.Json.Benchmark
{
[IterationCount(100)]
public class TypeBenchmarks: BaseIterationBenchmark
{
protected override int IterationCount => 200;

private readonly ArrayList _list = new();
private const string ArrayListFullName = "System.Collections.ArrayList";
private static readonly Type ArrayListType = typeof(ArrayList);

[Benchmark]
public void Benchmark_FullName_Comparison()
{
RunInIteration(() =>
{
if (!ArrayListFullName.Equals(_list.GetType().FullName))
{
throw new ApplicationException();
}
});
}

[Benchmark]
public void Benchmark_Type_Comparison()
{
RunInIteration(() =>
{
if (_list.GetType() != typeof(ArrayList))
{
throw new ApplicationException();
}
});
}

[Benchmark]
public void Benchmark_Type_Comparison_Static()
{
RunInIteration(() =>
{
if (_list.GetType() != ArrayListType)
{
throw new ApplicationException();
}
});
}

[Benchmark]
public void Benchmark_TypeUtils_Comparison()
{
RunInIteration(() =>
{
if (!TypeUtils.IsArrayList(_list.GetType()))
{
throw new ApplicationException();
}
});
}
}
}
10 changes: 10 additions & 0 deletions nanoFramework.Json.Benchmark/nanoFramework.Json.Benchmark.nfproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
<RestoreLockedMode Condition="'$(TF_BUILD)' == 'True' or '$(ContinuousIntegrationBuild)' == 'True'">true</RestoreLockedMode>
<CodeAnalysisRuleSet>..\.sonarlint\nanoframework_lib-nanoframework.jsoncsharp.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>..\key.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup>
<DelaySign>false</DelaySign>
</PropertyGroup>
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
<ItemGroup>
<Compile Include="Base\BaseIterationBenchmark.cs" />
Expand All @@ -29,6 +38,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="DeserializationBenchmarks\ReferenceTypesDeserializationBenchmark.cs" />
<Compile Include="SerializationBenchmarks\ValueTypesSerializationBenchmark.cs" />
<Compile Include="TypeBenchmarks.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="mscorlib, Version=1.15.6.0, Culture=neutral, PublicKeyToken=c07d481e9758c731">
Expand Down
37 changes: 14 additions & 23 deletions nanoFramework.Json/JsonConvert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//

using nanoFramework.Json.Configuration;
using nanoFramework.Json.Converters;
using System;
using System.Collections;
using System.IO;
Expand Down Expand Up @@ -61,7 +60,7 @@ public static object DeserializeObject(string value, Type type) =>
public static object DeserializeObject(string value, Type type, JsonSerializerOptions options)
{
// Short circuit populating the object when target type is string
if (type == typeof(string))
if (TypeUtils.IsString(type))
{
var converter = ConvertersMapping.GetConverter(type);
return converter.ToType(value);
Expand Down Expand Up @@ -160,12 +159,11 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
throw new DeserializationException();
}

Type rootElementType = rootType.GetElementType();
var rootElementType = rootType.GetElementType();

if (rootToken is JsonObject rootObject)
{
if (rootElementType == null
&& rootType.FullName == "System.Collections.Hashtable")
if (rootElementType is null && TypeUtils.IsHashTable(rootType))
{
Hashtable rootInstanceHashtable = new();

Expand Down Expand Up @@ -194,8 +192,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
return rootInstanceHashtable;
}

if (rootElementType == null
&& rootType.FullName == "System.Collections.ArrayList")
if (rootElementType is null && TypeUtils.IsArrayList(rootType))
{
ArrayList rootArrayList = new();

Expand Down Expand Up @@ -306,7 +303,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string

// check if property type it's HashTable
// whole if can be replaced with memberObject = PopulateObject(memberProperty.Value, memberType, memberPath);??
if (memberResolver.ObjectType.FullName == "System.Collections.Hashtable")
if (TypeUtils.IsHashTable(memberResolver.ObjectType))
{
Hashtable table = new();

Expand Down Expand Up @@ -347,7 +344,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
Type memberElementType = memberResolver.ObjectType.GetElementType();
bool isArrayList = false;

if (memberElementType == null && memberResolver.ObjectType.FullName == "System.Collections.ArrayList")
if (memberElementType is null && TypeUtils.IsArrayList(memberResolver.ObjectType))
{
memberElementType = memberResolver.ObjectType;
isArrayList = true;
Expand Down Expand Up @@ -395,19 +392,18 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
{
ArrayList targetArray = new();

for (int i = 0; i < memberValueArrayList.Count; i++)
for (var i = 0; i < memberValueArrayList.Count; i++)
{
var item = memberValueArrayList[i];
// Test if we have only 1 element and that the element is a Hashtable.
// In this case, we'll make it more efficient and add it as an Hashtable.
if ((memberValueArrayList[i].GetType() == typeof(ArrayList)) &&
(((ArrayList)memberValueArrayList[i]).Count == 1) &&
((ArrayList)memberValueArrayList[i])[0].GetType() == typeof(Hashtable))
if (item is ArrayList { Count: 1 } itemArrayList && itemArrayList[0] is Hashtable elementHashTable)
{
targetArray.Add(((ArrayList)memberValueArrayList[i])[0]);
targetArray.Add(elementHashTable);
}
else
{
targetArray.Add(memberValueArrayList[i]);
targetArray.Add(item);
}
}

Expand Down Expand Up @@ -444,9 +440,7 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
if (rootElementType == null)
{
// check if this is an ArrayList
if (rootType.FullName == "System.Collections.ArrayList"
|| rootType.BaseType.FullName == "System.ValueType"
|| rootType.FullName == "System.String")
if (TypeUtils.IsArrayList(rootType) || TypeUtils.IsString(rootType) || TypeUtils.IsValueType(rootType))
{
isArrayList = true;

Expand Down Expand Up @@ -481,12 +475,9 @@ private static object PopulateObject(JsonToken rootToken, Type rootType, string
}
}

if ((rootType.BaseType.FullName == "System.ValueType"
|| rootType.FullName == "System.String")
&& rootArrayList.Count == 1)
if ((TypeUtils.IsString(rootType) || TypeUtils.IsValueType(rootType)) && rootArrayList.Count == 1)
{
// this is a case of deserialing a array with a single element,
// so just return the element
// This is a case of deserializing an array with a single element, so just return the element
return rootArrayList[0];
}

Expand Down
15 changes: 5 additions & 10 deletions nanoFramework.Json/JsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//

using nanoFramework.Json.Configuration;
using nanoFramework.Json.Converters;
using System;
using System.Collections;
using System.Reflection;
Expand All @@ -17,7 +16,7 @@ namespace nanoFramework.Json
/// </summary>
public class JsonSerializer
{
JsonSerializer()
private JsonSerializer()
{
}

Expand All @@ -35,11 +34,9 @@ public static string SerializeObject(object o, bool topObject = true)
return "null";
}

Type type = o.GetType();
var type = o.GetType();

if (topObject
&& !type.IsArray
&& type.BaseType.FullName == "System.ValueType")
if (topObject && !type.IsArray && TypeUtils.IsValueType(type))
{
return $"[{SerializeObject(o, false)}]";
}
Expand All @@ -55,15 +52,13 @@ public static string SerializeObject(object o, bool topObject = true)
return o.ToString();
}

if (o is IDictionary && !type.IsArray)
if (o is IDictionary dictionary && !type.IsArray)
{
IDictionary dictionary = o as IDictionary;
return SerializeIDictionary(dictionary);
}

if (o is IEnumerable)
if (o is IEnumerable enumerable)
{
IEnumerable enumerable = o as IEnumerable;
return SerializeIEnumerable(enumerable);
}

Expand Down
6 changes: 6 additions & 0 deletions nanoFramework.Json/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@
"c174a5f8e78a9c0b6087d3aef373d7d0f3d9be67700fc2a5a38de1fb71b5b6f6046d841ff35abe" +
"e2e0b0840a6291a312be184eb311baff5fef0ff6895b9a5f2253aed32fb06b819134f6bb9d5314" +
"88a87ea2")]

[assembly: InternalsVisibleTo("nanoFramework.Json.Benchmark, PublicKey=" + "00240000048000009400000006020000002400005253413100040000010001001120aa3e809b3d" +
"a4f65e1b1f65c0a3a1bf6335c39860ca41acb3c48de278c6b63c5df38239ec1f2e32d58cb897c8" +
"c174a5f8e78a9c0b6087d3aef373d7d0f3d9be67700fc2a5a38de1fb71b5b6f6046d841ff35abe" +
"e2e0b0840a6291a312be184eb311baff5fef0ff6895b9a5f2253aed32fb06b819134f6bb9d5314" +
"88a87ea2")]
23 changes: 23 additions & 0 deletions nanoFramework.Json/TypeUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#nullable enable
using System;
using System.Collections;

namespace nanoFramework.Json
{
internal static class TypeUtils
{
// A very small optimization occurs by caching these types
public static readonly Type ArrayListType = typeof(ArrayList);
public static readonly Type HashTableType = typeof(Hashtable);
public static readonly Type StringType = typeof(string);
public static readonly Type ValueTypeType = typeof(ValueType);

public static bool IsArrayList(Type? type) => ArrayListType == type;

public static bool IsHashTable(Type? type) => HashTableType == type;

public static bool IsString(Type? type) => StringType == type;

public static bool IsValueType(Type? type) => ValueTypeType == type?.BaseType;
}
}
1 change: 1 addition & 0 deletions nanoFramework.Json/nanoFramework.Json.nfproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<Compile Include="JsonValue.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TimeExtensions.cs" />
<Compile Include="TypeUtils.cs" />
</ItemGroup>
<ItemGroup>
<None Include="..\key.snk" />
Expand Down

0 comments on commit efddd3f

Please sign in to comment.