Skip to content

Commit

Permalink
Implement Pl_RunLength pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
jberkenbilt committed Aug 19, 2017
1 parent e0d1cd1 commit 2d2f619
Show file tree
Hide file tree
Showing 19 changed files with 352 additions and 3 deletions.
6 changes: 3 additions & 3 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ Soon
prioritized so that we can poll all registered filters to see
whether they are capable of filtering a particular stream.

* If possible, consider adding RLE, CCITT3, CCITT4, or any other easy
filters. For some reference code that we probably can't use but
may be handy anyway, see
* If possible, consider adding CCITT3, CCITT4, or any other easy
filters. For some reference code that we probably can't use but may
be handy anyway, see
http://partners.adobe.com/public/developer/ps/sdk/index_archive.html

* If possible, support the following types of broken files:
Expand Down
42 changes: 42 additions & 0 deletions include/qpdf/Pl_RunLength.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2005-2015 Jay Berkenbilt
//
// This file is part of qpdf. This software may be distributed under
// the terms of version 2 of the Artistic License which may be found
// in the source distribution. It is provided "as is" without express
// or implied warranty.

#ifndef __PL_RUNLENGTH_HH__
#define __PL_RUNLENGTH_HH__

#include <qpdf/Pipeline.hh>

class Pl_RunLength: public Pipeline
{
public:
enum action_e { a_encode, a_decode };

QPDF_DLL
Pl_RunLength(char const* identifier, Pipeline* next,
action_e action);
QPDF_DLL
virtual ~Pl_RunLength();

QPDF_DLL
virtual void write(unsigned char* data, size_t len);
QPDF_DLL
virtual void finish();

private:
void encode(unsigned char* data, size_t len);
void decode(unsigned char* data, size_t len);
void flush_encode();

enum state_e { st_top, st_copying, st_run };

action_e action;
state_e state;
unsigned char buf[128];
unsigned int length;
};

#endif // __PL_RUNLENGTH_HH__
171 changes: 171 additions & 0 deletions libqpdf/Pl_RunLength.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#include <qpdf/Pl_RunLength.hh>

#include <qpdf/QUtil.hh>
#include <qpdf/QTC.hh>

Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next,
action_e action) :
Pipeline(identifier, next),
action(action),
state(st_top),
length(0)
{
}

Pl_RunLength::~Pl_RunLength()
{
}

void
Pl_RunLength::write(unsigned char* data, size_t len)
{
if (this->action == a_encode)
{
encode(data, len);
}
else
{
decode(data, len);
}
}

void
Pl_RunLength::encode(unsigned char* data, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
if ((this->state == st_top) != (this->length <= 1))
{
throw std::logic_error(
"Pl_RunLength::encode: state/length inconsistency");
}
unsigned char ch = data[i];
if ((this->length > 0) &&
((this->state == st_copying) || (this->length < 128)) &&
(ch == this->buf[this->length-1]))
{
QTC::TC("libtests", "Pl_RunLength: switch to run",
(this->length == 128) ? 0 : 1);
if (this->state == st_copying)
{
--this->length;
flush_encode();
this->buf[0] = ch;
this->length = 1;
}
this->state = st_run;
this->buf[this->length] = ch;
++this->length;
}
else
{
if ((this->length == 128) || (this->state == st_run))
{
flush_encode();
}
else if (this->length > 0)
{
this->state = st_copying;
}
this->buf[this->length] = ch;
++this->length;
}
}
}

void
Pl_RunLength::decode(unsigned char* data, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
unsigned char ch = data[i];
switch (this->state)
{
case st_top:
if (ch < 128)
{
// length represents remaining number of bytes to copy
this->length = 1 + ch;
this->state = st_copying;
}
else if (ch > 128)
{
// length represents number of copies of next byte
this->length = 257 - ch;
this->state = st_run;
}
else // ch == 128
{
// EOD; stay in this state
}
break;

case st_copying:
this->getNext()->write(&ch, 1);
if (--this->length == 0)
{
this->state = st_top;
}
break;

case st_run:
for (unsigned int j = 0; j < this->length; ++j)
{
this->getNext()->write(&ch, 1);
}
this->state = st_top;
break;
}
}
}

void
Pl_RunLength::flush_encode()
{
if (this->length == 128)
{
QTC::TC("libtests", "Pl_RunLength flush full buffer",
(this->state == st_copying ? 0 :
this->state == st_run ? 1 :
-1));
}
if (this->length == 0)
{
QTC::TC("libtests", "Pl_RunLength flush empty buffer");
}
if (this->state == st_run)
{
if ((this->length < 2) || (this->length > 128))
{
throw std::logic_error(
"Pl_RunLength: invalid length in flush_encode for run");
}
unsigned char ch = static_cast<unsigned char>(257 - this->length);
this->getNext()->write(&ch, 1);
this->getNext()->write(&this->buf[0], 1);
}
else if (this->length > 0)
{
unsigned char ch = static_cast<unsigned char>(this->length - 1);
this->getNext()->write(&ch, 1);
this->getNext()->write(this->buf, this->length);
}
this->state = st_top;
this->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 (this->action == a_encode)
{
flush_encode();
unsigned char ch = 128;
this->getNext()->write(&ch, 1);
}
this->getNext()->finish();
}
1 change: 1 addition & 0 deletions libqpdf/build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ SRCS_libqpdf = \
libqpdf/Pl_PNGFilter.cc \
libqpdf/Pl_QPDFTokenizer.cc \
libqpdf/Pl_RC4.cc \
libqpdf/Pl_RunLength.cc \
libqpdf/Pl_SHA2.cc \
libqpdf/Pl_StdioFile.cc \
libqpdf/QPDF.cc \
Expand Down
1 change: 1 addition & 0 deletions libtests/build.mk
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ BINS_libtests = \
qutil \
random \
rc4 \
runlength \
sha2

TARGETS_libtests = $(foreach B,$(BINS_libtests),libtests/$(OUTPUT_DIR)/$(call binname,$(B)))
Expand Down
3 changes: 3 additions & 0 deletions libtests/libtests.testcov
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ InputSource start_chars matched but not check 0
InputSource not enough bytes 0
InputSource findLast found more than one 0
InputSource found match at buf[0] 0
Pl_RunLength: switch to run 1
Pl_RunLength flush full buffer 1
Pl_RunLength flush empty buffer 0
75 changes: 75 additions & 0 deletions libtests/qtest/runlength.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env perl
require 5.008;
use warnings;
use strict;

chdir("runlength") or die "chdir testdir failed: $!\n";

require TestDriver;

my $td = new TestDriver('runlength');

cleanup();

my @files = (
"01", # basic case, ends with copy
"02", # basic case, ends with run
"03", # long run run
"04", # ends with copy, length % 128 == 0
"05", # run starts at byte 128
"empty", # empty file
);

# Create this rather than committing an empty file, which always looks
# like an error.
open(F, ">empty");
close(F);

foreach my $f (@files)
{
$td->runtest("encode $f",
{$td->COMMAND => "runlength -encode $f a"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check encoded output",
{$td->FILE => "a"},
{$td->FILE => "$f.encoded"});
$td->runtest("decode $f.encoded",
{$td->COMMAND => "runlength -decode $f.encoded a"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check decoded output",
{$td->FILE => "a"},
{$td->FILE => "$f"});
}

concatenate("01.encoded", "02.encoded", "concat.encoded");
concatenate("01", "02", "concat");

$td->runtest("decode with embedded EOD",
{$td->COMMAND => "runlength -decode concat.encoded a"},
{$td->STRING => "", $td->EXIT_STATUS => 0});
$td->runtest("check decoded output",
{$td->FILE => "a"},
{$td->FILE => "concat"});

cleanup();

$td->report(2 + (4 * scalar(@files)));

sub cleanup
{
system("rm -f a concat concat.encoded empty");
}

sub concatenate
{
my ($a, $b, $out) = @_;
open(F, ">$out");
foreach my $f ($a, $b)
{
local $/ = undef;
open(G, "<$f");
print F <G>;
close(G);
}
close(F);
}
1 change: 1 addition & 0 deletions libtests/qtest/runlength/01
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wwwwwwwwwwwwwwwwwwwwwwwwwqqqqqrstv
1 change: 1 addition & 0 deletions libtests/qtest/runlength/01.encoded
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�w�qrstv�
1 change: 1 addition & 0 deletions libtests/qtest/runlength/02
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wwwwwwwwwwwwwwwwwwwwwwwwwqqqqqrstvxxxxxxxxxxxxxxxxxxxxxxx
1 change: 1 addition & 0 deletions libtests/qtest/runlength/02.encoded
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�w�qrstv�x�
Binary file added libtests/qtest/runlength/03
Binary file not shown.
Binary file added libtests/qtest/runlength/03.encoded
Binary file not shown.
1 change: 1 addition & 0 deletions libtests/qtest/runlength/04
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab
1 change: 1 addition & 0 deletions libtests/qtest/runlength/04.encoded
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
�wabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababab�
1 change: 1 addition & 0 deletions libtests/qtest/runlength/05
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababbbbbb
1 change: 1 addition & 0 deletions libtests/qtest/runlength/05.encoded
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
~abababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababa�b�
1 change: 1 addition & 0 deletions libtests/qtest/runlength/empty.encoded
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Loading

0 comments on commit 2d2f619

Please sign in to comment.