summaryrefslogtreecommitdiffstats
path: root/src/network/access/http2/bitstreams_p.h
blob: c96adb539025ec71ef3bf3e7fba34dc4668e2dc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef BITSTREAMS_P_H
#define BITSTREAMS_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists for the convenience
// of other Qt classes.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <QtCore/private/qglobal_p.h>
#include <QtCore/qdebug.h>

#include <type_traits>
#include <algorithm>
#include <vector>

QT_BEGIN_NAMESPACE

class QByteArray;

namespace HPack
{

// BitOStream works with an external buffer,
// for example, HEADERS frame.
class Q_AUTOTEST_EXPORT BitOStream
{
public:
    BitOStream(std::vector<uchar> &buffer);

    // Write 'bitLength' bits from the least significant
    // bits in 'bits' to bitstream:
    void writeBits(uchar bits, quint8 bitLength);
    // HPACK data format, we support:
    // * 32-bit integers
    // * strings
    void write(quint32 src);
    void write(QByteArrayView src, bool compressed);

    quint64 bitLength() const;
    quint64 byteLength() const;
    const uchar *begin() const;
    const uchar *end() const;

    void clear();

private:
    Q_DISABLE_COPY_MOVE(BitOStream);

    std::vector<uchar> &buffer;
    quint64 bitsSet;
};

class Q_AUTOTEST_EXPORT BitIStream
{
public:
    // Error is set by 'read' functions.
    // 'peek' does not set the error,
    // since it just peeks some bits
    // without the notion of wrong/right.
    // 'read' functions only change 'streamOffset'
    // on success.
    enum class Error
    {
        NoError,
        NotEnoughData,
        CompressionError,
        InvalidInteger
    };

    BitIStream();
    BitIStream(const uchar *f, const uchar *l);

    quint64 bitLength() const;
    bool hasMoreBits() const;

    // peekBits tries to read 'length' bits from the bitstream into
    // 'dst' ('length' must be <= sizeof(dst) * 8), packing them
    // starting from the most significant bit of the most significant
    // byte. It's a template so that we can use it with different
    // integer types. Returns the number of bits actually read.
    // Does not change stream's offset.

    template<class T>
    quint64 peekBits(quint64 from, quint64 length, T *dstPtr) const
    {
        static_assert(std::is_unsigned<T>::value, "peekBits: unsigned integer type expected");

        Q_ASSERT(dstPtr);
        Q_ASSERT(length <= sizeof(T) * 8);

        if (from >= bitLength() || !length)
            return 0;

        T &dst = *dstPtr;
        dst = T();
        length = std::min(length, bitLength() - from);

        const uchar *srcByte = first + from / 8;
        auto bitsToRead = length + from % 8;

        while (bitsToRead > 8) {
            dst = (dst << 8) | *srcByte;
            bitsToRead -= 8;
            ++srcByte;
        }

        dst <<= bitsToRead;
        dst |= *srcByte >> (8 - bitsToRead);
        dst <<= sizeof(T) * 8 - length;

        return length;
    }

    quint64 streamOffset() const
    {
        return offset;
    }

    bool skipBits(quint64 nBits);
    bool rewindOffset(quint64 nBits);

    bool read(quint32 *dstPtr);
    bool read(QByteArray *dstPtr);

    Error error() const;

private:
    void setError(Error newState);

    const uchar *first;
    const uchar *last;
    quint64 offset;
    Error streamError;
};

} // namespace HPack

QT_END_NAMESPACE

#endif