Coverage Report

Created: 2024-09-08 06:05

/src/qpdf/libqpdf/Pl_RunLength.cc
Line
Count
Source (jump to first uncovered line)
1
#include <qpdf/Pl_RunLength.hh>
2
3
#include <qpdf/QTC.hh>
4
#include <qpdf/QUtil.hh>
5
6
Pl_RunLength::Members::Members(action_e action) :
7
    action(action),
8
    state(st_top),
9
    length(0)
10
79
{
11
79
}
12
13
Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e action) :
14
    Pipeline(identifier, next),
15
    m(new Members(action))
16
79
{
17
79
}
18
19
Pl_RunLength::~Pl_RunLength() // NOLINT (modernize-use-equals-default)
20
79
{
21
    // Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
22
79
}
23
24
void
25
Pl_RunLength::write(unsigned char const* data, size_t len)
26
79
{
27
79
    if (m->action == a_encode) {
28
0
        encode(data, len);
29
79
    } else {
30
79
        decode(data, len);
31
79
    }
32
79
}
33
34
void
35
Pl_RunLength::encode(unsigned char const* data, size_t len)
36
0
{
37
0
    for (size_t i = 0; i < len; ++i) {
38
0
        if ((m->state == st_top) != (m->length <= 1)) {
39
0
            throw std::logic_error("Pl_RunLength::encode: state/length inconsistency");
40
0
        }
41
0
        unsigned char ch = data[i];
42
0
        if ((m->length > 0) && ((m->state == st_copying) || (m->length < 128)) &&
43
0
            (ch == m->buf[m->length - 1])) {
44
0
            QTC::TC("libtests", "Pl_RunLength: switch to run", (m->length == 128) ? 0 : 1);
45
0
            if (m->state == st_copying) {
46
0
                --m->length;
47
0
                flush_encode();
48
0
                m->buf[0] = ch;
49
0
                m->length = 1;
50
0
            }
51
0
            m->state = st_run;
52
0
            m->buf[m->length] = ch;
53
0
            ++m->length;
54
0
        } else {
55
0
            if ((m->length == 128) || (m->state == st_run)) {
56
0
                flush_encode();
57
0
            } else if (m->length > 0) {
58
0
                m->state = st_copying;
59
0
            }
60
0
            m->buf[m->length] = ch;
61
0
            ++m->length;
62
0
        }
63
0
    }
64
0
}
65
66
void
67
Pl_RunLength::decode(unsigned char const* data, size_t len)
68
79
{
69
79
    m->out.reserve(len);
70
4.19M
    for (size_t i = 0; i < len; ++i) {
71
4.19M
        unsigned char const& ch = data[i];
72
4.19M
        switch (m->state) {
73
1.18M
        case st_top:
74
1.18M
            if (ch < 128) {
75
                // length represents remaining number of bytes to copy
76
1.15M
                m->length = 1U + ch;
77
1.15M
                m->state = st_copying;
78
1.15M
            } else if (ch > 128) {
79
                // length represents number of copies of next byte
80
28.3k
                m->length = 257U - ch;
81
28.3k
                m->state = st_run;
82
28.3k
            } else // ch == 128
83
425
            {
84
                // EOD; stay in this state
85
425
            }
86
1.18M
            break;
87
88
2.97M
        case st_copying:
89
2.97M
            m->out.append(1, static_cast<char>(ch));
90
2.97M
            if (--m->length == 0) {
91
1.15M
                m->state = st_top;
92
1.15M
            }
93
2.97M
            break;
94
95
28.3k
        case st_run:
96
28.3k
            m->out.append(m->length, static_cast<char>(ch));
97
28.3k
            m->state = st_top;
98
28.3k
            break;
99
4.19M
        }
100
4.19M
    }
101
79
}
102
103
void
104
Pl_RunLength::flush_encode()
105
0
{
106
0
    if (m->length == 128) {
107
0
        QTC::TC(
108
0
            "libtests",
109
0
            "Pl_RunLength flush full buffer",
110
0
            (m->state == st_copying   ? 0
111
0
                 : m->state == st_run ? 1
112
0
                                      : -1));
113
0
    }
114
0
    if (m->length == 0) {
115
0
        QTC::TC("libtests", "Pl_RunLength flush empty buffer");
116
0
    }
117
0
    if (m->state == st_run) {
118
0
        if ((m->length < 2) || (m->length > 128)) {
119
0
            throw std::logic_error("Pl_RunLength: invalid length in flush_encode for run");
120
0
        }
121
0
        auto ch = static_cast<unsigned char>(257 - m->length);
122
0
        this->getNext()->write(&ch, 1);
123
0
        this->getNext()->write(&m->buf[0], 1);
124
0
    } else if (m->length > 0) {
125
0
        auto ch = static_cast<unsigned char>(m->length - 1);
126
0
        this->getNext()->write(&ch, 1);
127
0
        this->getNext()->write(m->buf, m->length);
128
0
    }
129
0
    m->state = st_top;
130
0
    m->length = 0;
131
0
}
132
133
void
134
Pl_RunLength::finish()
135
79
{
136
    // When decoding, we might have read a length byte not followed by data, which means the stream
137
    // was terminated early, but we will just ignore this case since this is the only sensible thing
138
    // to do.
139
79
    auto next = getNext();
140
79
    if (m->action == a_encode) {
141
0
        flush_encode();
142
0
        unsigned char ch = 128;
143
0
        next->write(&ch, 1);
144
79
    } else {
145
79
        next->writeString(m->out);
146
79
    }
147
79
    next->finish();
148
79
}