< Summary

Information
Class: Chronicler.ChronicleHashWriter
Assembly: Chronicler
File(s): /home/runner/work/Chronicler/Chronicler/src/Chronicler/Recording/ChronicleHashWriter.cs
Line coverage
100%
Covered lines: 86
Uncovered lines: 0
Coverable lines: 86
Total lines: 242
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
WriteSection(...)100%1010100%
WriteBool(...)100%11100%
WriteByte(...)100%11100%
WriteSByte(...)100%11100%
WriteInt16(...)100%11100%
WriteUInt16(...)100%11100%
WriteInt32(...)100%11100%
WriteUInt32(...)100%11100%
WriteInt64(...)100%11100%
WriteUInt64(...)100%11100%
WriteChar(...)100%11100%
WriteString(...)100%44100%
WriteEnum(...)100%66100%
ToHash()100%11100%
EnsureInitialized()100%22100%
FinalizeLane(...)100%11100%

File(s)

/home/runner/work/Chronicler/Chronicler/src/Chronicler/Recording/ChronicleHashWriter.cs

#LineLine coverage
 1using System;
 2using System.Runtime.CompilerServices;
 3
 4namespace Chronicler;
 5
 6/// <summary>
 7/// Writes deterministic primitive payloads into a fixed-width record hash.
 8/// </summary>
 9/// <remarks>
 10/// This is a non-cryptographic conformance/replay hash writer. It is intended for deterministic
 11/// state comparison, not for security or tamper resistance.
 12/// </remarks>
 13public struct ChronicleHashWriter
 14{
 15    private const ulong FnvPrime = 1099511628211UL;
 16    private const ulong LowOffset = 14695981039346656037UL;
 17    private const ulong HighOffset = 7809847782465536322UL;
 18
 19    private ulong _low;
 20    private ulong _high;
 21    private bool _initialized;
 22
 23    /// <summary>
 24    /// Creates a writer seeded for an empty deterministic hash stream.
 25    /// </summary>
 26    public ChronicleHashWriter()
 27    {
 11066428        _low = LowOffset;
 11066429        _high = HighOffset;
 11066430        _initialized = true;
 11066431    }
 32
 33    /// <summary>
 34    /// Writes a stable ASCII section tag and schema version.
 35    /// </summary>
 36    public void WriteSection(string tag, int version)
 37    {
 47945038        if (tag == null)
 139            throw new ArgumentNullException(nameof(tag));
 47944940        if (tag.Length == 0)
 141            throw new ArgumentException("Record hash section tags must not be empty.", nameof(tag));
 42
 1785011443        for (int i = 0; i < tag.Length; i++)
 44        {
 844561045            if (tag[i] > 0x7f)
 146                throw new ArgumentException("Record hash section tags must be stable ASCII.", nameof(tag));
 47        }
 48
 47944749        WriteInt32(tag.Length);
 1785010850        for (int i = 0; i < tag.Length; i++)
 51        {
 844560752            WriteByte((byte)tag[i]);
 53        }
 54
 47944755        WriteInt32(version);
 47944756    }
 57
 58    /// <summary>
 59    /// Writes a Boolean value as one canonical byte.
 60    /// </summary>
 61    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 62    public void WriteBool(bool value)
 63    {
 162347264        WriteByte(value ? (byte)1 : (byte)0);
 162347265    }
 66
 67    /// <summary>
 68    /// Writes an unsigned 8-bit value.
 69    /// </summary>
 70    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 71    public void WriteByte(byte value)
 72    {
 6846545673        EnsureInitialized();
 74
 75        unchecked
 76        {
 6846545677            _low ^= value;
 6846545678            _low *= FnvPrime;
 79
 6846545680            _high ^= (ulong)value + 0x9e3779b97f4a7c15UL + (_low << 6) + (_low >> 2);
 6846545681            _high *= FnvPrime;
 82        }
 6846545683    }
 84
 85    /// <summary>
 86    /// Writes a signed 8-bit value by its two's-complement byte.
 87    /// </summary>
 88    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 89    public void WriteSByte(sbyte value)
 90    {
 3689491        WriteByte(unchecked((byte)value));
 3689492    }
 93
 94    /// <summary>
 95    /// Writes a signed 16-bit value in little-endian byte order.
 96    /// </summary>
 97    public void WriteInt16(short value)
 98    {
 3689599        WriteUInt16(unchecked((ushort)value));
 36895100    }
 101
 102    /// <summary>
 103    /// Writes an unsigned 16-bit value in little-endian byte order.
 104    /// </summary>
 105    public void WriteUInt16(ushort value)
 106    {
 24088830107        WriteByte((byte)value);
 24088830108        WriteByte((byte)(value >> 8));
 24088830109    }
 110
 111    /// <summary>
 112    /// Writes a signed 32-bit value in little-endian byte order.
 113    /// </summary>
 114    public void WriteInt32(int value)
 115    {
 2102621116        WriteUInt32(unchecked((uint)value));
 2102621117    }
 118
 119    /// <summary>
 120    /// Writes an unsigned 32-bit value in little-endian byte order.
 121    /// </summary>
 122    public void WriteUInt32(uint value)
 123    {
 2250159124        WriteByte((byte)value);
 2250159125        WriteByte((byte)(value >> 8));
 2250159126        WriteByte((byte)(value >> 16));
 2250159127        WriteByte((byte)(value >> 24));
 2250159128    }
 129
 130    /// <summary>
 131    /// Writes a signed 64-bit value in little-endian byte order.
 132    /// </summary>
 133    public void WriteInt64(long value)
 134    {
 36895135        WriteUInt64(unchecked((ulong)value));
 36895136    }
 137
 138    /// <summary>
 139    /// Writes an unsigned 64-bit value in little-endian byte order.
 140    /// </summary>
 141    public void WriteUInt64(ulong value)
 142    {
 73832143        WriteByte((byte)value);
 73832144        WriteByte((byte)(value >> 8));
 73832145        WriteByte((byte)(value >> 16));
 73832146        WriteByte((byte)(value >> 24));
 73832147        WriteByte((byte)(value >> 32));
 73832148        WriteByte((byte)(value >> 40));
 73832149        WriteByte((byte)(value >> 48));
 73832150        WriteByte((byte)(value >> 56));
 73832151    }
 152
 153    /// <summary>
 154    /// Writes a UTF-16 code unit in little-endian byte order.
 155    /// </summary>
 156    public void WriteChar(char value)
 157    {
 24014998158        WriteUInt16(value);
 24014998159    }
 160
 161    /// <summary>
 162    /// Writes a nullable string as a presence marker, length, and UTF-16 code units.
 163    /// </summary>
 164    public void WriteString(string? value)
 165    {
 996186166        if (value == null)
 167        {
 36894168            WriteBool(false);
 36894169            return;
 170        }
 171
 959292172        WriteBool(true);
 959292173        WriteInt32(value.Length);
 49874788174        for (int i = 0; i < value.Length; i++)
 175        {
 23978102176            WriteChar(value[i]);
 177        }
 959292178    }
 179
 180    /// <summary>
 181    /// Writes an enum value by its underlying integral bytes in little-endian byte order.
 182    /// </summary>
 183    public void WriteEnum<TEnum>(TEnum value)
 184        where TEnum : struct, Enum
 185    {
 36881186        if (Unsafe.SizeOf<TEnum>() == 1)
 187        {
 2188            WriteByte(Unsafe.As<TEnum, byte>(ref value));
 2189            return;
 190        }
 191
 36879192        if (Unsafe.SizeOf<TEnum>() == 2)
 193        {
 2194            WriteUInt16(Unsafe.As<TEnum, ushort>(ref value));
 2195            return;
 196        }
 197
 36877198        if (Unsafe.SizeOf<TEnum>() == 4)
 199        {
 36875200            WriteUInt32(Unsafe.As<TEnum, uint>(ref value));
 36875201            return;
 202        }
 203
 2204        WriteUInt64(Unsafe.As<TEnum, ulong>(ref value));
 2205    }
 206
 207    /// <summary>
 208    /// Finalizes and returns the current hash value.
 209    /// </summary>
 210    public ChronicleHash ToHash()
 211    {
 110660212        EnsureInitialized();
 213
 110660214        ulong low = FinalizeLane(_low);
 110660215        ulong high = FinalizeLane(_high ^ low);
 110660216        return new ChronicleHash(low, high);
 217    }
 218
 219    [MethodImpl(MethodImplOptions.AggressiveInlining)]
 220    private void EnsureInitialized()
 221    {
 68576116222        if (_initialized)
 68576115223            return;
 224
 1225        _low = LowOffset;
 1226        _high = HighOffset;
 1227        _initialized = true;
 1228    }
 229
 230    private static ulong FinalizeLane(ulong value)
 231    {
 232        unchecked
 233        {
 221320234            value ^= value >> 33;
 221320235            value *= 0xff51afd7ed558ccdUL;
 221320236            value ^= value >> 33;
 221320237            value *= 0xc4ceb9fe1a85ec53UL;
 221320238            value ^= value >> 33;
 221320239            return value;
 240        }
 241    }
 242}