Coverage Report

Created: 2026-05-30 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qpdf/libqpdf/Pl_LZWDecoder.cc
Line
Count
Source
1
#include <qpdf/Pl_LZWDecoder.hh>
2
3
#include <qpdf/QIntC.hh>
4
#include <qpdf/QTC.hh>
5
#include <qpdf/Util.hh>
6
#include <cstring>
7
#include <stdexcept>
8
9
using namespace qpdf;
10
11
Pl_LZWDecoder::Pl_LZWDecoder(char const* identifier, Pipeline* next, bool early_code_change) :
12
2.44k
    Pipeline(identifier, next),
13
2.44k
    code_change_delta(early_code_change)
14
2.44k
{
15
2.44k
    util::assertion(next, "Attempt to create Pl_LZWDecoder with nullptr as next");
16
2.44k
}
17
18
void
19
Pl_LZWDecoder::write(unsigned char const* bytes, size_t len)
20
21.2k
{
21
4.30M
    for (size_t i = 0; i < len; ++i) {
22
4.28M
        buf[next_char_++] = bytes[i];
23
4.28M
        if (next_char_ == 3) {
24
1.42M
            next_char_ = 0;
25
1.42M
        }
26
4.28M
        bits_available += 8;
27
4.28M
        if (bits_available >= code_size) {
28
3.47M
            sendNextCode();
29
3.47M
        }
30
4.28M
    }
31
21.2k
}
32
33
void
34
Pl_LZWDecoder::finish()
35
2.33k
{
36
2.33k
    next()->finish();
37
2.33k
}
38
39
void
40
Pl_LZWDecoder::sendNextCode()
41
3.47M
{
42
3.47M
    unsigned int high = byte_pos;
43
3.47M
    unsigned int med = (byte_pos + 1) % 3;
44
3.47M
    unsigned int low = (byte_pos + 2) % 3;
45
46
3.47M
    unsigned int bits_from_high = 8 - bit_pos;
47
3.47M
    unsigned int bits_from_med = code_size - bits_from_high;
48
3.47M
    unsigned int bits_from_low = 0;
49
3.47M
    if (bits_from_med > 8) {
50
495k
        bits_from_low = bits_from_med - 8;
51
495k
        bits_from_med = 8;
52
495k
    }
53
3.47M
    unsigned int high_mask = (1U << bits_from_high) - 1U;
54
3.47M
    unsigned int med_mask = 0xff - ((1U << (8 - bits_from_med)) - 1U);
55
3.47M
    unsigned int low_mask = 0xff - ((1U << (8 - bits_from_low)) - 1U);
56
3.47M
    unsigned int code = 0;
57
3.47M
    code += (buf[high] & high_mask) << bits_from_med;
58
3.47M
    code += ((buf[med] & med_mask) >> (8 - bits_from_med));
59
3.47M
    if (bits_from_low) {
60
495k
        code <<= bits_from_low;
61
495k
        code += ((buf[low] & low_mask) >> (8 - bits_from_low));
62
495k
        byte_pos = low;
63
495k
        bit_pos = bits_from_low;
64
2.97M
    } else {
65
2.97M
        byte_pos = med;
66
2.97M
        bit_pos = bits_from_med;
67
2.97M
    }
68
3.47M
    if (bit_pos == 8) {
69
315k
        bit_pos = 0;
70
315k
        ++byte_pos;
71
315k
        byte_pos %= 3;
72
315k
    }
73
3.47M
    bits_available -= code_size;
74
75
3.47M
    handleCode(code);
76
3.47M
}
77
78
unsigned char
79
Pl_LZWDecoder::getFirstChar(unsigned int code)
80
12.7k
{
81
12.7k
    if (code < 256) {
82
219
        return static_cast<unsigned char>(code);
83
219
    }
84
12.5k
    util::no_ci_rt_error_if(
85
12.5k
        code <= 257,
86
12.5k
        "Pl_LZWDecoder::getFirstChar called with invalid code (" + std::to_string(code) + ")");
87
88
12.5k
    unsigned int idx = code - 258;
89
12.5k
    util::no_ci_rt_error_if(idx >= table.size(), "Pl_LZWDecoder::getFirstChar: table overflow");
90
12.5k
    Buffer& b = table.at(idx);
91
12.5k
    return b.getBuffer()[0];
92
12.7k
}
93
94
void
95
Pl_LZWDecoder::addToTable(unsigned char c)
96
3.41M
{
97
3.41M
    unsigned int last_size = 0;
98
3.41M
    unsigned char const* last_data = nullptr;
99
3.41M
    unsigned char tmp[1];
100
101
3.41M
    if (last_code < 256) {
102
3.39M
        tmp[0] = static_cast<unsigned char>(last_code);
103
3.39M
        last_data = tmp;
104
3.39M
        last_size = 1;
105
3.39M
    } else {
106
12.6k
        util::no_ci_rt_error_if(
107
12.6k
            last_code <= 257,
108
12.6k
            "Pl_LZWDecoder::addToTable called with invalid code (" + std::to_string(last_code) +
109
12.6k
                ")");
110
12.6k
        unsigned int idx = last_code - 258;
111
12.6k
        util::no_ci_rt_error_if(idx >= table.size(), "Pl_LZWDecoder::addToTable: table overflow");
112
12.6k
        Buffer& b = table.at(idx);
113
12.6k
        last_data = b.getBuffer();
114
12.6k
        last_size = QIntC::to_uint(b.getSize());
115
12.6k
    }
116
117
3.41M
    Buffer entry(1 + last_size);
118
3.41M
    unsigned char* new_data = entry.getBuffer();
119
3.41M
    memcpy(new_data, last_data, last_size);
120
3.41M
    new_data[last_size] = c;
121
3.41M
    table.push_back(std::move(entry));
122
3.41M
}
123
124
void
125
Pl_LZWDecoder::handleCode(unsigned int code)
126
3.47M
{
127
3.47M
    if (eod) {
128
40.6k
        return;
129
40.6k
    }
130
131
3.43M
    if (code == 256) {
132
8.69k
        if (!table.empty()) {
133
8.39k
            QTC::TC("libtests", "Pl_LZWDecoder intermediate reset");
134
8.39k
        }
135
8.69k
        table.clear();
136
8.69k
        code_size = 9;
137
3.42M
    } else if (code == 257) {
138
123
        eod = true;
139
3.42M
    } else {
140
3.42M
        if (last_code != 256) {
141
            // Add to the table from last time.  New table entry would be what we read last plus the
142
            // first character of what we're reading now.
143
3.41M
            unsigned char next_c = '\0';
144
3.41M
            unsigned int table_size = QIntC::to_uint(table.size());
145
3.41M
            if (code < 256) {
146
                // just read < 256; last time's next_c was code
147
3.39M
                next_c = static_cast<unsigned char>(code);
148
3.39M
            } else if (code > 257) {
149
13.0k
                size_t idx = code - 258;
150
13.0k
                if (idx > table_size) {
151
298
                    throw std::runtime_error("LZWDecoder: bad code received");
152
12.7k
                } else if (idx == table_size) {
153
                    // The encoder would have just created this entry, so the first character of
154
                    // this entry would have been the same as the first character of the last entry.
155
329
                    next_c = getFirstChar(last_code);
156
12.3k
                } else {
157
12.3k
                    next_c = getFirstChar(code);
158
12.3k
                }
159
13.0k
            }
160
3.41M
            unsigned int new_idx = 258 + table_size;
161
3.41M
            util::no_ci_rt_error_if(new_idx == 4096, "LZWDecoder: table full");
162
3.41M
            addToTable(next_c);
163
3.41M
            unsigned int change_idx = new_idx + code_change_delta;
164
3.41M
            if (change_idx == 511 || change_idx == 1023 || change_idx == 2047) {
165
5.14k
                ++code_size;
166
5.14k
            }
167
3.41M
        }
168
169
3.42M
        if (code < 256) {
170
3.40M
            auto ch = static_cast<unsigned char>(code);
171
3.40M
            next()->write(&ch, 1);
172
3.40M
        } else {
173
12.7k
            unsigned int idx = code - 258;
174
12.7k
            if (idx >= table.size()) {
175
25
                throw std::runtime_error("Pl_LZWDecoder::handleCode: table overflow");
176
25
            }
177
12.7k
            Buffer& b = table.at(idx);
178
12.7k
            next()->write(b.getBuffer(), b.getSize());
179
12.7k
        }
180
3.42M
    }
181
182
3.43M
    last_code = code;
183
3.43M
}