Commit 0500d4347a6d31ef05fd860559e380c2e488c194
1 parent
05fda4af
JSON: add blob type that generates base64-encoded binary data
Showing
6 changed files
with
57 additions
and
5 deletions
ChangeLog
| 1 | 2022-05-04 Jay Berkenbilt <ejb@ql.org> | 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 | * FileInputSource has new constructors that eliminate the need to | 7 | * FileInputSource has new constructors that eliminate the need to |
| 4 | call setFilename or setFile in most cases. | 8 | call setFilename or setFile in most cases. |
| 5 | 9 |
TODO
| @@ -51,11 +51,6 @@ library, when context is available, to have a pipeline rather than a | @@ -51,11 +51,6 @@ library, when context is available, to have a pipeline rather than a | ||
| 51 | FILE* or std::ostream. This makes it possible for people to capture | 51 | FILE* or std::ostream. This makes it possible for people to capture |
| 52 | output more flexibly. | 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 | For json output, do not unparse to string. Use the writers instead. | 54 | For json output, do not unparse to string. Use the writers instead. |
| 60 | Write incrementally. This changes ordering only, but we should be able | 55 | Write incrementally. This changes ordering only, but we should be able |
| 61 | manually update the test output for those cases. Objects should be | 56 | manually update the test output for those cases. Objects should be |
cSpell.json
include/qpdf/JSON.hh
| @@ -122,6 +122,13 @@ class JSON | @@ -122,6 +122,13 @@ class JSON | ||
| 122 | QPDF_DLL | 122 | QPDF_DLL |
| 123 | static JSON makeNull(); | 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 | QPDF_DLL | 132 | QPDF_DLL |
| 126 | bool isArray() const; | 133 | bool isArray() const; |
| 127 | 134 | ||
| @@ -323,6 +330,13 @@ class JSON | @@ -323,6 +330,13 @@ class JSON | ||
| 323 | virtual ~JSON_null() = default; | 330 | virtual ~JSON_null() = default; |
| 324 | virtual void write(Pipeline*, size_t depth) const; | 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 | JSON(std::shared_ptr<JSON_value>); | 341 | JSON(std::shared_ptr<JSON_value>); |
| 328 | 342 |
libqpdf/JSON.cc
| 1 | #include <qpdf/JSON.hh> | 1 | #include <qpdf/JSON.hh> |
| 2 | 2 | ||
| 3 | #include <qpdf/BufferInputSource.hh> | 3 | #include <qpdf/BufferInputSource.hh> |
| 4 | +#include <qpdf/Pl_Base64.hh> | ||
| 5 | +#include <qpdf/Pl_Concatenate.hh> | ||
| 4 | #include <qpdf/Pl_String.hh> | 6 | #include <qpdf/Pl_String.hh> |
| 5 | #include <qpdf/QTC.hh> | 7 | #include <qpdf/QTC.hh> |
| 6 | #include <qpdf/QUtil.hh> | 8 | #include <qpdf/QUtil.hh> |
| @@ -168,6 +170,22 @@ JSON::JSON_null::write(Pipeline* p, size_t) const | @@ -168,6 +170,22 @@ JSON::JSON_null::write(Pipeline* p, size_t) const | ||
| 168 | *p << "null"; | 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 | void | 189 | void |
| 172 | JSON::write(Pipeline* p, size_t depth) const | 190 | JSON::write(Pipeline* p, size_t depth) const |
| 173 | { | 191 | { |
| @@ -306,6 +324,12 @@ JSON::makeNull() | @@ -306,6 +324,12 @@ JSON::makeNull() | ||
| 306 | return JSON(std::make_shared<JSON_null>()); | 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 | bool | 333 | bool |
| 310 | JSON::isArray() const | 334 | JSON::isArray() const |
| 311 | { | 335 | { |
libtests/json.cc
| 1 | #include <qpdf/assert_test.h> | 1 | #include <qpdf/assert_test.h> |
| 2 | 2 | ||
| 3 | #include <qpdf/JSON.hh> | 3 | #include <qpdf/JSON.hh> |
| 4 | +#include <qpdf/Pipeline.hh> | ||
| 4 | #include <qpdf/QPDFObjectHandle.hh> | 5 | #include <qpdf/QPDFObjectHandle.hh> |
| 5 | #include <iostream> | 6 | #include <iostream> |
| 6 | 7 | ||
| @@ -113,6 +114,19 @@ test_main() | @@ -113,6 +114,19 @@ test_main() | ||
| 113 | {"c", "[\n true\n]"}, | 114 | {"c", "[\n true\n]"}, |
| 114 | }; | 115 | }; |
| 115 | assert(dvalue == xdvalue); | 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 | static void | 132 | static void |