Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve type checks #347

Merged
merged 6 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading