/src/qpdf/libqpdf/qpdf/NNTree.hh
Line | Count | Source |
1 | | #ifndef NNTREE_HH |
2 | | #define NNTREE_HH |
3 | | |
4 | | #include <qpdf/QPDF.hh> |
5 | | #include <qpdf/QPDFObjectHandle_private.hh> |
6 | | |
7 | | #include <iterator> |
8 | | #include <list> |
9 | | #include <memory> |
10 | | |
11 | | class NNTreeImpl; |
12 | | class NNTreeIterator final |
13 | | { |
14 | | friend class NNTreeImpl; |
15 | | |
16 | | public: |
17 | | typedef std::pair<QPDFObjectHandle, QPDFObjectHandle> T; |
18 | | using iterator_category = std::bidirectional_iterator_tag; |
19 | | using value_type = T; |
20 | | using difference_type = long; |
21 | | using pointer = T*; |
22 | | using reference = T&; |
23 | | |
24 | 34.0k | ~NNTreeIterator() = default; |
25 | | // iterator can be incremented or decremented, or dereferenced. This does not imply that it |
26 | | // points to a valid item. |
27 | | bool |
28 | | valid() const |
29 | 59.4k | { |
30 | 59.4k | return item_number >= 0; |
31 | 59.4k | } |
32 | | NNTreeIterator& |
33 | | operator++() |
34 | 24.4k | { |
35 | 24.4k | increment(false); |
36 | 24.4k | return *this; |
37 | 24.4k | } |
38 | | NNTreeIterator |
39 | | operator++(int) |
40 | 0 | { |
41 | 0 | NNTreeIterator t = *this; |
42 | 0 | ++(*this); |
43 | 0 | return t; |
44 | 0 | } |
45 | | NNTreeIterator& |
46 | | operator--() |
47 | 0 | { |
48 | 0 | increment(true); |
49 | 0 | return *this; |
50 | 0 | } |
51 | | NNTreeIterator |
52 | | operator--(int) |
53 | 0 | { |
54 | 0 | NNTreeIterator t = *this; |
55 | 0 | --(*this); |
56 | 0 | return t; |
57 | 0 | } |
58 | | reference |
59 | | operator*() |
60 | 24.9k | { |
61 | 24.9k | updateIValue(false); |
62 | 24.9k | return ivalue; |
63 | 24.9k | } |
64 | | pointer |
65 | | operator->() |
66 | 26.4k | { |
67 | 26.4k | updateIValue(false); |
68 | 26.4k | return &ivalue; |
69 | 26.4k | } |
70 | | bool operator==(NNTreeIterator const& other) const; |
71 | | bool |
72 | | operator!=(NNTreeIterator const& other) const |
73 | 26.2k | { |
74 | 26.2k | return !operator==(other); |
75 | 26.2k | } |
76 | | |
77 | | void insertAfter(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
78 | | void remove(); |
79 | | |
80 | | private: |
81 | | class PathElement |
82 | | { |
83 | | public: |
84 | | PathElement(qpdf::Dictionary const& node, int kid_number) : |
85 | 8.95k | node(node), |
86 | 8.95k | kid_number(kid_number) |
87 | 8.95k | { |
88 | 8.95k | } |
89 | | |
90 | | qpdf::Dictionary node; |
91 | | int kid_number; |
92 | | }; |
93 | | |
94 | | NNTreeIterator(NNTreeImpl& impl) : |
95 | 23.6k | impl(impl) |
96 | 23.6k | { |
97 | 23.6k | } |
98 | | void updateIValue(bool allow_invalid = true); |
99 | | bool deepen(qpdf::Dictionary node, bool first, bool allow_empty); |
100 | | void |
101 | | setItemNumber(QPDFObjectHandle const& a_node, int n) |
102 | 19.7k | { |
103 | 19.7k | node = a_node; |
104 | 19.7k | item_number = n; |
105 | 19.7k | updateIValue(); |
106 | 19.7k | } |
107 | | void |
108 | | addPathElement(QPDFObjectHandle const& a_node, int kid_number) |
109 | 8.88k | { |
110 | 8.88k | path.emplace_back(a_node, kid_number); |
111 | 8.88k | } |
112 | | qpdf::Dictionary getNextKid(PathElement& element, bool backward); |
113 | | void increment(bool backward); |
114 | | void resetLimits(qpdf::Dictionary node, std::list<PathElement>::iterator parent); |
115 | | |
116 | | void split(qpdf::Dictionary to_split, std::list<PathElement>::iterator parent); |
117 | | std::list<PathElement>::iterator lastPathElement(); |
118 | | |
119 | | NNTreeImpl& impl; |
120 | | std::list<PathElement> path; |
121 | | qpdf::Dictionary node; |
122 | | int item_number{-1}; |
123 | | value_type ivalue; |
124 | | }; |
125 | | |
126 | | class NNTreeImpl final |
127 | | { |
128 | | friend class NNTreeIterator; |
129 | | |
130 | | public: |
131 | | typedef NNTreeIterator iterator; |
132 | | |
133 | | NNTreeImpl( |
134 | | QPDF& qpdf, |
135 | | qpdf::Dictionary tree_root, |
136 | | qpdf_object_type_e key_type, |
137 | | std::function<bool(QPDFObjectHandle const&)> value_validator, |
138 | | bool auto_repair) : |
139 | 1.97k | qpdf(qpdf), |
140 | 1.97k | tree_root(std::move(tree_root)), |
141 | 1.97k | key_type(key_type), |
142 | 1.97k | items_key(key_type == ::ot_string ? "/Names" : "/Nums"), |
143 | 1.97k | value_valid(value_validator), |
144 | 1.97k | auto_repair(auto_repair) |
145 | 1.97k | { |
146 | 1.97k | } |
147 | | iterator begin(); |
148 | | iterator |
149 | | end() |
150 | 6.05k | { |
151 | 6.05k | return {*this}; |
152 | 6.05k | } |
153 | | iterator last(); |
154 | | iterator find(QPDFObjectHandle const& key, bool return_prev_if_not_found = false); |
155 | | iterator insertFirst(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
156 | | iterator insert(QPDFObjectHandle const& key, QPDFObjectHandle const& value); |
157 | | bool remove(QPDFObjectHandle const& key, QPDFObjectHandle* value = nullptr); |
158 | | |
159 | | bool validate(bool repair = true); |
160 | | |
161 | | // Change the split threshold for easier testing. There's no real reason to expose this to |
162 | | // downstream tree helpers, but it has to be public so we can call it from the test suite. |
163 | | void |
164 | | setSplitThreshold(int threshold) |
165 | 0 | { |
166 | 0 | split_threshold = threshold; |
167 | 0 | } |
168 | | |
169 | | private: |
170 | | void repair(); |
171 | | iterator findInternal(QPDFObjectHandle const& key, bool return_prev_if_not_found = false); |
172 | | int binarySearch( |
173 | | QPDFObjectHandle const& key, |
174 | | qpdf::Array const& items, |
175 | | size_t num_items, |
176 | | bool return_prev_if_not_found, |
177 | | bool search_kids) const; |
178 | | int compareKeyItem(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; |
179 | | int compareKeyKid(QPDFObjectHandle const& key, qpdf::Array const& items, int idx) const; |
180 | | void warn(QPDFObjectHandle const& node, std::string const& msg); |
181 | | void error(QPDFObjectHandle const& node, std::string const& msg) const; |
182 | | |
183 | | std::string const& |
184 | | itemsKey() const |
185 | 188k | { |
186 | 188k | return items_key; |
187 | 188k | } |
188 | | bool |
189 | | keyValid(QPDFObjectHandle o) const |
190 | 200k | { |
191 | 200k | return o.resolved_type_code() == key_type; |
192 | 200k | } |
193 | | int compareKeys(QPDFObjectHandle a, QPDFObjectHandle b) const; |
194 | | |
195 | | QPDF& qpdf; |
196 | | int split_threshold{32}; |
197 | | qpdf::Dictionary tree_root; |
198 | | const qpdf_object_type_e key_type; |
199 | | const std::string items_key; |
200 | | const std::function<bool(QPDFObjectHandle const&)> value_valid; |
201 | | bool auto_repair{true}; |
202 | | size_t error_count{0}; |
203 | | }; |
204 | | |
205 | | #endif // NNTREE_HH |