Commit 071fe4a0e5c7e6abd6725552d1bf0b6119bce1c9
Committed by
GitHub
Merge pull request #985 from m-holger/members
Change JSONHandler::m to std::unique_ptr and declare Members in implementation file
Showing
4 changed files
with
77 additions
and
72 deletions
README-maintainer.md
| @@ -235,16 +235,26 @@ Building docs from pull requests is also enabled. | @@ -235,16 +235,26 @@ Building docs from pull requests is also enabled. | ||
| 235 | // README-maintainer | 235 | // README-maintainer |
| 236 | ``` | 236 | ``` |
| 237 | 237 | ||
| 238 | -* Put private member variables in std::shared_ptr<Members> for all | ||
| 239 | - public classes. Remember to use QPDF_DLL on ~Members(). Exception: | ||
| 240 | - indirection through std::shared_ptr<Members> is expensive, so don't | ||
| 241 | - do it for classes that are copied a lot, like QPDFObjectHandle and | ||
| 242 | - QPDFObject. It may be possible to declare | ||
| 243 | - std::shared_ptr<Members> m_ph; | ||
| 244 | - Member* m; | ||
| 245 | - with m = m_ph.get(), and then indirect through m in | ||
| 246 | - performance-critical settings, though in 2022, std::shared_ptr is | ||
| 247 | - sufficiently performant that this may not be worth it. | 238 | +* Put private member variables in std::unique_ptr<Members> for all |
| 239 | + public classes. Forward declare Members in the header file and define | ||
| 240 | + Members in the implementation file. One of the major benefits of | ||
| 241 | + defining Members in the implementation file is that it makes it easier | ||
| 242 | + to use private classes as data members and simplifies the include order. | ||
| 243 | + Remember that Members must be fully defined before the destructor of the | ||
| 244 | + main class. For an example of this pattern see class JSONHandler. | ||
| 245 | + | ||
| 246 | + Exception: indirection through std::unique_ptr<Members> incurs an overhead, | ||
| 247 | + so don't do it for: | ||
| 248 | + * (especially private) classes that are copied a lot, like QPDFObjectHandle | ||
| 249 | + and QPDFObject. | ||
| 250 | + * classes that are a shared pointer to another class, such as QPDFObjectHandle | ||
| 251 | + or JSON. | ||
| 252 | + | ||
| 253 | + For exported classes that do not use the member pattern for performance | ||
| 254 | + reasons it is worth considering adding a std::unique_ptr to an empty Members | ||
| 255 | + class initialized to nullptr to give the flexibility to add data members | ||
| 256 | + without breaking the ABI. | ||
| 257 | + | ||
| 248 | 258 | ||
| 249 | * Traversal of objects is expensive. It's worth adding some complexity | 259 | * Traversal of objects is expensive. It's worth adding some complexity |
| 250 | to avoid needless traversals of objects. | 260 | to avoid needless traversals of objects. |
libqpdf/JSONHandler.cc
| @@ -4,11 +4,48 @@ | @@ -4,11 +4,48 @@ | ||
| 4 | #include <qpdf/QTC.hh> | 4 | #include <qpdf/QTC.hh> |
| 5 | #include <qpdf/QUtil.hh> | 5 | #include <qpdf/QUtil.hh> |
| 6 | 6 | ||
| 7 | +struct Handlers | ||
| 8 | +{ | ||
| 9 | + Handlers() = default; | ||
| 10 | + | ||
| 11 | + JSONHandler::json_handler_t any_handler{nullptr}; | ||
| 12 | + JSONHandler::void_handler_t null_handler{nullptr}; | ||
| 13 | + JSONHandler::string_handler_t string_handler{nullptr}; | ||
| 14 | + JSONHandler::string_handler_t number_handler{nullptr}; | ||
| 15 | + JSONHandler::bool_handler_t bool_handler{nullptr}; | ||
| 16 | + JSONHandler::json_handler_t dict_start_handler{nullptr}; | ||
| 17 | + JSONHandler::void_handler_t dict_end_handler{nullptr}; | ||
| 18 | + JSONHandler::json_handler_t array_start_handler{nullptr}; | ||
| 19 | + JSONHandler::void_handler_t array_end_handler{nullptr}; | ||
| 20 | + JSONHandler::void_handler_t final_handler{nullptr}; | ||
| 21 | + std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; | ||
| 22 | + std::shared_ptr<JSONHandler> fallback_dict_handler; | ||
| 23 | + std::shared_ptr<JSONHandler> array_item_handler; | ||
| 24 | +}; | ||
| 25 | + | ||
| 26 | +class JSONHandler::Members | ||
| 27 | +{ | ||
| 28 | + friend class JSONHandler; | ||
| 29 | + | ||
| 30 | + public: | ||
| 31 | + ~Members() = default; | ||
| 32 | + | ||
| 33 | + private: | ||
| 34 | + Members() = default; | ||
| 35 | + Members(Members const&) = delete; | ||
| 36 | + | ||
| 37 | + Handlers h; | ||
| 38 | +}; | ||
| 39 | + | ||
| 7 | JSONHandler::JSONHandler() : | 40 | JSONHandler::JSONHandler() : |
| 8 | m(new Members()) | 41 | m(new Members()) |
| 9 | { | 42 | { |
| 10 | } | 43 | } |
| 11 | 44 | ||
| 45 | +JSONHandler::~JSONHandler() | ||
| 46 | +{ | ||
| 47 | +} | ||
| 48 | + | ||
| 12 | void | 49 | void |
| 13 | JSONHandler::usage(std::string const& msg) | 50 | JSONHandler::usage(std::string const& msg) |
| 14 | { | 51 | { |
| @@ -80,24 +117,24 @@ JSONHandler::handle(std::string const& path, JSON j) | @@ -80,24 +117,24 @@ JSONHandler::handle(std::string const& path, JSON j) | ||
| 80 | m->h.any_handler(path, j); | 117 | m->h.any_handler(path, j); |
| 81 | return; | 118 | return; |
| 82 | } | 119 | } |
| 83 | - bool handled = false; | 120 | + |
| 84 | bool bvalue = false; | 121 | bool bvalue = false; |
| 85 | std::string s_value; | 122 | std::string s_value; |
| 86 | if (m->h.null_handler && j.isNull()) { | 123 | if (m->h.null_handler && j.isNull()) { |
| 87 | m->h.null_handler(path); | 124 | m->h.null_handler(path); |
| 88 | - handled = true; | 125 | + return; |
| 89 | } | 126 | } |
| 90 | if (m->h.string_handler && j.getString(s_value)) { | 127 | if (m->h.string_handler && j.getString(s_value)) { |
| 91 | m->h.string_handler(path, s_value); | 128 | m->h.string_handler(path, s_value); |
| 92 | - handled = true; | 129 | + return; |
| 93 | } | 130 | } |
| 94 | if (m->h.number_handler && j.getNumber(s_value)) { | 131 | if (m->h.number_handler && j.getNumber(s_value)) { |
| 95 | m->h.number_handler(path, s_value); | 132 | m->h.number_handler(path, s_value); |
| 96 | - handled = true; | 133 | + return; |
| 97 | } | 134 | } |
| 98 | if (m->h.bool_handler && j.getBool(bvalue)) { | 135 | if (m->h.bool_handler && j.getBool(bvalue)) { |
| 99 | m->h.bool_handler(path, bvalue); | 136 | m->h.bool_handler(path, bvalue); |
| 100 | - handled = true; | 137 | + return; |
| 101 | } | 138 | } |
| 102 | if (m->h.dict_start_handler && j.isDictionary()) { | 139 | if (m->h.dict_start_handler && j.isDictionary()) { |
| 103 | m->h.dict_start_handler(path, j); | 140 | m->h.dict_start_handler(path, j); |
| @@ -119,7 +156,7 @@ JSONHandler::handle(std::string const& path, JSON j) | @@ -119,7 +156,7 @@ JSONHandler::handle(std::string const& path, JSON j) | ||
| 119 | } | 156 | } |
| 120 | }); | 157 | }); |
| 121 | m->h.dict_end_handler(path); | 158 | m->h.dict_end_handler(path); |
| 122 | - handled = true; | 159 | + return; |
| 123 | } | 160 | } |
| 124 | if (m->h.array_start_handler && j.isArray()) { | 161 | if (m->h.array_start_handler && j.isArray()) { |
| 125 | m->h.array_start_handler(path, j); | 162 | m->h.array_start_handler(path, j); |
| @@ -129,15 +166,13 @@ JSONHandler::handle(std::string const& path, JSON j) | @@ -129,15 +166,13 @@ JSONHandler::handle(std::string const& path, JSON j) | ||
| 129 | ++i; | 166 | ++i; |
| 130 | }); | 167 | }); |
| 131 | m->h.array_end_handler(path); | 168 | m->h.array_end_handler(path); |
| 132 | - handled = true; | 169 | + return; |
| 133 | } | 170 | } |
| 134 | 171 | ||
| 135 | - if (!handled) { | ||
| 136 | - // It would be nice to include information about what type the object was and what types | ||
| 137 | - // were allowed, but we're relying on schema validation to make sure input is properly | ||
| 138 | - // structured before calling the handlers. It would be different if this code were trying to | ||
| 139 | - // be part of a general-purpose JSON package. | ||
| 140 | - QTC::TC("libtests", "JSONHandler unhandled value"); | ||
| 141 | - usage("JSON handler: value at " + path + " is not of expected type"); | ||
| 142 | - } | 172 | + // It would be nice to include information about what type the object was and what types were |
| 173 | + // allowed, but we're relying on schema validation to make sure input is properly structured | ||
| 174 | + // before calling the handlers. It would be different if this code were trying to be part of a | ||
| 175 | + // general-purpose JSON package. | ||
| 176 | + QTC::TC("libtests", "JSONHandler unhandled value"); | ||
| 177 | + usage("JSON handler: value at " + path + " is not of expected type"); | ||
| 143 | } | 178 | } |
libqpdf/QPDF.cc
| @@ -1482,7 +1482,8 @@ QPDF::readObjectAtOffset( | @@ -1482,7 +1482,8 @@ QPDF::readObjectAtOffset( | ||
| 1482 | 1482 | ||
| 1483 | // Special case: if offset is 0, just return null. Some PDF writers, in particular | 1483 | // Special case: if offset is 0, just return null. Some PDF writers, in particular |
| 1484 | // "Mac OS X 10.7.5 Quartz PDFContext", may store deleted objects in the xref table as | 1484 | // "Mac OS X 10.7.5 Quartz PDFContext", may store deleted objects in the xref table as |
| 1485 | - // "0000000000 00000 n", which is not correct, but it won't hurt anything for to ignore these. | 1485 | + // "0000000000 00000 n", which is not correct, but it won't hurt anything for us to ignore |
| 1486 | + // these. | ||
| 1486 | if (offset == 0) { | 1487 | if (offset == 0) { |
| 1487 | QTC::TC("qpdf", "QPDF bogus 0 offset", 0); | 1488 | QTC::TC("qpdf", "QPDF bogus 0 offset", 0); |
| 1488 | warn(damagedPDF(0, "object has offset 0")); | 1489 | warn(damagedPDF(0, "object has offset 0")); |
libqpdf/qpdf/JSONHandler.hh
| @@ -16,7 +16,7 @@ class JSONHandler | @@ -16,7 +16,7 @@ class JSONHandler | ||
| 16 | public: | 16 | public: |
| 17 | // A QPDFUsage exception is thrown if there are any errors validating the JSON object. | 17 | // A QPDFUsage exception is thrown if there are any errors validating the JSON object. |
| 18 | JSONHandler(); | 18 | JSONHandler(); |
| 19 | - ~JSONHandler() = default; | 19 | + ~JSONHandler(); |
| 20 | 20 | ||
| 21 | // Based on the type of handler, expect the object to be of a certain type. QPDFUsage is thrown | 21 | // Based on the type of handler, expect the object to be of a certain type. QPDFUsage is thrown |
| 22 | // otherwise. Multiple handlers may be registered, which allows the object to be of various | 22 | // otherwise. Multiple handlers may be registered, which allows the object to be of various |
| @@ -53,51 +53,10 @@ class JSONHandler | @@ -53,51 +53,10 @@ class JSONHandler | ||
| 53 | 53 | ||
| 54 | static void usage(std::string const& msg); | 54 | static void usage(std::string const& msg); |
| 55 | 55 | ||
| 56 | - struct Handlers | ||
| 57 | - { | ||
| 58 | - Handlers() : | ||
| 59 | - any_handler(nullptr), | ||
| 60 | - null_handler(nullptr), | ||
| 61 | - string_handler(nullptr), | ||
| 62 | - number_handler(nullptr), | ||
| 63 | - bool_handler(nullptr), | ||
| 64 | - dict_start_handler(nullptr), | ||
| 65 | - dict_end_handler(nullptr), | ||
| 66 | - array_start_handler(nullptr), | ||
| 67 | - array_end_handler(nullptr), | ||
| 68 | - final_handler(nullptr) | ||
| 69 | - { | ||
| 70 | - } | ||
| 71 | - | ||
| 72 | - json_handler_t any_handler; | ||
| 73 | - void_handler_t null_handler; | ||
| 74 | - string_handler_t string_handler; | ||
| 75 | - string_handler_t number_handler; | ||
| 76 | - bool_handler_t bool_handler; | ||
| 77 | - json_handler_t dict_start_handler; | ||
| 78 | - void_handler_t dict_end_handler; | ||
| 79 | - json_handler_t array_start_handler; | ||
| 80 | - void_handler_t array_end_handler; | ||
| 81 | - void_handler_t final_handler; | ||
| 82 | - std::map<std::string, std::shared_ptr<JSONHandler>> dict_handlers; | ||
| 83 | - std::shared_ptr<JSONHandler> fallback_dict_handler; | ||
| 84 | - std::shared_ptr<JSONHandler> array_item_handler; | ||
| 85 | - }; | ||
| 86 | - | ||
| 87 | - class Members | ||
| 88 | - { | ||
| 89 | - friend class JSONHandler; | ||
| 90 | - | ||
| 91 | - public: | ||
| 92 | - ~Members() = default; | ||
| 93 | - | ||
| 94 | - private: | ||
| 95 | - Members() = default; | ||
| 96 | - Members(Members const&) = delete; | ||
| 97 | - | ||
| 98 | - Handlers h; | ||
| 99 | - }; | ||
| 100 | - std::shared_ptr<Members> m; | 56 | + |
| 57 | + class Members; | ||
| 58 | + | ||
| 59 | + std::unique_ptr<Members> m; | ||
| 101 | }; | 60 | }; |
| 102 | 61 | ||
| 103 | #endif // JSONHANDLER_HH | 62 | #endif // JSONHANDLER_HH |