Add ability to initialize Pl_Function with a C-style function

This commit is contained in:
Jay Berkenbilt 2022-09-08 16:38:36 -04:00
parent 5911a348a5
commit b0f054e600
4 changed files with 119 additions and 0 deletions

View File

@ -31,6 +31,9 @@
//
// It is okay to not call finish() on this pipeline if it has no
// "next".
//
// It is okay to keep calling write() after a previous write throws an
// exception as long as the delegated function allows it.
#include <qpdf/Pipeline.hh>
@ -41,8 +44,26 @@ class QPDF_DLL_CLASS Pl_Function: public Pipeline
public:
typedef std::function<void(unsigned char const*, size_t)> writer_t;
// The supplied function is called every time write is called.
QPDF_DLL
Pl_Function(char const* identifier, Pipeline* next, writer_t fn);
// The supplied C-style function is called every time write is
// called. The udata option is passed into the function with each
// call. If the function returns a non-zero value, a runtime error
// is thrown.
typedef int (*writer_c_t)(unsigned char const*, size_t, void*);
QPDF_DLL
Pl_Function(
char const* identifier, Pipeline* next, writer_c_t fn, void* udata);
typedef int (*writer_c_char_t)(char const*, size_t, void*);
QPDF_DLL
Pl_Function(
char const* identifier,
Pipeline* next,
writer_c_char_t fn,
void* udata);
QPDF_DLL
virtual ~Pl_Function();

View File

@ -15,6 +15,36 @@ Pl_Function::Pl_Function(char const* identifier, Pipeline* next, writer_t fn) :
{
}
Pl_Function::Pl_Function(
char const* identifier, Pipeline* next, writer_c_t fn, void* udata) :
Pipeline(identifier, next),
m(new Members(nullptr))
{
m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) {
int code = fn(data, len, udata);
if (code != 0) {
throw std::runtime_error(
std::string(identifier) + " function returned code " +
QUtil::int_to_string(code));
}
};
}
Pl_Function::Pl_Function(
char const* identifier, Pipeline* next, writer_c_char_t fn, void* udata) :
Pipeline(identifier, next),
m(new Members(nullptr))
{
m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) {
int code = fn(reinterpret_cast<char const*>(data), len, udata);
if (code != 0) {
throw std::runtime_error(
std::string(identifier) + " function returned code " +
QUtil::int_to_string(code));
}
};
}
Pl_Function::~Pl_Function()
{
// Must be explicit and not inline -- see QPDF_DLL_CLASS in

View File

@ -5,6 +5,38 @@
#include <qpdf/Pl_String.hh>
#include <iostream>
namespace
{
struct Count
{
int count{0};
};
} // namespace
int
f(unsigned char const* data, size_t len, void* udata)
{
auto c = reinterpret_cast<Count*>(udata);
++c->count;
std::cout << "got " << data << "(" << len << ")" << std::endl;
if (c->count == 3) {
return 1;
}
return 0;
}
int
g(char const* data, size_t len, void* udata)
{
auto c = reinterpret_cast<Count*>(udata);
++c->count;
std::cout << "signed got " << data << "(" << len << ")" << std::endl;
if (c->count == 2) {
return 2;
}
return 0;
}
int
main(int argc, char* argv[])
{
@ -23,5 +55,32 @@ main(int argc, char* argv[])
p2.finish();
assert(s == "c2FsYWQ=");
Count c;
Pl_Function p3("c-function", nullptr, f, &c);
p3 << "one";
p3 << "two";
try {
p3 << "three";
assert(false);
} catch (std::runtime_error& e) {
std::cout << "three threw " << e.what() << std::endl;
}
p3 << "four";
p3.finish();
assert(c.count == 4);
c.count = 0;
Pl_Function p4("c-function", nullptr, g, &c);
p4 << "potato";
try {
p4 << "salad";
assert(false);
} catch (std::runtime_error& e) {
std::cout << "salad threw " << e.what() << std::endl;
}
p4 << "quack";
p4.finish();
assert(c.count == 3);
return 0;
}

View File

@ -1,2 +1,11 @@
p1: 6: potato
p2: 5: salad
got one(3)
got two(3)
got three(5)
three threw c-function function returned code 1
got four(4)
signed got potato(6)
signed got salad(5)
salad threw c-function function returned code 2
signed got quack(5)