Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -0,0 +1,98 @@
using System.Text.Json;
using StellaOps.Canonicalization.Json;
namespace StellaOps.Canonicalization.Verification;
/// <summary>
/// Verifies that serialization produces identical output across runs.
/// </summary>
public sealed class DeterminismVerifier
{
public DeterminismResult Verify<T>(T value, int iterations = 10)
{
var outputs = new HashSet<string>(StringComparer.Ordinal);
var digests = new HashSet<string>(StringComparer.Ordinal);
for (var i = 0; i < iterations; i++)
{
var (json, digest) = CanonicalJsonSerializer.SerializeWithDigest(value);
outputs.Add(json);
digests.Add(digest);
}
return new DeterminismResult(
IsDeterministic: outputs.Count == 1 && digests.Count == 1,
UniqueOutputs: outputs.Count,
UniqueDigests: digests.Count,
SampleOutput: outputs.FirstOrDefault() ?? string.Empty,
SampleDigest: digests.FirstOrDefault() ?? string.Empty);
}
public ComparisonResult Compare(string jsonA, string jsonB)
{
if (string.Equals(jsonA, jsonB, StringComparison.Ordinal))
{
return new ComparisonResult(true, []);
}
var differences = FindDifferences(jsonA, jsonB);
return new ComparisonResult(false, differences);
}
private static IReadOnlyList<string> FindDifferences(string a, string b)
{
var differences = new List<string>();
using var docA = JsonDocument.Parse(a);
using var docB = JsonDocument.Parse(b);
CompareElements(docA.RootElement, docB.RootElement, "$", differences);
return differences;
}
private static void CompareElements(JsonElement a, JsonElement b, string path, List<string> differences)
{
if (a.ValueKind != b.ValueKind)
{
differences.Add($"{path}: type mismatch ({a.ValueKind} vs {b.ValueKind})");
return;
}
switch (a.ValueKind)
{
case JsonValueKind.Object:
var propsA = a.EnumerateObject().ToDictionary(p => p.Name, StringComparer.Ordinal);
var propsB = b.EnumerateObject().ToDictionary(p => p.Name, StringComparer.Ordinal);
foreach (var key in propsA.Keys.Union(propsB.Keys).OrderBy(k => k, StringComparer.Ordinal))
{
var hasA = propsA.TryGetValue(key, out var propA);
var hasB = propsB.TryGetValue(key, out var propB);
if (!hasA) differences.Add($"{path}.{key}: missing in first");
else if (!hasB) differences.Add($"{path}.{key}: missing in second");
else CompareElements(propA.Value, propB.Value, $"{path}.{key}", differences);
}
break;
case JsonValueKind.Array:
var arrA = a.EnumerateArray().ToList();
var arrB = b.EnumerateArray().ToList();
if (arrA.Count != arrB.Count)
differences.Add($"{path}: array length mismatch ({arrA.Count} vs {arrB.Count})");
for (var i = 0; i < Math.Min(arrA.Count, arrB.Count); i++)
CompareElements(arrA[i], arrB[i], $"{path}[{i}]", differences);
break;
default:
if (a.GetRawText() != b.GetRawText())
differences.Add($"{path}: value mismatch");
break;
}
}
}
public sealed record DeterminismResult(
bool IsDeterministic,
int UniqueOutputs,
int UniqueDigests,
string SampleOutput,
string SampleDigest);
public sealed record ComparisonResult(
bool IsIdentical,
IReadOnlyList<string> Differences);