aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf/qpdf/bits_functions.hh
blob: f6e385f6a212feaa075fea41715d7d1affa54d1d (plain) (blame)
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
150
151
152
#ifndef __BITS_FUNCTIONS_HH__
#define __BITS_FUNCTIONS_HH__

#include <qpdf/Pipeline.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
#include <algorithm>
#include <stdexcept>

// This file is #included by specific source files, which must define
// certain preprocessor symbols. These functions may be run at places
// where the function call overhead from test coverage testing would
// be too high. Therefore, we make the test coverage cases conditional
// upon a preprocessor symbol. Library code includes this file without
// BITS_TESTING, and the specially designed test code that fully
// exercises this code includes with the symbol defined.

#ifdef BITS_READ
static unsigned long long
read_bits(
    unsigned char const*& p,
    size_t& bit_offset,
    size_t& bits_available,
    size_t bits_wanted)
{
    // View p as a stream of bits:

    // 76543210 76543210 ....

    // bit_offset is the bit number within the first byte that marks
    // the first bit that we would read.

    if (bits_wanted > bits_available) {
        throw std::runtime_error(
            "overflow reading bit stream: wanted = " +
            QUtil::uint_to_string(bits_wanted) +
            "; available = " + QUtil::uint_to_string(bits_available));
    }
    if (bits_wanted > 32) {
        throw std::out_of_range("read_bits: too many bits requested");
    }

    unsigned long result = 0;
# ifdef BITS_TESTING
    if (bits_wanted == 0) {
        QTC::TC("libtests", "bits zero bits wanted");
    }
# endif
    while (bits_wanted > 0) {
        // Grab bits from the first byte clearing anything before
        // bit_offset.
        unsigned char byte =
            static_cast<unsigned char>(*p & ((1U << (bit_offset + 1U)) - 1U));

        // There are bit_offset + 1 bits available in the first byte.
        size_t to_copy = std::min(bits_wanted, bit_offset + 1);
        size_t leftover = (bit_offset + 1) - to_copy;

# ifdef BITS_TESTING
        QTC::TC(
            "libtests",
            "bits bit_offset",
            ((bit_offset == 0)       ? 0
                 : (bit_offset == 7) ? 1
                                     : 2));
        QTC::TC("libtests", "bits leftover", (leftover > 0) ? 1 : 0);
# endif

        // Right shift so that all the bits we want are right justified.
        byte = static_cast<unsigned char>(byte >> leftover);

        // Copy the bits into result
        result <<= to_copy;
        result |= byte;

        // Update pointers
        if (leftover) {
            bit_offset = leftover - 1;
        } else {
            bit_offset = 7;
            ++p;
        }
        bits_wanted -= to_copy;
        bits_available -= to_copy;

# ifdef BITS_TESTING
        QTC::TC(
            "libtests",
            "bits iterations",
            ((bits_wanted > 8)       ? 0
                 : (bits_wanted > 0) ? 1
                                     : 2));
# endif
    }

    return result;
}
#endif

#ifdef BITS_WRITE
static void
write_bits(
    unsigned char& ch,
    size_t& bit_offset,
    unsigned long long val,
    size_t bits,
    Pipeline* pipeline)
{
    if (bits > 32) {
        throw std::out_of_range("write_bits: too many bits requested");
    }

    // bit_offset + 1 is the number of bits left in ch
# ifdef BITS_TESTING
    if (bits == 0) {
        QTC::TC("libtests", "bits write zero bits");
    }
# endif
    while (bits > 0) {
        size_t bits_to_write = std::min(bits, bit_offset + 1);
        unsigned char newval = static_cast<unsigned char>(
            (val >> (bits - bits_to_write)) & ((1U << bits_to_write) - 1));
        size_t bits_left_in_ch = bit_offset + 1 - bits_to_write;
        newval = static_cast<unsigned char>(newval << bits_left_in_ch);
        ch |= newval;
        if (bits_left_in_ch == 0) {
# ifdef BITS_TESTING
            QTC::TC("libtests", "bits write pipeline");
# endif
            pipeline->write(&ch, 1);
            bit_offset = 7;
            ch = 0;
        } else {
# ifdef BITS_TESTING
            QTC::TC("libtests", "bits write leftover");
# endif
            bit_offset -= bits_to_write;
        }
        bits -= bits_to_write;
# ifdef BITS_TESTING
        QTC::TC(
            "libtests",
            "bits write iterations",
            ((bits > 8)       ? 0
                 : (bits > 0) ? 1
                              : 2));
# endif
    }
}
#endif

#endif // __BITS_FUNCTIONS_HH__