Files
git.stella-ops.org/src/StellaOps.Concelier.Exporter.Json/ExportDigestCalculator.cs
2025-10-18 20:46:16 +03:00

53 lines
1.7 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace StellaOps.Concelier.Exporter.Json;
public static class ExportDigestCalculator
{
public static string ComputeTreeDigest(JsonExportResult result)
{
ArgumentNullException.ThrowIfNull(result);
using var sha256 = SHA256.Create();
var buffer = new byte[128 * 1024];
foreach (var file in result.FilePaths.OrderBy(static path => path, StringComparer.Ordinal))
{
var normalized = file.Replace("\\", "/");
var pathBytes = Encoding.UTF8.GetBytes(normalized);
_ = sha256.TransformBlock(pathBytes, 0, pathBytes.Length, null, 0);
var fullPath = ResolveFullPath(result.ExportDirectory, normalized);
using var stream = File.OpenRead(fullPath);
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
_ = sha256.TransformBlock(buffer, 0, read, null, 0);
}
}
_ = sha256.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
var hash = sha256.Hash ?? Array.Empty<byte>();
var hex = Convert.ToHexString(hash).ToLowerInvariant();
return $"sha256:{hex}";
}
private static string ResolveFullPath(string root, string normalizedRelativePath)
{
var segments = normalizedRelativePath.Split('/', StringSplitOptions.RemoveEmptyEntries);
var parts = new string[segments.Length + 1];
parts[0] = root;
for (var i = 0; i < segments.Length; i++)
{
parts[i + 1] = segments[i];
}
return Path.Combine(parts);
}
}