2
1
mirror of https://github.com/qpdf/qpdf.git synced 2024-12-23 03:18:59 +00:00
qpdf/libqpdf/Pl_RunLength.cc

147 lines
4.1 KiB
C++
Raw Normal View History

2017-08-16 10:26:31 +00:00
#include <qpdf/Pl_RunLength.hh>
#include <qpdf/QTC.hh>
#include <qpdf/QUtil.hh>
2017-08-16 10:26:31 +00:00
Pl_RunLength::Members::Members(action_e action) :
2017-08-16 10:26:31 +00:00
action(action),
state(st_top),
length(0)
{
}
2023-05-21 17:35:09 +00:00
Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e action) :
Pipeline(identifier, next),
m(new Members(action))
{
}
Pl_RunLength::~Pl_RunLength() // NOLINT (modernize-use-equals-default)
2017-08-16 10:26:31 +00:00
{
// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
2017-08-16 10:26:31 +00:00
}
void
Pl_RunLength::write(unsigned char const* data, size_t len)
2017-08-16 10:26:31 +00:00
{
if (m->action == a_encode) {
2017-08-16 10:26:31 +00:00
encode(data, len);
} else {
2017-08-16 10:26:31 +00:00
decode(data, len);
}
}
void
Pl_RunLength::encode(unsigned char const* data, size_t len)
2017-08-16 10:26:31 +00:00
{
for (size_t i = 0; i < len; ++i) {
if ((m->state == st_top) != (m->length <= 1)) {
2023-05-21 17:35:09 +00:00
throw std::logic_error("Pl_RunLength::encode: state/length inconsistency");
2017-08-16 10:26:31 +00:00
}
unsigned char ch = data[i];
2023-05-21 17:35:09 +00:00
if ((m->length > 0) && ((m->state == st_copying) || (m->length < 128)) &&
(ch == m->buf[m->length - 1])) {
2023-05-21 17:35:09 +00:00
QTC::TC("libtests", "Pl_RunLength: switch to run", (m->length == 128) ? 0 : 1);
if (m->state == st_copying) {
--m->length;
2017-08-16 10:26:31 +00:00
flush_encode();
m->buf[0] = ch;
m->length = 1;
2017-08-16 10:26:31 +00:00
}
m->state = st_run;
m->buf[m->length] = ch;
++m->length;
} else {
if ((m->length == 128) || (m->state == st_run)) {
2017-08-16 10:26:31 +00:00
flush_encode();
} else if (m->length > 0) {
m->state = st_copying;
2017-08-16 10:26:31 +00:00
}
m->buf[m->length] = ch;
++m->length;
2017-08-16 10:26:31 +00:00
}
}
}
void
Pl_RunLength::decode(unsigned char const* data, size_t len)
2017-08-16 10:26:31 +00:00
{
for (size_t i = 0; i < len; ++i) {
2017-08-16 10:26:31 +00:00
unsigned char ch = data[i];
switch (m->state) {
case st_top:
if (ch < 128) {
2017-08-16 10:26:31 +00:00
// length represents remaining number of bytes to copy
m->length = 1U + ch;
m->state = st_copying;
} else if (ch > 128) {
2017-08-16 10:26:31 +00:00
// length represents number of copies of next byte
m->length = 257U - ch;
m->state = st_run;
} else // ch == 128
2017-08-16 10:26:31 +00:00
{
// EOD; stay in this state
}
break;
case st_copying:
2017-08-16 10:26:31 +00:00
this->getNext()->write(&ch, 1);
if (--m->length == 0) {
m->state = st_top;
2017-08-16 10:26:31 +00:00
}
break;
case st_run:
for (unsigned int j = 0; j < m->length; ++j) {
2017-08-16 10:26:31 +00:00
this->getNext()->write(&ch, 1);
}
m->state = st_top;
2017-08-16 10:26:31 +00:00
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));
2017-08-16 10:26:31 +00:00
}
if (m->length == 0) {
2017-08-16 10:26:31 +00:00
QTC::TC("libtests", "Pl_RunLength flush empty buffer");
}
if (m->state == st_run) {
if ((m->length < 2) || (m->length > 128)) {
2023-05-21 17:35:09 +00:00
throw std::logic_error("Pl_RunLength: invalid length in flush_encode for run");
2017-08-16 10:26:31 +00:00
}
auto ch = static_cast<unsigned char>(257 - m->length);
2017-08-16 10:26:31 +00:00
this->getNext()->write(&ch, 1);
this->getNext()->write(&m->buf[0], 1);
} else if (m->length > 0) {
auto ch = static_cast<unsigned char>(m->length - 1);
2017-08-16 10:26:31 +00:00
this->getNext()->write(&ch, 1);
this->getNext()->write(m->buf, m->length);
2017-08-16 10:26:31 +00:00
}
m->state = st_top;
m->length = 0;
2017-08-16 10:26:31 +00:00
}
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) {
2017-08-16 10:26:31 +00:00
flush_encode();
unsigned char ch = 128;
this->getNext()->write(&ch, 1);
}
this->getNext()->finish();
}