Commit 071fe4a0e5c7e6abd6725552d1bf0b6119bce1c9

Authored by Jay Berkenbilt
Committed by GitHub
2 parents 0b538ec8 d8bbe46e

Merge pull request #985 from m-holger/members

 Change JSONHandler::m to std::unique_ptr and declare Members in implementation file
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&amp; path, JSON j) @@ -80,24 +117,24 @@ JSONHandler::handle(std::string const&amp; 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&amp; path, JSON j) @@ -119,7 +156,7 @@ JSONHandler::handle(std::string const&amp; 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&amp; path, JSON j) @@ -129,15 +166,13 @@ JSONHandler::handle(std::string const&amp; 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