436 lines
11 KiB
C#
436 lines
11 KiB
C#
// Copyright (c) StellaOps. All rights reserved.
|
|
// Licensed under BUSL-1.1. See LICENSE in the project root.
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace StellaOps.BinaryIndex.DeltaSig;
|
|
|
|
/// <summary>
|
|
/// Source file pair used to compute semantic edit scripts.
|
|
/// </summary>
|
|
public sealed record SourceFileDiff
|
|
{
|
|
/// <summary>
|
|
/// Source file path (repository-relative).
|
|
/// </summary>
|
|
public required string Path { get; init; }
|
|
|
|
/// <summary>
|
|
/// Previous file content.
|
|
/// </summary>
|
|
public string? BeforeContent { get; init; }
|
|
|
|
/// <summary>
|
|
/// Current file content.
|
|
/// </summary>
|
|
public string? AfterContent { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Source line span.
|
|
/// </summary>
|
|
public sealed record SourceSpan
|
|
{
|
|
/// <summary>
|
|
/// 1-based start line.
|
|
/// </summary>
|
|
public required int StartLine { get; init; }
|
|
|
|
/// <summary>
|
|
/// 1-based end line.
|
|
/// </summary>
|
|
public required int EndLine { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deterministic semantic edit script.
|
|
/// </summary>
|
|
public sealed record SemanticEditScript
|
|
{
|
|
/// <summary>
|
|
/// Schema version.
|
|
/// </summary>
|
|
public string SchemaVersion { get; init; } = "1.0.0";
|
|
|
|
/// <summary>
|
|
/// Digest of the full source tree diff input.
|
|
/// </summary>
|
|
public required string SourceTreeDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Deterministic ordered edits.
|
|
/// </summary>
|
|
public required IReadOnlyList<SemanticEdit> Edits { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Single semantic edit entry.
|
|
/// </summary>
|
|
public sealed record SemanticEdit
|
|
{
|
|
/// <summary>
|
|
/// Stable digest-derived ID for dedupe and audit references.
|
|
/// </summary>
|
|
public required string StableId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Edit type: add, remove, move, update, rename.
|
|
/// </summary>
|
|
public required string EditType { get; init; }
|
|
|
|
/// <summary>
|
|
/// Node kind: file, method, class, field, import, statement.
|
|
/// </summary>
|
|
public required string NodeKind { get; init; }
|
|
|
|
/// <summary>
|
|
/// Deterministic node path.
|
|
/// </summary>
|
|
public required string NodePath { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol anchor used to link to binary symbols.
|
|
/// </summary>
|
|
public required string Anchor { get; init; }
|
|
|
|
/// <summary>
|
|
/// Pre-change source span.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public SourceSpan? PreSpan { get; init; }
|
|
|
|
/// <summary>
|
|
/// Post-change source span.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public SourceSpan? PostSpan { get; init; }
|
|
|
|
/// <summary>
|
|
/// Pre-change digest.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? PreDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Post-change digest.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? PostDigest { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Build-stage symbol map linked to build identity.
|
|
/// </summary>
|
|
public sealed record SymbolMap
|
|
{
|
|
/// <summary>
|
|
/// Schema version.
|
|
/// </summary>
|
|
public string SchemaVersion { get; init; } = "1.0.0";
|
|
|
|
/// <summary>
|
|
/// Build ID (ELF build-id, PDB GUID, or equivalent).
|
|
/// </summary>
|
|
public required string BuildId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Optional binary digest for traceability.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? BinaryDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Address derivation source (manifest, synthetic-signature).
|
|
/// </summary>
|
|
public string AddressSource { get; init; } = "manifest";
|
|
|
|
/// <summary>
|
|
/// Ordered symbol entries.
|
|
/// </summary>
|
|
public required IReadOnlyList<SymbolMapEntry> Symbols { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Symbol map entry.
|
|
/// </summary>
|
|
public sealed record SymbolMapEntry
|
|
{
|
|
/// <summary>
|
|
/// Symbol name.
|
|
/// </summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol kind: function, object, section.
|
|
/// </summary>
|
|
public string Kind { get; init; } = "function";
|
|
|
|
/// <summary>
|
|
/// Start address.
|
|
/// </summary>
|
|
public required ulong AddressStart { get; init; }
|
|
|
|
/// <summary>
|
|
/// End address.
|
|
/// </summary>
|
|
public required ulong AddressEnd { get; init; }
|
|
|
|
/// <summary>
|
|
/// Containing section.
|
|
/// </summary>
|
|
public string Section { get; init; } = ".text";
|
|
|
|
/// <summary>
|
|
/// Source ranges for this symbol.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public IReadOnlyList<SourceRange>? SourceRanges { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol size derived from address range.
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public long Size => checked((long)(AddressEnd >= AddressStart ? AddressEnd - AddressStart + 1UL : 0UL));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Source range metadata.
|
|
/// </summary>
|
|
public sealed record SourceRange
|
|
{
|
|
/// <summary>
|
|
/// Source file path.
|
|
/// </summary>
|
|
public required string File { get; init; }
|
|
|
|
/// <summary>
|
|
/// 1-based start line.
|
|
/// </summary>
|
|
public required int LineStart { get; init; }
|
|
|
|
/// <summary>
|
|
/// 1-based end line.
|
|
/// </summary>
|
|
public required int LineEnd { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Join artifact linking semantic edits to symbol changes.
|
|
/// </summary>
|
|
public sealed record SymbolPatchPlan
|
|
{
|
|
/// <summary>
|
|
/// Schema version.
|
|
/// </summary>
|
|
public string SchemaVersion { get; init; } = "1.0.0";
|
|
|
|
/// <summary>
|
|
/// Build ID before patch.
|
|
/// </summary>
|
|
public required string BuildIdBefore { get; init; }
|
|
|
|
/// <summary>
|
|
/// Build ID after patch.
|
|
/// </summary>
|
|
public required string BuildIdAfter { get; init; }
|
|
|
|
/// <summary>
|
|
/// Semantic script digest.
|
|
/// </summary>
|
|
public required string EditsDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Old symbol map digest.
|
|
/// </summary>
|
|
public required string SymbolMapDigestBefore { get; init; }
|
|
|
|
/// <summary>
|
|
/// New symbol map digest.
|
|
/// </summary>
|
|
public required string SymbolMapDigestAfter { get; init; }
|
|
|
|
/// <summary>
|
|
/// Ordered symbol changes.
|
|
/// </summary>
|
|
public required IReadOnlyList<SymbolPatchChange> Changes { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Single symbol patch plan entry.
|
|
/// </summary>
|
|
public sealed record SymbolPatchChange
|
|
{
|
|
/// <summary>
|
|
/// Symbol name.
|
|
/// </summary>
|
|
public required string Symbol { get; init; }
|
|
|
|
/// <summary>
|
|
/// Change type: added, removed, modified, moved.
|
|
/// </summary>
|
|
public required string ChangeType { get; init; }
|
|
|
|
/// <summary>
|
|
/// Linked source anchors.
|
|
/// </summary>
|
|
public required IReadOnlyList<string> AstAnchors { get; init; }
|
|
|
|
/// <summary>
|
|
/// Hash before.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? PreHash { get; init; }
|
|
|
|
/// <summary>
|
|
/// Hash after.
|
|
/// </summary>
|
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
public string? PostHash { get; init; }
|
|
|
|
/// <summary>
|
|
/// Delta reference digest.
|
|
/// </summary>
|
|
public required string DeltaRef { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Normalized patch manifest for per-symbol deltas.
|
|
/// </summary>
|
|
public sealed record PatchManifest
|
|
{
|
|
/// <summary>
|
|
/// Schema version.
|
|
/// </summary>
|
|
public string SchemaVersion { get; init; } = "1.0.0";
|
|
|
|
/// <summary>
|
|
/// Build ID.
|
|
/// </summary>
|
|
public required string BuildId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Normalization recipe identifier.
|
|
/// </summary>
|
|
public required string NormalizationRecipeId { get; init; }
|
|
|
|
/// <summary>
|
|
/// Ordered patch entries.
|
|
/// </summary>
|
|
public required IReadOnlyList<SymbolPatchArtifact> Patches { get; init; }
|
|
|
|
/// <summary>
|
|
/// Total delta bytes across symbols.
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public long TotalDeltaBytes => Patches.Sum(p => p.DeltaSizeBytes);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Per-symbol patch artifact.
|
|
/// </summary>
|
|
public sealed record SymbolPatchArtifact
|
|
{
|
|
/// <summary>
|
|
/// Symbol name.
|
|
/// </summary>
|
|
public required string Symbol { get; init; }
|
|
|
|
/// <summary>
|
|
/// Address range in hex format.
|
|
/// </summary>
|
|
public required string AddressRange { get; init; }
|
|
|
|
/// <summary>
|
|
/// Digest of patch payload.
|
|
/// </summary>
|
|
public required string DeltaDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Pre-patch metrics.
|
|
/// </summary>
|
|
public required PatchSizeHash Pre { get; init; }
|
|
|
|
/// <summary>
|
|
/// Post-patch metrics.
|
|
/// </summary>
|
|
public required PatchSizeHash Post { get; init; }
|
|
|
|
/// <summary>
|
|
/// Absolute byte delta.
|
|
/// </summary>
|
|
[JsonIgnore]
|
|
public long DeltaSizeBytes => Math.Abs(Post.Size - Pre.Size);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Size/hash tuple.
|
|
/// </summary>
|
|
public sealed record PatchSizeHash
|
|
{
|
|
/// <summary>
|
|
/// Size in bytes.
|
|
/// </summary>
|
|
public required long Size { get; init; }
|
|
|
|
/// <summary>
|
|
/// Hash digest.
|
|
/// </summary>
|
|
public required string Hash { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Full hybrid diff evidence bundle.
|
|
/// </summary>
|
|
public sealed record HybridDiffEvidence
|
|
{
|
|
/// <summary>
|
|
/// Semantic edit script.
|
|
/// </summary>
|
|
public required SemanticEditScript SemanticEditScript { get; init; }
|
|
|
|
/// <summary>
|
|
/// Old symbol map.
|
|
/// </summary>
|
|
public required SymbolMap OldSymbolMap { get; init; }
|
|
|
|
/// <summary>
|
|
/// New symbol map.
|
|
/// </summary>
|
|
public required SymbolMap NewSymbolMap { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol patch plan.
|
|
/// </summary>
|
|
public required SymbolPatchPlan SymbolPatchPlan { get; init; }
|
|
|
|
/// <summary>
|
|
/// Normalized patch manifest.
|
|
/// </summary>
|
|
public required PatchManifest PatchManifest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Semantic edit script digest.
|
|
/// </summary>
|
|
public required string SemanticEditScriptDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Old symbol map digest.
|
|
/// </summary>
|
|
public required string OldSymbolMapDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// New symbol map digest.
|
|
/// </summary>
|
|
public required string NewSymbolMapDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Symbol patch plan digest.
|
|
/// </summary>
|
|
public required string SymbolPatchPlanDigest { get; init; }
|
|
|
|
/// <summary>
|
|
/// Patch manifest digest.
|
|
/// </summary>
|
|
public required string PatchManifestDigest { get; init; }
|
|
}
|