Commit 0500d4347a6d31ef05fd860559e380c2e488c194

Authored by Jay Berkenbilt
1 parent 05fda4af

JSON: add blob type that generates base64-encoded binary data

ChangeLog
1 1 2022-05-04 Jay Berkenbilt <ejb@ql.org>
2 2  
  3 + * JSON: add a new "blob" type that takes a function to write data
  4 + into. The blob is serialized as a base64-encoded representation of
  5 + whatever is written to the function.
  6 +
3 7 * FileInputSource has new constructors that eliminate the need to
4 8 call setFilename or setFile in most cases.
5 9  
... ...
... ... @@ -51,11 +51,6 @@ library, when context is available, to have a pipeline rather than a
51 51 FILE* or std::ostream. This makes it possible for people to capture
52 52 output more flexibly.
53 53  
54   -Have a json blob defined by a function that takes a pipeline and
55   -writes data to the pipeline. It's writer should create a Pl_Base64 ->
56   -Pl_Concatenate in front of the pipeline passed to write and call the
57   -function with that.
58   -
59 54 For json output, do not unparse to string. Use the writers instead.
60 55 Write incrementally. This changes ordering only, but we should be able
61 56 manually update the test output for those cases. Objects should be
... ...
cSpell.json
... ... @@ -199,6 +199,7 @@
199 199 "itemizedlist",
200 200 "jarr",
201 201 "jbig",
  202 + "jblob",
202 203 "jdimension",
203 204 "jdouble",
204 205 "jerr",
... ...
include/qpdf/JSON.hh
... ... @@ -122,6 +122,13 @@ class JSON
122 122 QPDF_DLL
123 123 static JSON makeNull();
124 124  
  125 + // A blob serializes as a string. The function will be called by
  126 + // JSON with a pipeline and should write binary data to the
  127 + // pipeline but not call finish(). JSON will call finish() at the
  128 + // right time.
  129 + QPDF_DLL
  130 + static JSON makeBlob(std::function<void(Pipeline*)>);
  131 +
125 132 QPDF_DLL
126 133 bool isArray() const;
127 134  
... ... @@ -323,6 +330,13 @@ class JSON
323 330 virtual ~JSON_null() = default;
324 331 virtual void write(Pipeline*, size_t depth) const;
325 332 };
  333 + struct JSON_blob: public JSON_value
  334 + {
  335 + JSON_blob(std::function<void(Pipeline*)> fn);
  336 + virtual ~JSON_blob() = default;
  337 + virtual void write(Pipeline*, size_t depth) const;
  338 + std::function<void(Pipeline*)> fn;
  339 + };
326 340  
327 341 JSON(std::shared_ptr<JSON_value>);
328 342  
... ...
libqpdf/JSON.cc
1 1 #include <qpdf/JSON.hh>
2 2  
3 3 #include <qpdf/BufferInputSource.hh>
  4 +#include <qpdf/Pl_Base64.hh>
  5 +#include <qpdf/Pl_Concatenate.hh>
4 6 #include <qpdf/Pl_String.hh>
5 7 #include <qpdf/QTC.hh>
6 8 #include <qpdf/QUtil.hh>
... ... @@ -168,6 +170,22 @@ JSON::JSON_null::write(Pipeline* p, size_t) const
168 170 *p << "null";
169 171 }
170 172  
  173 +JSON::JSON_blob::JSON_blob(std::function<void(Pipeline*)> fn) :
  174 + fn(fn)
  175 +{
  176 +}
  177 +
  178 +void
  179 +JSON::JSON_blob::write(Pipeline* p, size_t) const
  180 +{
  181 + *p << "\"";
  182 + Pl_Concatenate cat("blob concatenate", p);
  183 + Pl_Base64 base64("blob base64", &cat, Pl_Base64::a_encode);
  184 + fn(&base64);
  185 + base64.finish();
  186 + *p << "\"";
  187 +}
  188 +
171 189 void
172 190 JSON::write(Pipeline* p, size_t depth) const
173 191 {
... ... @@ -306,6 +324,12 @@ JSON::makeNull()
306 324 return JSON(std::make_shared<JSON_null>());
307 325 }
308 326  
  327 +JSON
  328 +JSON::makeBlob(std::function<void(Pipeline*)> fn)
  329 +{
  330 + return JSON(std::make_shared<JSON_blob>(fn));
  331 +}
  332 +
309 333 bool
310 334 JSON::isArray() const
311 335 {
... ...
libtests/json.cc
1 1 #include <qpdf/assert_test.h>
2 2  
3 3 #include <qpdf/JSON.hh>
  4 +#include <qpdf/Pipeline.hh>
4 5 #include <qpdf/QPDFObjectHandle.hh>
5 6 #include <iostream>
6 7  
... ... @@ -113,6 +114,19 @@ test_main()
113 114 {"c", "[\n true\n]"},
114 115 };
115 116 assert(dvalue == xdvalue);
  117 + auto blob_data = [](Pipeline* p) {
  118 + *p << "\x01\x02\x03\x04\x05\xff\xfe\xfd\xfc\xfb";
  119 + };
  120 + JSON jblob = JSON::makeDictionary();
  121 + jblob.addDictionaryMember("normal", JSON::parse(R"("string")"));
  122 + jblob.addDictionaryMember("blob", JSON::makeBlob(blob_data));
  123 + // cSpell:ignore AQIDBAX
  124 + check(
  125 + jblob,
  126 + "{\n"
  127 + " \"blob\": \"AQIDBAX//v38+w==\",\n"
  128 + " \"normal\": \"string\"\n"
  129 + "}");
116 130 }
117 131  
118 132 static void
... ...