Editor/Embedded/Compression/Lz4.cs

A small LZ4 block decoder used in the editor tools, including support for a chained 64KiB dictionary (history) as used by binary KV3 blobs. It parses LZ4 tokens, copies literals, and performs back-reference matches into the current output or the provided history buffer.

File Access
// Bounded LZ4 block decoding, including the chained 64K dictionary used by binary KV3 blobs.
#nullable enable
using System;
using System.IO;

namespace HumanoidRetargeter.EditorTools.Embedded.Compression;

internal static class Lz4
{
    internal static int Decode(ReadOnlySpan<byte> input, Span<byte> output, ReadOnlySpan<byte> history = default)
    {
        int read = 0, written = 0;
        while (read < input.Length)
        {
            var token = input[read++];
            var literals = Length(input, ref read, token >> 4);
            if (literals > input.Length - read || literals > output.Length - written) throw new InvalidDataException("Invalid LZ4 literal length.");
            input.Slice(read, literals).CopyTo(output[written..]); read += literals; written += literals;
            if (read == input.Length) break;
            if (input.Length - read < 2) throw new InvalidDataException("Truncated LZ4 match.");
            var distance = input[read] | input[read + 1] << 8; read += 2;
            var length = checked(Length(input, ref read, token & 15) + 4);
            if (distance == 0 || distance > written + history.Length || length > output.Length - written) throw new InvalidDataException("Invalid LZ4 match.");
            for (var i = 0; i < length; i++)
            {
                var from = written - distance;
                output[written++] = from < 0 ? history[history.Length + from] : output[from];
            }
        }
        return written;
    }

    static int Length(ReadOnlySpan<byte> input, ref int read, int value)
    {
        if (value != 15) return value;
        byte next;
        do
        {
            if (read == input.Length) throw new InvalidDataException("Truncated LZ4 length.");
            next = input[read++]; value = checked(value + next);
        } while (next == 255);
        return value;
    }
}