All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.netty.incubator.codec.http3.QpackUtil Maven / Gradle / Ivy

/*
 * Copyright 2020 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.incubator.codec.http3;

import io.netty.buffer.ByteBuf;
import io.netty.util.AsciiString;
import io.netty.util.internal.ConstantTimeUtils;
import io.netty.util.internal.PlatformDependent;

import static io.netty.util.internal.ObjectUtil.checkInRange;
import static java.lang.Math.floorDiv;

final class QpackUtil {
    private static final QpackException PREFIXED_INTEGER_TOO_LONG =
            QpackException.newStatic(QpackDecoder.class, "toIntOrThrow(...)",
                    "QPACK - invalid prefixed integer");

    /**
     * Encode integer according to
     * Section 5.1.
     */
    static void encodePrefixedInteger(ByteBuf out, byte mask, int prefixLength, long toEncode) {
        checkInRange(toEncode, 0, MAX_UNSIGNED_INT, "toEncode");
        int nbits = (1 << prefixLength) - 1;
        if (toEncode < nbits) {
            out.writeByte((byte) (mask | toEncode));
        } else {
            out.writeByte((byte) (mask | nbits));
            long remainder = toEncode - nbits;
            while (remainder > 128) {
                byte next = (byte) ((remainder % 128) | 0x80);
                out.writeByte(next);
                remainder = remainder / 128;
            }
            out.writeByte((byte) remainder);
        }
    }

    /**
     * Decode the integer or return {@code -1} if not enough bytes are readable.
     * This method increases the readerIndex when the integer could be decoded.
     *
     * @param in the input {@link ByteBuf}
     * @param prefixLength the prefix length
     * @return the integer or {@code -1} if not enough readable bytes are in the {@link ByteBuf).
     */
    static int decodePrefixedIntegerAsInt(ByteBuf in, int prefixLength) throws QpackException {
        return toIntOrThrow(decodePrefixedInteger(in, prefixLength));
    }

    /**
     * Converts the passed {@code aLong} to an {@code int} if the value can fit an {@code int}, otherwise throws a
     * {@link QpackException}.
     *
     * @param aLong to convert.
     * @throws QpackException If the value does not fit an {@code int}.
     */
    static int toIntOrThrow(long aLong) throws QpackException {
        if ((int) aLong != aLong) {
            throw PREFIXED_INTEGER_TOO_LONG;
        }
        return (int) aLong;
    }

    /**
     * Decode the integer or return {@code -1} if not enough bytes are readable.
     * This method increases the readerIndex when the integer could be decoded.
     *
     * @param in the input {@link ByteBuf}
     * @param prefixLength the prefix length
     * @return the integer or {@code -1} if not enough readable bytes are in the {@link ByteBuf).
     */
    static long decodePrefixedInteger(ByteBuf in, int prefixLength) {
        int readerIndex = in.readerIndex();
        int writerIndex = in.writerIndex();
        if (readerIndex == writerIndex) {
            return -1;
        }

        int nbits = (1 << prefixLength) - 1;
        int first = in.readByte() & nbits;
        if (first < nbits) {
            return first;
        }

        int idx = readerIndex + 1;
        long i = first;
        int factor = 0;
        byte next;
        do {
            if (idx == writerIndex) {
                in.readerIndex(readerIndex);
                return -1;
            }
            next = in.getByte(idx++);
            i += (next & 0x7fL) << factor;
            factor += 7;
        } while ((next & 0x80) == 0x80);
        in.readerIndex(idx);
        return i;
    }

    static boolean firstByteEquals(ByteBuf in, byte mask) {
        return (in.getByte(in.readerIndex()) & mask) == mask;
    }

    /**
     * Compare two {@link CharSequence} objects without leaking timing information.
     * 

* The {@code int} return type is intentional and is designed to allow cascading of constant time operations: *

     *     String s1 = "foo";
     *     String s2 = "foo";
     *     String s3 = "foo";
     *     String s4 = "goo";
     *     boolean equals = (equalsConstantTime(s1, s2) & equalsConstantTime(s3, s4)) != 0;
     * 
* @param s1 the first value. * @param s2 the second value. * @return {@code 0} if not equal. {@code 1} if equal. */ static int equalsConstantTime(CharSequence s1, CharSequence s2) { if (s1 instanceof AsciiString && s2 instanceof AsciiString) { if (s1.length() != s2.length()) { return 0; } AsciiString s1Ascii = (AsciiString) s1; AsciiString s2Ascii = (AsciiString) s2; return PlatformDependent.equalsConstantTime(s1Ascii.array(), s1Ascii.arrayOffset(), s2Ascii.array(), s2Ascii.arrayOffset(), s1.length()); } return ConstantTimeUtils.equalsConstantTime(s1, s2); } /** * Compare two {@link CharSequence}s. * @param s1 the first value. * @param s2 the second value. * @return {@code false} if not equal. {@code true} if equal. */ static boolean equalsVariableTime(CharSequence s1, CharSequence s2) { return AsciiString.contentEquals(s1, s2); } /** * Calculate the MaxEntries based on * RFC9204 Section 4.5.1.1. * * @param maxTableCapacity the maximum table capacity. * @return maxEntries. */ static long maxEntries(long maxTableCapacity) { // MaxEntries = floor( MaxTableCapacity / 32 ) return floorDiv(maxTableCapacity, 32); } // Section 6.2. Literal Header Field Representation enum IndexType { INCREMENTAL, // Section 6.2.1. Literal Header Field with Incremental Indexing NONE, // Section 6.2.2. Literal Header Field without Indexing NEVER // Section 6.2.3. Literal Header Field never Indexed } // Appendix B: Huffman Codes // https://tools.ietf.org/html/rfc7541#appendix-B static final int[] HUFFMAN_CODES = { 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec, 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb, 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc, 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22, 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc, 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef, 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1, 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed, 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4, 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee, 0x3fffffff // EOS }; static final byte[] HUFFMAN_CODE_LENGTHS = { 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, 30 // EOS }; static final int HUFFMAN_EOS = 256; static final long MIN_HEADER_TABLE_SIZE = 0; static final long MAX_UNSIGNED_INT = 0xffffffffL; static final long MAX_HEADER_TABLE_SIZE = MAX_UNSIGNED_INT; private QpackUtil() { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy