File: System\IO\Compression\BrotliUtils.cs
Web Access
Project: System.IO.Compression.Brotli.csproj (System.IO.Compression.Brotli)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers;

namespace System.IO.Compression
{
    internal static partial class BrotliUtils
    {
        public const int WindowBits_Min = 10;
        public const int WindowBits_Default = 22;
        public const int WindowBits_Max = 24;
        /// <summary>The maximum window size supported by the "Large Window Brotli" extension (BROTLI_LARGE_MAX_WINDOW_BITS).</summary>
        public const int WindowBits_MaxLarge = 30;
        /// <summary>
        /// The default value of <see cref="BrotliDecompressionOptions.MaxWindowLog2"/>: the RFC 7932 ceiling, so decompression
        /// behaves as it did before "Large Window Brotli" support was added unless a caller opts into a higher value.
        /// </summary>
        public const int WindowBits_DefaultLarge = WindowBits_Max;
        public const int Quality_Min = 0;
        public const int Quality_Default = 4;
        public const int Quality_Max = 11;

        /// <summary>
        /// Reads the window size declared by the header of a Brotli stream without consuming any input.
        /// </summary>
        /// <param name="source">A buffer positioned at the start of a Brotli stream.</param>
        /// <param name="windowBits">When this method returns <see cref="OperationStatus.Done"/>, the declared base-2 logarithm of the window size.</param>
        /// <param name="largeWindowEnabled">
        /// <see langword="true"/> if the decoder has "Large Window Brotli" enabled; otherwise, <see langword="false"/>.
        /// </param>
        /// <returns>
        /// <see cref="OperationStatus.Done"/> if the window size was determined; <see cref="OperationStatus.NeedMoreData"/> if
        /// <paramref name="source"/> is too short to determine it; otherwise <see cref="OperationStatus.InvalidData"/>.
        /// </returns>
        /// <remarks>
        /// The header is read least-significant-bit first and occupies at most 14 bits. This mirrors DecodeWindowBits and the
        /// BROTLI_STATE_LARGE_WINDOW_BITS state of the native decoder: when <paramref name="largeWindowEnabled"/> is
        /// <see langword="false"/>, the "Large Window Brotli" escape is rejected as soon as it is recognized, without waiting
        /// for the byte that would otherwise carry the window size, matching the native decoder's behavior.
        /// </remarks>
        internal static OperationStatus TryGetWindowBits(ReadOnlySpan<byte> source, out int windowBits, bool largeWindowEnabled)
        {
            // The header is a variable-length prefix, read least-significant-bit first:
            //   bit 0 == 0         -> window is 16
            //   bits 1..3 (n) != 0 -> window is 17 + n (18..24)
            //   bits 4..6 (n) == 1 -> "Large Window Brotli" escape: bit 7 is reserved and must be zero,
            //                         and bits 8..13 hold the window size (10..30)
            //   bits 4..6 (n) > 1  -> window is 8 + n (10..15)
            //   bits 4..6 (n) == 0 -> window is 17
            const int LargeWindowEscape = 1;
            const int LargeWindowBitCount = 6;

            windowBits = 0;

            if (source.IsEmpty)
            {
                return OperationStatus.NeedMoreData;
            }

            int header = source[0];
            if ((header & 0b1) == 0)
            {
                windowBits = 16;
                return OperationStatus.Done;
            }

            int value = (header >> 1) & 0b111;
            if (value != 0)
            {
                windowBits = 17 + value;
                return OperationStatus.Done;
            }

            value = (header >> 4) & 0b111;
            if (value != LargeWindowEscape)
            {
                windowBits = value != 0 ? 8 + value : 17;
                return OperationStatus.Done;
            }

            // The native decoder rejects the escape outright when Large Window Brotli is disabled, without ever looking at
            // the reserved bit or the window size that would otherwise follow.
            if (!largeWindowEnabled)
            {
                return OperationStatus.InvalidData;
            }

            // The escape is followed by a reserved bit that must be zero, and then by the window size itself.
            if ((header & 0b1000_0000) != 0)
            {
                return OperationStatus.InvalidData;
            }

            if (source.Length < 2)
            {
                return OperationStatus.NeedMoreData;
            }

            windowBits = source[1] & ((1 << LargeWindowBitCount) - 1);
            return windowBits is < WindowBits_Min or > WindowBits_MaxLarge
                ? OperationStatus.InvalidData
                : OperationStatus.Done;
        }

        internal static int GetQualityFromCompressionLevel(CompressionLevel compressionLevel) =>
            compressionLevel switch
            {
                CompressionLevel.NoCompression => Quality_Min,
                CompressionLevel.Fastest => 1,
                CompressionLevel.Optimal => Quality_Default,
                CompressionLevel.SmallestSize => Quality_Max,
                _ => throw new ArgumentOutOfRangeException(nameof(compressionLevel), compressionLevel, SR.ArgumentOutOfRange_Enum)
            };
    }
}