Coverage Report

Created: 2024-09-08 06:06

/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
    Pipeline(identifier, next),
11
    code_size(9),
12
    next(0),
13
    byte_pos(0),
14
    bit_pos(0),
15
    bits_available(0),
16
    code_change_delta(early_code_change),
17
    eod(false),
18
    last_code(256)
19
604
{
20
604
    memset(buf, 0, 3);
21
604
}
22
23
void
24
Pl_LZWDecoder::write(unsigned char const* bytes, size_t len)
25
30.0k
{
26
326k
    for (size_t i = 0; i < len; ++i) {
27
296k
        this->buf[next++] = bytes[i];
28
296k
        if (this->next == 3) {
29
98.4k
            this->next = 0;
30
98.4k
        }
31
296k
        this->bits_available += 8;
32
296k
        if (this->bits_available >= this->code_size) {
33
236k
            sendNextCode();
34
236k
        }
35
296k
    }
36
30.0k
}
37
38
void
39
Pl_LZWDecoder::finish()
40
587
{
41
587
    getNext()->finish();
42
587
}
43
44
void
45
Pl_LZWDecoder::sendNextCode()
46
236k
{
47
236k
    unsigned int high = this->byte_pos;
48
236k
    unsigned int med = (this->byte_pos + 1) % 3;
49
236k
    unsigned int low = (this->byte_pos + 2) % 3;
50
51
236k
    unsigned int bits_from_high = 8 - this->bit_pos;
52
236k
    unsigned int bits_from_med = this->code_size - bits_from_high;
53
236k
    unsigned int bits_from_low = 0;
54
236k
    if (bits_from_med > 8) {
55
31.7k
        bits_from_low = bits_from_med - 8;
56
31.7k
        bits_from_med = 8;
57
31.7k
    }
58
236k
    unsigned int high_mask = (1U << bits_from_high) - 1U;
59
236k
    unsigned int med_mask = 0xff - ((1U << (8 - bits_from_med)) - 1U);
60
236k
    unsigned int low_mask = 0xff - ((1U << (8 - bits_from_low)) - 1U);
61
236k
    unsigned int code = 0;
62
236k
    code += (this->buf[high] & high_mask) << bits_from_med;
63
236k
    code += ((this->buf[med] & med_mask) >> (8 - bits_from_med));
64
236k
    if (bits_from_low) {
65
31.7k
        code <<= bits_from_low;
66
31.7k
        code += ((this->buf[low] & low_mask) >> (8 - bits_from_low));
67
31.7k
        this->byte_pos = low;
68
31.7k
        this->bit_pos = bits_from_low;
69
204k
    } else {
70
204k
        this->byte_pos = med;
71
204k
        this->bit_pos = bits_from_med;
72
204k
    }
73
236k
    if (this->bit_pos == 8) {
74
27.8k
        this->bit_pos = 0;
75
27.8k
        ++this->byte_pos;
76
27.8k
        this->byte_pos %= 3;
77
27.8k
    }
78
236k
    this->bits_available -= this->code_size;
79
80
236k
    handleCode(code);
81
236k
}
82
83
unsigned char
84
Pl_LZWDecoder::getFirstChar(unsigned int code)
85
85.5k
{
86
85.5k
    unsigned char result = '\0';
87
85.5k
    if (code < 256) {
88
2.69k
        result = static_cast<unsigned char>(code);
89
82.8k
    } else if (code > 257) {
90
82.8k
        unsigned int idx = code - 258;
91
82.8k
        if (idx >= table.size()) {
92
0
            throw std::runtime_error("Pl_LZWDecoder::getFirstChar: table overflow");
93
0
        }
94
82.8k
        Buffer& b = table.at(idx);
95
82.8k
        result = b.getBuffer()[0];
96
82.8k
    } else {
97
0
        throw std::runtime_error(
98
0
            "Pl_LZWDecoder::getFirstChar called with invalid code (" + std::to_string(code) + ")");
99
0
    }
100
85.5k
    return result;
101
85.5k
}
102
103
void
104
Pl_LZWDecoder::addToTable(unsigned char next)
105
228k
{
106
228k
    unsigned int last_size = 0;
107
228k
    unsigned char const* last_data = nullptr;
108
228k
    unsigned char tmp[1];
109
110
228k
    if (this->last_code < 256) {
111
142k
        tmp[0] = static_cast<unsigned char>(this->last_code);
112
142k
        last_data = tmp;
113
142k
        last_size = 1;
114
142k
    } else if (this->last_code > 257) {
115
85.1k
        unsigned int idx = this->last_code - 258;
116
85.1k
        if (idx >= table.size()) {
117
0
            throw std::runtime_error("Pl_LZWDecoder::addToTable: table overflow");
118
0
        }
119
85.1k
        Buffer& b = table.at(idx);
120
85.1k
        last_data = b.getBuffer();
121
85.1k
        last_size = QIntC::to_uint(b.getSize());
122
85.1k
    } else {
123
0
        throw std::runtime_error(
124
0
            "Pl_LZWDecoder::addToTable called with invalid code (" +
125
0
            std::to_string(this->last_code) + ")");
126
0
    }
127
128
228k
    Buffer entry(1 + last_size);
129
228k
    unsigned char* new_data = entry.getBuffer();
130
228k
    memcpy(new_data, last_data, last_size);
131
228k
    new_data[last_size] = next;
132
228k
    this->table.push_back(std::move(entry));
133
228k
}
134
135
void
136
Pl_LZWDecoder::handleCode(unsigned int code)
137
236k
{
138
236k
    if (this->eod) {
139
6.34k
        return;
140
6.34k
    }
141
142
229k
    if (code == 256) {
143
519
        if (!this->table.empty()) {
144
45
            QTC::TC("libtests", "Pl_LZWDecoder intermediate reset");
145
45
        }
146
519
        this->table.clear();
147
519
        this->code_size = 9;
148
229k
    } else if (code == 257) {
149
251
        this->eod = true;
150
228k
    } else {
151
228k
        if (this->last_code != 256) {
152
            // Add to the table from last time.  New table entry would be what we read last plus the
153
            // first character of what we're reading now.
154
228k
            unsigned char next = '\0';
155
228k
            unsigned int table_size = QIntC::to_uint(table.size());
156
228k
            if (code < 256) {
157
                // just read < 256; last time's next was code
158
142k
                next = static_cast<unsigned char>(code);
159
142k
            } else if (code > 257) {
160
85.6k
                size_t idx = code - 258;
161
85.6k
                if (idx > table_size) {
162
148
                    throw std::runtime_error("LZWDecoder: bad code received");
163
85.5k
                } else if (idx == table_size) {
164
                    // The encoder would have just created this entry, so the first character of
165
                    // this entry would have been the same as the first character of the last entry.
166
5.50k
                    QTC::TC("libtests", "Pl_LZWDecoder last was table size");
167
5.50k
                    next = getFirstChar(this->last_code);
168
80.0k
                } else {
169
80.0k
                    next = getFirstChar(code);
170
80.0k
                }
171
85.6k
            }
172
228k
            unsigned int new_idx = 258 + table_size;
173
228k
            if (new_idx == 4096) {
174
6
                throw std::runtime_error("LZWDecoder: table full");
175
6
            }
176
228k
            addToTable(next);
177
228k
            unsigned int change_idx = new_idx + code_change_delta;
178
228k
            if ((change_idx == 511) || (change_idx == 1023) || (change_idx == 2047)) {
179
418
                ++this->code_size;
180
418
            }
181
228k
        }
182
183
228k
        if (code < 256) {
184
143k
            auto ch = static_cast<unsigned char>(code);
185
143k
            getNext()->write(&ch, 1);
186
143k
        } else {
187
85.5k
            unsigned int idx = code - 258;
188
85.5k
            if (idx >= table.size()) {
189
6
                throw std::runtime_error("Pl_LZWDecoder::handleCode: table overflow");
190
6
            }
191
85.5k
            Buffer& b = table.at(idx);
192
85.5k
            getNext()->write(b.getBuffer(), b.getSize());
193
85.5k
        }
194
228k
    }
195
196
229k
    this->last_code = code;
197
229k
}