Coverage Report

Created: 2025-06-22 06:28

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