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 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 259 * Traversal of objects is expensive. It's worth adding some complexity
250 260 to avoid needless traversals of objects.
... ...
libqpdf/JSONHandler.cc
... ... @@ -4,11 +4,48 @@
4 4 #include <qpdf/QTC.hh>
5 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 40 JSONHandler::JSONHandler() :
8 41 m(new Members())
9 42 {
10 43 }
11 44  
  45 +JSONHandler::~JSONHandler()
  46 +{
  47 +}
  48 +
12 49 void
13 50 JSONHandler::usage(std::string const& msg)
14 51 {
... ... @@ -80,24 +117,24 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
80 117 m->h.any_handler(path, j);
81 118 return;
82 119 }
83   - bool handled = false;
  120 +
84 121 bool bvalue = false;
85 122 std::string s_value;
86 123 if (m->h.null_handler && j.isNull()) {
87 124 m->h.null_handler(path);
88   - handled = true;
  125 + return;
89 126 }
90 127 if (m->h.string_handler && j.getString(s_value)) {
91 128 m->h.string_handler(path, s_value);
92   - handled = true;
  129 + return;
93 130 }
94 131 if (m->h.number_handler && j.getNumber(s_value)) {
95 132 m->h.number_handler(path, s_value);
96   - handled = true;
  133 + return;
97 134 }
98 135 if (m->h.bool_handler && j.getBool(bvalue)) {
99 136 m->h.bool_handler(path, bvalue);
100   - handled = true;
  137 + return;
101 138 }
102 139 if (m->h.dict_start_handler && j.isDictionary()) {
103 140 m->h.dict_start_handler(path, j);
... ... @@ -119,7 +156,7 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
119 156 }
120 157 });
121 158 m->h.dict_end_handler(path);
122   - handled = true;
  159 + return;
123 160 }
124 161 if (m->h.array_start_handler && j.isArray()) {
125 162 m->h.array_start_handler(path, j);
... ... @@ -129,15 +166,13 @@ JSONHandler::handle(std::string const&amp; path, JSON j)
129 166 ++i;
130 167 });
131 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 1482  
1483 1483 // Special case: if offset is 0, just return null. Some PDF writers, in particular
1484 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 1487 if (offset == 0) {
1487 1488 QTC::TC("qpdf", "QPDF bogus 0 offset", 0);
1488 1489 warn(damagedPDF(0, "object has offset 0"));
... ...
libqpdf/qpdf/JSONHandler.hh
... ... @@ -16,7 +16,7 @@ class JSONHandler
16 16 public:
17 17 // A QPDFUsage exception is thrown if there are any errors validating the JSON object.
18 18 JSONHandler();
19   - ~JSONHandler() = default;
  19 + ~JSONHandler();
20 20  
21 21 // Based on the type of handler, expect the object to be of a certain type. QPDFUsage is thrown
22 22 // otherwise. Multiple handlers may be registered, which allows the object to be of various
... ... @@ -53,51 +53,10 @@ class JSONHandler
53 53  
54 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 62 #endif // JSONHANDLER_HH
... ...