Commit b0f054e600bd07f540ac0a081dd8b5a8cc561617

Authored by Jay Berkenbilt
1 parent 5911a348

Add ability to initialize Pl_Function with a C-style function

include/qpdf/Pl_Function.hh
@@ -31,6 +31,9 @@ @@ -31,6 +31,9 @@
31 // 31 //
32 // It is okay to not call finish() on this pipeline if it has no 32 // It is okay to not call finish() on this pipeline if it has no
33 // "next". 33 // "next".
  34 +//
  35 +// It is okay to keep calling write() after a previous write throws an
  36 +// exception as long as the delegated function allows it.
34 37
35 #include <qpdf/Pipeline.hh> 38 #include <qpdf/Pipeline.hh>
36 39
@@ -41,8 +44,26 @@ class QPDF_DLL_CLASS Pl_Function: public Pipeline @@ -41,8 +44,26 @@ class QPDF_DLL_CLASS Pl_Function: public Pipeline
41 public: 44 public:
42 typedef std::function<void(unsigned char const*, size_t)> writer_t; 45 typedef std::function<void(unsigned char const*, size_t)> writer_t;
43 46
  47 + // The supplied function is called every time write is called.
44 QPDF_DLL 48 QPDF_DLL
45 Pl_Function(char const* identifier, Pipeline* next, writer_t fn); 49 Pl_Function(char const* identifier, Pipeline* next, writer_t fn);
  50 +
  51 + // The supplied C-style function is called every time write is
  52 + // called. The udata option is passed into the function with each
  53 + // call. If the function returns a non-zero value, a runtime error
  54 + // is thrown.
  55 + typedef int (*writer_c_t)(unsigned char const*, size_t, void*);
  56 + QPDF_DLL
  57 + Pl_Function(
  58 + char const* identifier, Pipeline* next, writer_c_t fn, void* udata);
  59 + typedef int (*writer_c_char_t)(char const*, size_t, void*);
  60 + QPDF_DLL
  61 + Pl_Function(
  62 + char const* identifier,
  63 + Pipeline* next,
  64 + writer_c_char_t fn,
  65 + void* udata);
  66 +
46 QPDF_DLL 67 QPDF_DLL
47 virtual ~Pl_Function(); 68 virtual ~Pl_Function();
48 69
libqpdf/Pl_Function.cc
@@ -15,6 +15,36 @@ Pl_Function::Pl_Function(char const* identifier, Pipeline* next, writer_t fn) : @@ -15,6 +15,36 @@ Pl_Function::Pl_Function(char const* identifier, Pipeline* next, writer_t fn) :
15 { 15 {
16 } 16 }
17 17
  18 +Pl_Function::Pl_Function(
  19 + char const* identifier, Pipeline* next, writer_c_t fn, void* udata) :
  20 + Pipeline(identifier, next),
  21 + m(new Members(nullptr))
  22 +{
  23 + m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) {
  24 + int code = fn(data, len, udata);
  25 + if (code != 0) {
  26 + throw std::runtime_error(
  27 + std::string(identifier) + " function returned code " +
  28 + QUtil::int_to_string(code));
  29 + }
  30 + };
  31 +}
  32 +
  33 +Pl_Function::Pl_Function(
  34 + char const* identifier, Pipeline* next, writer_c_char_t fn, void* udata) :
  35 + Pipeline(identifier, next),
  36 + m(new Members(nullptr))
  37 +{
  38 + m->fn = [identifier, fn, udata](unsigned char const* data, size_t len) {
  39 + int code = fn(reinterpret_cast<char const*>(data), len, udata);
  40 + if (code != 0) {
  41 + throw std::runtime_error(
  42 + std::string(identifier) + " function returned code " +
  43 + QUtil::int_to_string(code));
  44 + }
  45 + };
  46 +}
  47 +
18 Pl_Function::~Pl_Function() 48 Pl_Function::~Pl_Function()
19 { 49 {
20 // Must be explicit and not inline -- see QPDF_DLL_CLASS in 50 // Must be explicit and not inline -- see QPDF_DLL_CLASS in
libtests/pl_function.cc
@@ -5,6 +5,38 @@ @@ -5,6 +5,38 @@
5 #include <qpdf/Pl_String.hh> 5 #include <qpdf/Pl_String.hh>
6 #include <iostream> 6 #include <iostream>
7 7
  8 +namespace
  9 +{
  10 + struct Count
  11 + {
  12 + int count{0};
  13 + };
  14 +} // namespace
  15 +
  16 +int
  17 +f(unsigned char const* data, size_t len, void* udata)
  18 +{
  19 + auto c = reinterpret_cast<Count*>(udata);
  20 + ++c->count;
  21 + std::cout << "got " << data << "(" << len << ")" << std::endl;
  22 + if (c->count == 3) {
  23 + return 1;
  24 + }
  25 + return 0;
  26 +}
  27 +
  28 +int
  29 +g(char const* data, size_t len, void* udata)
  30 +{
  31 + auto c = reinterpret_cast<Count*>(udata);
  32 + ++c->count;
  33 + std::cout << "signed got " << data << "(" << len << ")" << std::endl;
  34 + if (c->count == 2) {
  35 + return 2;
  36 + }
  37 + return 0;
  38 +}
  39 +
8 int 40 int
9 main(int argc, char* argv[]) 41 main(int argc, char* argv[])
10 { 42 {
@@ -23,5 +55,32 @@ main(int argc, char* argv[]) @@ -23,5 +55,32 @@ main(int argc, char* argv[])
23 p2.finish(); 55 p2.finish();
24 assert(s == "c2FsYWQ="); 56 assert(s == "c2FsYWQ=");
25 57
  58 + Count c;
  59 + Pl_Function p3("c-function", nullptr, f, &c);
  60 + p3 << "one";
  61 + p3 << "two";
  62 + try {
  63 + p3 << "three";
  64 + assert(false);
  65 + } catch (std::runtime_error& e) {
  66 + std::cout << "three threw " << e.what() << std::endl;
  67 + }
  68 + p3 << "four";
  69 + p3.finish();
  70 + assert(c.count == 4);
  71 +
  72 + c.count = 0;
  73 + Pl_Function p4("c-function", nullptr, g, &c);
  74 + p4 << "potato";
  75 + try {
  76 + p4 << "salad";
  77 + assert(false);
  78 + } catch (std::runtime_error& e) {
  79 + std::cout << "salad threw " << e.what() << std::endl;
  80 + }
  81 + p4 << "quack";
  82 + p4.finish();
  83 + assert(c.count == 3);
  84 +
26 return 0; 85 return 0;
27 } 86 }
libtests/qtest/pl_function/exp
1 p1: 6: potato 1 p1: 6: potato
2 p2: 5: salad 2 p2: 5: salad
  3 +got one(3)
  4 +got two(3)
  5 +got three(5)
  6 +three threw c-function function returned code 1
  7 +got four(4)
  8 +signed got potato(6)
  9 +signed got salad(5)
  10 +salad threw c-function function returned code 2
  11 +signed got quack(5)