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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
#include <qpdf/Pl_RunLength.hh>
#include <qpdf/QTC.hh>
#include <qpdf/Util.hh>
using namespace qpdf;
namespace
{
unsigned long long memory_limit{0};
} // namespace
class Pl_RunLength::Members
{
public:
Members(action_e action) :
action(action)
{
}
Members(Members const&) = delete;
~Members() = default;
action_e action;
state_e state{st_top};
unsigned char buf[128];
unsigned int length{0};
std::string out;
};
Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e action) :
Pipeline(identifier, next),
m(std::make_unique<Members>(action))
{
util::assertion(next, "Attempt to create Pl_RunLength with nullptr as next");
}
void
Pl_RunLength::setMemoryLimit(unsigned long long limit)
{
memory_limit = limit;
}
Pl_RunLength::~Pl_RunLength() = default;
void
Pl_RunLength::write(unsigned char const* data, size_t len)
{
if (m->action == a_encode) {
encode(data, len);
} else {
decode(data, len);
}
}
void
Pl_RunLength::encode(unsigned char const* data, size_t len)
{
for (size_t i = 0; i < len; ++i) {
util::assertion(
(m->state == st_top) == (m->length <= 1),
"Pl_RunLength::encode: state/length inconsistency");
unsigned char ch = data[i];
if (m->length > 0 && (m->state == st_copying || m->length < 128) &&
ch == m->buf[m->length - 1]) {
QTC::TC("libtests", "Pl_RunLength: switch to run", (m->length == 128) ? 0 : 1);
if (m->state == st_copying) {
--m->length;
flush_encode();
m->buf[0] = ch;
m->length = 1;
}
m->state = st_run;
m->buf[m->length] = ch;
++m->length;
} else {
if (m->length == 128 || m->state == st_run) {
flush_encode();
} else if (m->length > 0) {
m->state = st_copying;
}
m->buf[m->length] = ch;
++m->length;
}
}
}
void
Pl_RunLength::decode(unsigned char const* data, size_t len)
{
util::no_ci_rt_error_if(
memory_limit && (len + m->out.size()) > memory_limit, "Pl_RunLength memory limit exceeded");
m->out.reserve(len);
for (size_t i = 0; i < len; ++i) {
unsigned char const& ch = data[i];
switch (m->state) {
case st_top:
if (ch < 128) {
// length represents remaining number of bytes to copy
m->length = 1U + ch;
m->state = st_copying;
} else if (ch > 128) {
// length represents number of copies of next byte
m->length = 257U - ch;
m->state = st_run;
} else // ch == 128
{
// EOD; stay in this state
}
break;
case st_copying:
m->out.append(1, static_cast<char>(ch));
if (--m->length == 0) {
m->state = st_top;
}
break;
case st_run:
m->out.append(m->length, static_cast<char>(ch));
m->state = st_top;
break;
}
}
}
void
Pl_RunLength::flush_encode()
{
if (m->length == 128) {
QTC::TC(
"libtests",
"Pl_RunLength flush full buffer",
(m->state == st_copying ? 0
: m->state == st_run ? 1
: -1));
}
if (m->length == 0) {
QTC::TC("libtests", "Pl_RunLength flush empty buffer");
}
if (m->state == st_run) {
util::assertion(
!(m->length < 2 || m->length > 128),
"Pl_RunLength: invalid length in flush_encode for run");
auto ch = static_cast<unsigned char>(257 - m->length);
next()->write(&ch, 1);
next()->write(&m->buf[0], 1);
} else if (m->length > 0) {
auto ch = static_cast<unsigned char>(m->length - 1);
next()->write(&ch, 1);
next()->write(m->buf, m->length);
}
m->state = st_top;
m->length = 0;
}
void
Pl_RunLength::finish()
{
// When decoding, we might have read a length byte not followed by data, which means the stream
// was terminated early, but we will just ignore this case since this is the only sensible thing
// to do.
if (m->action == a_encode) {
flush_encode();
unsigned char ch = 128;
next()->write(&ch, 1);
} else {
if (memory_limit && (m->out.size()) > memory_limit) {
throw std::runtime_error("Pl_RunLength memory limit exceeded");
}
next()->writeString(m->out);
}
next()->finish();
}
|