Fix audit findings for coverage, architecture checks, and XML docs
All checks were successful
NuGet Publish / build-and-pack (push) Successful in 45s
NuGet Publish / publish-to-gitea (push) Successful in 52s

This commit is contained in:
Joseph Doherty
2026-02-20 15:43:25 -05:00
parent 5528806518
commit 3ffd468c79
99 changed files with 23746 additions and 9548 deletions

View File

@@ -29,6 +29,13 @@ public readonly struct CompressedPayloadHeader
/// </summary>
public uint Checksum { get; }
/// <summary>
/// Initializes a new instance of the <see cref="CompressedPayloadHeader"/> class.
/// </summary>
/// <param name="codec">Compression codec used for payload bytes.</param>
/// <param name="originalLength">Original uncompressed payload length.</param>
/// <param name="compressedLength">Compressed payload length.</param>
/// <param name="checksum">CRC32 checksum of compressed payload bytes.</param>
public CompressedPayloadHeader(CompressionCodec codec, int originalLength, int compressedLength, uint checksum)
{
if (originalLength < 0)
@@ -42,12 +49,22 @@ public readonly struct CompressedPayloadHeader
Checksum = checksum;
}
/// <summary>
/// Create.
/// </summary>
/// <param name="codec">Compression codec used for payload bytes.</param>
/// <param name="originalLength">Original uncompressed payload length.</param>
/// <param name="compressedPayload">Compressed payload bytes.</param>
public static CompressedPayloadHeader Create(CompressionCodec codec, int originalLength, ReadOnlySpan<byte> compressedPayload)
{
var checksum = ComputeChecksum(compressedPayload);
return new CompressedPayloadHeader(codec, originalLength, compressedPayload.Length, checksum);
}
/// <summary>
/// Write To.
/// </summary>
/// <param name="destination">Destination span that receives the serialized header.</param>
public void WriteTo(Span<byte> destination)
{
if (destination.Length < Size)
@@ -62,6 +79,10 @@ public readonly struct CompressedPayloadHeader
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(12, 4), Checksum);
}
/// <summary>
/// Read From.
/// </summary>
/// <param name="source">Source span containing a serialized header.</param>
public static CompressedPayloadHeader ReadFrom(ReadOnlySpan<byte> source)
{
if (source.Length < Size)
@@ -74,11 +95,19 @@ public readonly struct CompressedPayloadHeader
return new CompressedPayloadHeader(codec, originalLength, compressedLength, checksum);
}
/// <summary>
/// Validate Checksum.
/// </summary>
/// <param name="compressedPayload">Compressed payload bytes to validate.</param>
public bool ValidateChecksum(ReadOnlySpan<byte> compressedPayload)
{
return Checksum == ComputeChecksum(compressedPayload);
}
/// <summary>
/// Compute Checksum.
/// </summary>
/// <param name="payload">Payload bytes.</param>
public static uint ComputeChecksum(ReadOnlySpan<byte> payload) => Crc32Calculator.Compute(payload);
private static class Crc32Calculator
@@ -86,6 +115,10 @@ public readonly struct CompressedPayloadHeader
private const uint Polynomial = 0xEDB88320u;
private static readonly uint[] Table = CreateTable();
/// <summary>
/// Compute.
/// </summary>
/// <param name="payload">Payload bytes.</param>
public static uint Compute(ReadOnlySpan<byte> payload)
{
uint crc = 0xFFFFFFFFu;

View File

@@ -47,6 +47,10 @@ public sealed class CompressionOptions
/// </summary>
public int? MaxCompressionInputBytes { get; init; }
/// <summary>
/// Normalizes and validates compression options.
/// </summary>
/// <param name="options">Optional user-provided options.</param>
internal static CompressionOptions Normalize(CompressionOptions? options)
{
var candidate = options ?? Default;

View File

@@ -11,6 +11,10 @@ public sealed class CompressionService
{
private readonly ConcurrentDictionary<CompressionCodec, ICompressionCodec> _codecs = new();
/// <summary>
/// Initializes a new instance of the <see cref="CompressionService"/> class.
/// </summary>
/// <param name="additionalCodecs">Optional additional codecs to register.</param>
public CompressionService(IEnumerable<ICompressionCodec>? additionalCodecs = null)
{
RegisterCodec(new NoneCompressionCodec());
@@ -26,17 +30,32 @@ public sealed class CompressionService
}
}
/// <summary>
/// Registers or replaces a compression codec implementation.
/// </summary>
/// <param name="codec">The codec implementation to register.</param>
public void RegisterCodec(ICompressionCodec codec)
{
ArgumentNullException.ThrowIfNull(codec);
_codecs[codec.Codec] = codec;
}
/// <summary>
/// Attempts to resolve a registered codec implementation.
/// </summary>
/// <param name="codec">The codec identifier to resolve.</param>
/// <param name="compressionCodec">When this method returns, contains the resolved codec when found.</param>
/// <returns><see langword="true"/> when a codec is registered for <paramref name="codec"/>; otherwise, <see langword="false"/>.</returns>
public bool TryGetCodec(CompressionCodec codec, out ICompressionCodec compressionCodec)
{
return _codecs.TryGetValue(codec, out compressionCodec!);
}
/// <summary>
/// Gets a registered codec implementation.
/// </summary>
/// <param name="codec">The codec identifier to resolve.</param>
/// <returns>The registered codec implementation.</returns>
public ICompressionCodec GetCodec(CompressionCodec codec)
{
if (_codecs.TryGetValue(codec, out var compressionCodec))
@@ -45,16 +64,39 @@ public sealed class CompressionService
throw new InvalidOperationException($"Compression codec '{codec}' is not registered.");
}
/// <summary>
/// Compresses payload bytes using the selected codec and level.
/// </summary>
/// <param name="input">The payload bytes to compress.</param>
/// <param name="codec">The codec to use.</param>
/// <param name="level">The compression level.</param>
/// <returns>The compressed payload bytes.</returns>
public byte[] Compress(ReadOnlySpan<byte> input, CompressionCodec codec, CompressionLevel level)
{
return GetCodec(codec).Compress(input, level);
}
/// <summary>
/// Decompresses payload bytes using the selected codec.
/// </summary>
/// <param name="input">The compressed payload bytes.</param>
/// <param name="codec">The codec to use.</param>
/// <param name="expectedLength">The expected decompressed byte length, or a negative value to skip exact-length validation.</param>
/// <param name="maxDecompressedSizeBytes">The maximum allowed decompressed byte length.</param>
/// <returns>The decompressed payload bytes.</returns>
public byte[] Decompress(ReadOnlySpan<byte> input, CompressionCodec codec, int expectedLength, int maxDecompressedSizeBytes)
{
return GetCodec(codec).Decompress(input, expectedLength, maxDecompressedSizeBytes);
}
/// <summary>
/// Compresses and then decompresses payload bytes using the selected codec.
/// </summary>
/// <param name="input">The payload bytes to roundtrip.</param>
/// <param name="codec">The codec to use.</param>
/// <param name="level">The compression level.</param>
/// <param name="maxDecompressedSizeBytes">The maximum allowed decompressed byte length.</param>
/// <returns>The decompressed payload bytes after roundtrip.</returns>
public byte[] Roundtrip(ReadOnlySpan<byte> input, CompressionCodec codec, CompressionLevel level, int maxDecompressedSizeBytes)
{
var compressed = Compress(input, codec, level);
@@ -63,10 +105,26 @@ public sealed class CompressionService
private sealed class NoneCompressionCodec : ICompressionCodec
{
/// <summary>
/// Gets the codec identifier.
/// </summary>
public CompressionCodec Codec => CompressionCodec.None;
/// <summary>
/// Returns a copy of the input payload without compression.
/// </summary>
/// <param name="input">The payload bytes to copy.</param>
/// <param name="level">The requested compression level.</param>
/// <returns>The copied payload bytes.</returns>
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level) => input.ToArray();
/// <summary>
/// Validates and returns an uncompressed payload copy.
/// </summary>
/// <param name="input">The payload bytes to validate and copy.</param>
/// <param name="expectedLength">The expected payload length, or a negative value to skip exact-length validation.</param>
/// <param name="maxDecompressedSizeBytes">The maximum allowed payload size in bytes.</param>
/// <returns>The copied payload bytes.</returns>
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
if (input.Length > maxDecompressedSizeBytes)
@@ -81,13 +139,29 @@ public sealed class CompressionService
private sealed class BrotliCompressionCodec : ICompressionCodec
{
/// <summary>
/// Gets the codec identifier.
/// </summary>
public CompressionCodec Codec => CompressionCodec.Brotli;
/// <summary>
/// Compresses payload bytes using Brotli.
/// </summary>
/// <param name="input">The payload bytes to compress.</param>
/// <param name="level">The compression level.</param>
/// <returns>The compressed payload bytes.</returns>
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level)
{
return CompressWithCodecStream(input, stream => new BrotliStream(stream, level, leaveOpen: true));
}
/// <summary>
/// Decompresses Brotli-compressed payload bytes.
/// </summary>
/// <param name="input">The compressed payload bytes.</param>
/// <param name="expectedLength">The expected decompressed byte length, or a negative value to skip exact-length validation.</param>
/// <param name="maxDecompressedSizeBytes">The maximum allowed decompressed byte length.</param>
/// <returns>The decompressed payload bytes.</returns>
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
return DecompressWithCodecStream(input, stream => new BrotliStream(stream, CompressionMode.Decompress, leaveOpen: true), expectedLength, maxDecompressedSizeBytes);
@@ -96,13 +170,29 @@ public sealed class CompressionService
private sealed class DeflateCompressionCodec : ICompressionCodec
{
/// <summary>
/// Gets the codec identifier.
/// </summary>
public CompressionCodec Codec => CompressionCodec.Deflate;
/// <summary>
/// Compresses payload bytes using Deflate.
/// </summary>
/// <param name="input">The payload bytes to compress.</param>
/// <param name="level">The compression level.</param>
/// <returns>The compressed payload bytes.</returns>
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level)
{
return CompressWithCodecStream(input, stream => new DeflateStream(stream, level, leaveOpen: true));
}
/// <summary>
/// Decompresses Deflate-compressed payload bytes.
/// </summary>
/// <param name="input">The compressed payload bytes.</param>
/// <param name="expectedLength">The expected decompressed byte length, or a negative value to skip exact-length validation.</param>
/// <param name="maxDecompressedSizeBytes">The maximum allowed decompressed byte length.</param>
/// <returns>The decompressed payload bytes.</returns>
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
return DecompressWithCodecStream(input, stream => new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true), expectedLength, maxDecompressedSizeBytes);

View File

@@ -5,12 +5,36 @@ namespace ZB.MOM.WW.CBDD.Core.Compression;
/// </summary>
public readonly struct CompressionStats
{
/// <summary>
/// Gets or sets the CompressedDocumentCount.
/// </summary>
public long CompressedDocumentCount { get; init; }
/// <summary>
/// Gets or sets the BytesBeforeCompression.
/// </summary>
public long BytesBeforeCompression { get; init; }
/// <summary>
/// Gets or sets the BytesAfterCompression.
/// </summary>
public long BytesAfterCompression { get; init; }
/// <summary>
/// Gets or sets the CompressionCpuTicks.
/// </summary>
public long CompressionCpuTicks { get; init; }
/// <summary>
/// Gets or sets the DecompressionCpuTicks.
/// </summary>
public long DecompressionCpuTicks { get; init; }
/// <summary>
/// Gets or sets the CompressionFailureCount.
/// </summary>
public long CompressionFailureCount { get; init; }
/// <summary>
/// Gets or sets the ChecksumFailureCount.
/// </summary>
public long ChecksumFailureCount { get; init; }
/// <summary>
/// Gets or sets the SafetyLimitRejectionCount.
/// </summary>
public long SafetyLimitRejectionCount { get; init; }
}

View File

@@ -24,29 +24,100 @@ public sealed class CompressionTelemetry
private long _checksumFailureCount;
private long _safetyLimitRejectionCount;
/// <summary>
/// Gets the number of attempted compression operations.
/// </summary>
public long CompressionAttempts => Interlocked.Read(ref _compressionAttempts);
/// <summary>
/// Gets the number of successful compression operations.
/// </summary>
public long CompressionSuccesses => Interlocked.Read(ref _compressionSuccesses);
/// <summary>
/// Gets the number of failed compression operations.
/// </summary>
public long CompressionFailures => Interlocked.Read(ref _compressionFailures);
/// <summary>
/// Gets the number of compression attempts skipped because payloads were too small.
/// </summary>
public long CompressionSkippedTooSmall => Interlocked.Read(ref _compressionSkippedTooSmall);
/// <summary>
/// Gets the number of compression attempts skipped due to insufficient savings.
/// </summary>
public long CompressionSkippedInsufficientSavings => Interlocked.Read(ref _compressionSkippedInsufficientSavings);
/// <summary>
/// Gets the number of attempted decompression operations.
/// </summary>
public long DecompressionAttempts => Interlocked.Read(ref _decompressionAttempts);
/// <summary>
/// Gets the number of successful decompression operations.
/// </summary>
public long DecompressionSuccesses => Interlocked.Read(ref _decompressionSuccesses);
/// <summary>
/// Gets the number of failed decompression operations.
/// </summary>
public long DecompressionFailures => Interlocked.Read(ref _decompressionFailures);
/// <summary>
/// Gets the total input bytes observed by compression attempts.
/// </summary>
public long CompressionInputBytes => Interlocked.Read(ref _compressionInputBytes);
/// <summary>
/// Gets the total output bytes produced by successful compression attempts.
/// </summary>
public long CompressionOutputBytes => Interlocked.Read(ref _compressionOutputBytes);
/// <summary>
/// Gets the total output bytes produced by successful decompression attempts.
/// </summary>
public long DecompressionOutputBytes => Interlocked.Read(ref _decompressionOutputBytes);
/// <summary>
/// Gets the number of documents stored in compressed form.
/// </summary>
public long CompressedDocumentCount => Interlocked.Read(ref _compressedDocumentCount);
/// <summary>
/// Gets the total CPU ticks spent on compression.
/// </summary>
public long CompressionCpuTicks => Interlocked.Read(ref _compressionCpuTicks);
/// <summary>
/// Gets the total CPU ticks spent on decompression.
/// </summary>
public long DecompressionCpuTicks => Interlocked.Read(ref _decompressionCpuTicks);
/// <summary>
/// Gets the number of checksum validation failures.
/// </summary>
public long ChecksumFailureCount => Interlocked.Read(ref _checksumFailureCount);
/// <summary>
/// Gets the number of decompression safety-limit rejections.
/// </summary>
public long SafetyLimitRejectionCount => Interlocked.Read(ref _safetyLimitRejectionCount);
/// <summary>
/// Records a compression attempt and its input byte size.
/// </summary>
/// <param name="inputBytes">The number of input bytes provided to compression.</param>
public void RecordCompressionAttempt(int inputBytes)
{
Interlocked.Increment(ref _compressionAttempts);
Interlocked.Add(ref _compressionInputBytes, inputBytes);
}
/// <summary>
/// Records a successful compression operation.
/// </summary>
/// <param name="outputBytes">The number of compressed bytes produced.</param>
public void RecordCompressionSuccess(int outputBytes)
{
Interlocked.Increment(ref _compressionSuccesses);
@@ -54,23 +125,67 @@ public sealed class CompressionTelemetry
Interlocked.Add(ref _compressionOutputBytes, outputBytes);
}
/// <summary>
/// Records a failed compression operation.
/// </summary>
public void RecordCompressionFailure() => Interlocked.Increment(ref _compressionFailures);
/// <summary>
/// Records that compression was skipped because the payload was too small.
/// </summary>
public void RecordCompressionSkippedTooSmall() => Interlocked.Increment(ref _compressionSkippedTooSmall);
/// <summary>
/// Records that compression was skipped due to insufficient expected savings.
/// </summary>
public void RecordCompressionSkippedInsufficientSavings() => Interlocked.Increment(ref _compressionSkippedInsufficientSavings);
/// <summary>
/// Records a decompression attempt.
/// </summary>
public void RecordDecompressionAttempt() => Interlocked.Increment(ref _decompressionAttempts);
/// <summary>
/// Adds CPU ticks spent performing compression.
/// </summary>
/// <param name="ticks">The CPU ticks to add.</param>
public void RecordCompressionCpuTicks(long ticks) => Interlocked.Add(ref _compressionCpuTicks, ticks);
/// <summary>
/// Adds CPU ticks spent performing decompression.
/// </summary>
/// <param name="ticks">The CPU ticks to add.</param>
public void RecordDecompressionCpuTicks(long ticks) => Interlocked.Add(ref _decompressionCpuTicks, ticks);
/// <summary>
/// Records a checksum validation failure.
/// </summary>
public void RecordChecksumFailure() => Interlocked.Increment(ref _checksumFailureCount);
/// <summary>
/// Records a decompression rejection due to safety limits.
/// </summary>
public void RecordSafetyLimitRejection() => Interlocked.Increment(ref _safetyLimitRejectionCount);
/// <summary>
/// Records a successful decompression operation.
/// </summary>
/// <param name="outputBytes">The number of decompressed bytes produced.</param>
public void RecordDecompressionSuccess(int outputBytes)
{
Interlocked.Increment(ref _decompressionSuccesses);
Interlocked.Add(ref _decompressionOutputBytes, outputBytes);
}
/// <summary>
/// Records a failed decompression operation.
/// </summary>
public void RecordDecompressionFailure() => Interlocked.Increment(ref _decompressionFailures);
/// <summary>
/// Returns a point-in-time snapshot of compression telemetry.
/// </summary>
/// <returns>The aggregated compression statistics.</returns>
public CompressionStats GetSnapshot()
{
return new CompressionStats

View File

@@ -15,10 +15,15 @@ public interface ICompressionCodec
/// <summary>
/// Compresses input bytes.
/// </summary>
/// <param name="input">Input payload bytes to compress.</param>
/// <param name="level">Compression level to apply.</param>
byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level);
/// <summary>
/// Decompresses payload bytes with output bounds validation.
/// </summary>
/// <param name="input">Input payload bytes to decompress.</param>
/// <param name="expectedLength">Expected decompressed length.</param>
/// <param name="maxDecompressedSizeBytes">Maximum allowed decompressed payload size in bytes.</param>
byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes);
}