Initialize CBDD solution and add a .NET-focused gitignore for generated artifacts.

This commit is contained in:
Joseph Doherty
2026-02-20 12:54:07 -05:00
commit b8ed5ec500
214 changed files with 101452 additions and 0 deletions

View File

@@ -0,0 +1,118 @@
using System.Buffers.Binary;
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Fixed header prefix for compressed payload blobs.
/// </summary>
public readonly struct CompressedPayloadHeader
{
public const int Size = 16;
/// <summary>
/// Compression codec used for payload bytes.
/// </summary>
public CompressionCodec Codec { get; }
/// <summary>
/// Original uncompressed payload length.
/// </summary>
public int OriginalLength { get; }
/// <summary>
/// Compressed payload length.
/// </summary>
public int CompressedLength { get; }
/// <summary>
/// CRC32 checksum of compressed payload bytes.
/// </summary>
public uint Checksum { get; }
public CompressedPayloadHeader(CompressionCodec codec, int originalLength, int compressedLength, uint checksum)
{
if (originalLength < 0)
throw new ArgumentOutOfRangeException(nameof(originalLength));
if (compressedLength < 0)
throw new ArgumentOutOfRangeException(nameof(compressedLength));
Codec = codec;
OriginalLength = originalLength;
CompressedLength = compressedLength;
Checksum = checksum;
}
public static CompressedPayloadHeader Create(CompressionCodec codec, int originalLength, ReadOnlySpan<byte> compressedPayload)
{
var checksum = ComputeChecksum(compressedPayload);
return new CompressedPayloadHeader(codec, originalLength, compressedPayload.Length, checksum);
}
public void WriteTo(Span<byte> destination)
{
if (destination.Length < Size)
throw new ArgumentException($"Destination must be at least {Size} bytes.", nameof(destination));
destination[0] = (byte)Codec;
destination[1] = 0;
destination[2] = 0;
destination[3] = 0;
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(4, 4), OriginalLength);
BinaryPrimitives.WriteInt32LittleEndian(destination.Slice(8, 4), CompressedLength);
BinaryPrimitives.WriteUInt32LittleEndian(destination.Slice(12, 4), Checksum);
}
public static CompressedPayloadHeader ReadFrom(ReadOnlySpan<byte> source)
{
if (source.Length < Size)
throw new ArgumentException($"Source must be at least {Size} bytes.", nameof(source));
var codec = (CompressionCodec)source[0];
var originalLength = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(4, 4));
var compressedLength = BinaryPrimitives.ReadInt32LittleEndian(source.Slice(8, 4));
var checksum = BinaryPrimitives.ReadUInt32LittleEndian(source.Slice(12, 4));
return new CompressedPayloadHeader(codec, originalLength, compressedLength, checksum);
}
public bool ValidateChecksum(ReadOnlySpan<byte> compressedPayload)
{
return Checksum == ComputeChecksum(compressedPayload);
}
public static uint ComputeChecksum(ReadOnlySpan<byte> payload) => Crc32Calculator.Compute(payload);
private static class Crc32Calculator
{
private const uint Polynomial = 0xEDB88320u;
private static readonly uint[] Table = CreateTable();
public static uint Compute(ReadOnlySpan<byte> payload)
{
uint crc = 0xFFFFFFFFu;
for (int i = 0; i < payload.Length; i++)
{
var index = (crc ^ payload[i]) & 0xFF;
crc = (crc >> 8) ^ Table[index];
}
return ~crc;
}
private static uint[] CreateTable()
{
var table = new uint[256];
for (uint i = 0; i < table.Length; i++)
{
uint value = i;
for (int bit = 0; bit < 8; bit++)
{
value = (value & 1) != 0 ? (value >> 1) ^ Polynomial : value >> 1;
}
table[i] = value;
}
return table;
}
}
}

View File

@@ -0,0 +1,11 @@
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Supported payload compression codecs.
/// </summary>
public enum CompressionCodec : byte
{
None = 0,
Brotli = 1,
Deflate = 2
}

View File

@@ -0,0 +1,71 @@
using System.IO.Compression;
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Compression configuration for document payload processing.
/// </summary>
public sealed class CompressionOptions
{
/// <summary>
/// Default compression options (compression disabled).
/// </summary>
public static CompressionOptions Default { get; } = new();
/// <summary>
/// Enables payload compression for new writes.
/// </summary>
public bool EnableCompression { get; init; } = false;
/// <summary>
/// Minimum payload size (bytes) required before compression is attempted.
/// </summary>
public int MinSizeBytes { get; init; } = 1024;
/// <summary>
/// Minimum percentage of size reduction required to keep compressed output.
/// </summary>
public int MinSavingsPercent { get; init; } = 10;
/// <summary>
/// Preferred default codec for new writes.
/// </summary>
public CompressionCodec Codec { get; init; } = CompressionCodec.Brotli;
/// <summary>
/// Compression level passed to codec implementations.
/// </summary>
public CompressionLevel Level { get; init; } = CompressionLevel.Fastest;
/// <summary>
/// Maximum allowed decompressed payload size.
/// </summary>
public int MaxDecompressedSizeBytes { get; init; } = 16 * 1024 * 1024;
/// <summary>
/// Optional maximum input size allowed for compression attempts.
/// </summary>
public int? MaxCompressionInputBytes { get; init; }
internal static CompressionOptions Normalize(CompressionOptions? options)
{
var candidate = options ?? Default;
if (candidate.MinSizeBytes < 0)
throw new ArgumentOutOfRangeException(nameof(MinSizeBytes), "MinSizeBytes must be non-negative.");
if (candidate.MinSavingsPercent is < 0 or > 100)
throw new ArgumentOutOfRangeException(nameof(MinSavingsPercent), "MinSavingsPercent must be between 0 and 100.");
if (!Enum.IsDefined(candidate.Codec))
throw new ArgumentOutOfRangeException(nameof(Codec), $"Unsupported codec: {candidate.Codec}.");
if (candidate.MaxDecompressedSizeBytes <= 0)
throw new ArgumentOutOfRangeException(nameof(MaxDecompressedSizeBytes), "MaxDecompressedSizeBytes must be greater than 0.");
if (candidate.MaxCompressionInputBytes is <= 0)
throw new ArgumentOutOfRangeException(nameof(MaxCompressionInputBytes), "MaxCompressionInputBytes must be greater than 0 when provided.");
return candidate;
}
}

View File

@@ -0,0 +1,166 @@
using System.Buffers;
using System.Collections.Concurrent;
using System.IO.Compression;
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Compression codec registry and utility service.
/// </summary>
public sealed class CompressionService
{
private readonly ConcurrentDictionary<CompressionCodec, ICompressionCodec> _codecs = new();
public CompressionService(IEnumerable<ICompressionCodec>? additionalCodecs = null)
{
RegisterCodec(new NoneCompressionCodec());
RegisterCodec(new BrotliCompressionCodec());
RegisterCodec(new DeflateCompressionCodec());
if (additionalCodecs == null)
return;
foreach (var codec in additionalCodecs)
{
RegisterCodec(codec);
}
}
public void RegisterCodec(ICompressionCodec codec)
{
ArgumentNullException.ThrowIfNull(codec);
_codecs[codec.Codec] = codec;
}
public bool TryGetCodec(CompressionCodec codec, out ICompressionCodec compressionCodec)
{
return _codecs.TryGetValue(codec, out compressionCodec!);
}
public ICompressionCodec GetCodec(CompressionCodec codec)
{
if (_codecs.TryGetValue(codec, out var compressionCodec))
return compressionCodec;
throw new InvalidOperationException($"Compression codec '{codec}' is not registered.");
}
public byte[] Compress(ReadOnlySpan<byte> input, CompressionCodec codec, CompressionLevel level)
{
return GetCodec(codec).Compress(input, level);
}
public byte[] Decompress(ReadOnlySpan<byte> input, CompressionCodec codec, int expectedLength, int maxDecompressedSizeBytes)
{
return GetCodec(codec).Decompress(input, expectedLength, maxDecompressedSizeBytes);
}
public byte[] Roundtrip(ReadOnlySpan<byte> input, CompressionCodec codec, CompressionLevel level, int maxDecompressedSizeBytes)
{
var compressed = Compress(input, codec, level);
return Decompress(compressed, codec, input.Length, maxDecompressedSizeBytes);
}
private sealed class NoneCompressionCodec : ICompressionCodec
{
public CompressionCodec Codec => CompressionCodec.None;
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level) => input.ToArray();
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
if (input.Length > maxDecompressedSizeBytes)
throw new InvalidDataException($"Decompressed payload exceeds max allowed size ({maxDecompressedSizeBytes} bytes).");
if (expectedLength >= 0 && expectedLength != input.Length)
throw new InvalidDataException($"Expected decompressed length {expectedLength}, actual {input.Length}.");
return input.ToArray();
}
}
private sealed class BrotliCompressionCodec : ICompressionCodec
{
public CompressionCodec Codec => CompressionCodec.Brotli;
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level)
{
return CompressWithCodecStream(input, stream => new BrotliStream(stream, level, leaveOpen: true));
}
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
return DecompressWithCodecStream(input, stream => new BrotliStream(stream, CompressionMode.Decompress, leaveOpen: true), expectedLength, maxDecompressedSizeBytes);
}
}
private sealed class DeflateCompressionCodec : ICompressionCodec
{
public CompressionCodec Codec => CompressionCodec.Deflate;
public byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level)
{
return CompressWithCodecStream(input, stream => new DeflateStream(stream, level, leaveOpen: true));
}
public byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes)
{
return DecompressWithCodecStream(input, stream => new DeflateStream(stream, CompressionMode.Decompress, leaveOpen: true), expectedLength, maxDecompressedSizeBytes);
}
}
private static byte[] CompressWithCodecStream(ReadOnlySpan<byte> input, Func<Stream, Stream> streamFactory)
{
using var output = new MemoryStream(capacity: input.Length);
using (var codecStream = streamFactory(output))
{
codecStream.Write(input);
codecStream.Flush();
}
return output.ToArray();
}
private static byte[] DecompressWithCodecStream(
ReadOnlySpan<byte> input,
Func<Stream, Stream> streamFactory,
int expectedLength,
int maxDecompressedSizeBytes)
{
if (maxDecompressedSizeBytes <= 0)
throw new ArgumentOutOfRangeException(nameof(maxDecompressedSizeBytes));
using var compressed = new MemoryStream(input.ToArray(), writable: false);
using var codecStream = streamFactory(compressed);
using var output = expectedLength > 0
? new MemoryStream(capacity: expectedLength)
: new MemoryStream();
var buffer = ArrayPool<byte>.Shared.Rent(8192);
try
{
int totalWritten = 0;
while (true)
{
var bytesRead = codecStream.Read(buffer, 0, buffer.Length);
if (bytesRead <= 0)
break;
totalWritten += bytesRead;
if (totalWritten > maxDecompressedSizeBytes)
throw new InvalidDataException($"Decompressed payload exceeds max allowed size ({maxDecompressedSizeBytes} bytes).");
output.Write(buffer, 0, bytesRead);
}
if (expectedLength >= 0 && totalWritten != expectedLength)
throw new InvalidDataException($"Expected decompressed length {expectedLength}, actual {totalWritten}.");
return output.ToArray();
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}
}

View File

@@ -0,0 +1,16 @@
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Snapshot of aggregated compression and decompression telemetry.
/// </summary>
public readonly struct CompressionStats
{
public long CompressedDocumentCount { get; init; }
public long BytesBeforeCompression { get; init; }
public long BytesAfterCompression { get; init; }
public long CompressionCpuTicks { get; init; }
public long DecompressionCpuTicks { get; init; }
public long CompressionFailureCount { get; init; }
public long ChecksumFailureCount { get; init; }
public long SafetyLimitRejectionCount { get; init; }
}

View File

@@ -0,0 +1,88 @@
using System.Threading;
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Thread-safe counters for compression/decompression lifecycle events.
/// </summary>
public sealed class CompressionTelemetry
{
private long _compressionAttempts;
private long _compressionSuccesses;
private long _compressionFailures;
private long _compressionSkippedTooSmall;
private long _compressionSkippedInsufficientSavings;
private long _decompressionAttempts;
private long _decompressionSuccesses;
private long _decompressionFailures;
private long _compressionInputBytes;
private long _compressionOutputBytes;
private long _decompressionOutputBytes;
private long _compressedDocumentCount;
private long _compressionCpuTicks;
private long _decompressionCpuTicks;
private long _checksumFailureCount;
private long _safetyLimitRejectionCount;
public long CompressionAttempts => Interlocked.Read(ref _compressionAttempts);
public long CompressionSuccesses => Interlocked.Read(ref _compressionSuccesses);
public long CompressionFailures => Interlocked.Read(ref _compressionFailures);
public long CompressionSkippedTooSmall => Interlocked.Read(ref _compressionSkippedTooSmall);
public long CompressionSkippedInsufficientSavings => Interlocked.Read(ref _compressionSkippedInsufficientSavings);
public long DecompressionAttempts => Interlocked.Read(ref _decompressionAttempts);
public long DecompressionSuccesses => Interlocked.Read(ref _decompressionSuccesses);
public long DecompressionFailures => Interlocked.Read(ref _decompressionFailures);
public long CompressionInputBytes => Interlocked.Read(ref _compressionInputBytes);
public long CompressionOutputBytes => Interlocked.Read(ref _compressionOutputBytes);
public long DecompressionOutputBytes => Interlocked.Read(ref _decompressionOutputBytes);
public long CompressedDocumentCount => Interlocked.Read(ref _compressedDocumentCount);
public long CompressionCpuTicks => Interlocked.Read(ref _compressionCpuTicks);
public long DecompressionCpuTicks => Interlocked.Read(ref _decompressionCpuTicks);
public long ChecksumFailureCount => Interlocked.Read(ref _checksumFailureCount);
public long SafetyLimitRejectionCount => Interlocked.Read(ref _safetyLimitRejectionCount);
public void RecordCompressionAttempt(int inputBytes)
{
Interlocked.Increment(ref _compressionAttempts);
Interlocked.Add(ref _compressionInputBytes, inputBytes);
}
public void RecordCompressionSuccess(int outputBytes)
{
Interlocked.Increment(ref _compressionSuccesses);
Interlocked.Increment(ref _compressedDocumentCount);
Interlocked.Add(ref _compressionOutputBytes, outputBytes);
}
public void RecordCompressionFailure() => Interlocked.Increment(ref _compressionFailures);
public void RecordCompressionSkippedTooSmall() => Interlocked.Increment(ref _compressionSkippedTooSmall);
public void RecordCompressionSkippedInsufficientSavings() => Interlocked.Increment(ref _compressionSkippedInsufficientSavings);
public void RecordDecompressionAttempt() => Interlocked.Increment(ref _decompressionAttempts);
public void RecordCompressionCpuTicks(long ticks) => Interlocked.Add(ref _compressionCpuTicks, ticks);
public void RecordDecompressionCpuTicks(long ticks) => Interlocked.Add(ref _decompressionCpuTicks, ticks);
public void RecordChecksumFailure() => Interlocked.Increment(ref _checksumFailureCount);
public void RecordSafetyLimitRejection() => Interlocked.Increment(ref _safetyLimitRejectionCount);
public void RecordDecompressionSuccess(int outputBytes)
{
Interlocked.Increment(ref _decompressionSuccesses);
Interlocked.Add(ref _decompressionOutputBytes, outputBytes);
}
public void RecordDecompressionFailure() => Interlocked.Increment(ref _decompressionFailures);
public CompressionStats GetSnapshot()
{
return new CompressionStats
{
CompressedDocumentCount = CompressedDocumentCount,
BytesBeforeCompression = CompressionInputBytes,
BytesAfterCompression = CompressionOutputBytes,
CompressionCpuTicks = CompressionCpuTicks,
DecompressionCpuTicks = DecompressionCpuTicks,
CompressionFailureCount = CompressionFailures,
ChecksumFailureCount = ChecksumFailureCount,
SafetyLimitRejectionCount = SafetyLimitRejectionCount
};
}
}

View File

@@ -0,0 +1,24 @@
using System.IO.Compression;
namespace ZB.MOM.WW.CBDD.Core.Compression;
/// <summary>
/// Codec abstraction for payload compression and decompression.
/// </summary>
public interface ICompressionCodec
{
/// <summary>
/// Codec identifier.
/// </summary>
CompressionCodec Codec { get; }
/// <summary>
/// Compresses input bytes.
/// </summary>
byte[] Compress(ReadOnlySpan<byte> input, CompressionLevel level);
/// <summary>
/// Decompresses payload bytes with output bounds validation.
/// </summary>
byte[] Decompress(ReadOnlySpan<byte> input, int expectedLength, int maxDecompressedSizeBytes);
}