/src/qpdf/libqpdf/qpdf/ObjTable.hh
Line | Count | Source |
1 | | #ifndef OBJTABLE_HH |
2 | | #define OBJTABLE_HH |
3 | | |
4 | | #include <qpdf/QPDFObjGen.hh> |
5 | | #include <qpdf/QPDFObjectHandle.hh> |
6 | | |
7 | | #include "qpdf/QIntC.hh" |
8 | | #include <limits> |
9 | | |
10 | | // A table of objects indexed by object id. This is intended as a more efficient replacement for |
11 | | // std::map<QPDFObjGen, T> containers. |
12 | | // |
13 | | // The table is implemented as a std::vector, with the object id implicitly represented by the index |
14 | | // of the object. This has a number of implications, including: |
15 | | // - operations that change the index of existing elements such as insertion and deletions are not |
16 | | // permitted. |
17 | | // - operations that extend the table may invalidate iterators and references to objects. |
18 | | // |
19 | | // The provided overloads of the access operator[] are safe. For out of bounds access they will |
20 | | // either extend the table or throw a runtime error. |
21 | | // |
22 | | // ObjTable has a map 'sparse_elements' to deal with very sparse / extremely large object tables |
23 | | // (usually as the result of invalid dangling references). This map may contain objects not found in |
24 | | // the xref table of the original pdf if there are dangling references with an id significantly |
25 | | // larger than the largest valid object id found in original pdf. |
26 | | |
27 | | template <class T> |
28 | | class ObjTable: public std::vector<T> |
29 | | { |
30 | | public: |
31 | | using reference = T&; |
32 | 20.8k | ObjTable() = default; ObjTable<QPDFWriter::Object>::ObjTable() Line | Count | Source | 32 | 10.4k | ObjTable() = default; |
ObjTable<QPDFWriter::NewObject>::ObjTable() Line | Count | Source | 32 | 10.4k | ObjTable() = default; |
|
33 | | ObjTable(const ObjTable&) = delete; |
34 | | ObjTable(ObjTable&&) = delete; |
35 | | ObjTable& operator[](const ObjTable&) = delete; |
36 | | ObjTable& operator[](ObjTable&&) = delete; |
37 | | |
38 | | // Remove unchecked access. |
39 | | T& operator[](unsigned long idx) = delete; |
40 | | T const& operator[](unsigned long idx) const = delete; |
41 | | |
42 | | inline T const& |
43 | | operator[](int idx) const |
44 | 205k | { |
45 | 205k | return element(static_cast<size_t>(idx)); |
46 | 205k | } ObjTable<QPDFWriter::Object>::operator[](int) const Line | Count | Source | 44 | 73.3k | { | 45 | 73.3k | return element(static_cast<size_t>(idx)); | 46 | 73.3k | } |
ObjTable<QPDFWriter::NewObject>::operator[](int) const Line | Count | Source | 44 | 131k | { | 45 | 131k | return element(static_cast<size_t>(idx)); | 46 | 131k | } |
|
47 | | |
48 | | inline T const& |
49 | | operator[](QPDFObjGen og) const |
50 | 305k | { |
51 | 305k | return element(static_cast<size_t>(og.getObj())); |
52 | 305k | } |
53 | | |
54 | | inline T const& |
55 | | operator[](QPDFObjectHandle oh) const |
56 | 36.5k | { |
57 | 36.5k | return element(static_cast<size_t>(oh.getObjectID())); |
58 | 36.5k | } |
59 | | |
60 | | inline bool |
61 | | contains(size_t idx) const |
62 | 245k | { |
63 | 245k | return idx < std::vector<T>::size() || sparse_elements.count(idx); |
64 | 245k | } |
65 | | |
66 | | inline bool |
67 | | contains(QPDFObjGen og) const |
68 | 217k | { |
69 | 217k | return contains(static_cast<size_t>(og.getObj())); |
70 | 217k | } |
71 | | |
72 | | inline bool |
73 | | contains(QPDFObjectHandle oh) const |
74 | 27.9k | { |
75 | 27.9k | return contains(static_cast<size_t>(oh.getObjectID())); |
76 | 27.9k | } |
77 | | |
78 | | inline T& |
79 | | operator[](int id) |
80 | 872k | { |
81 | 872k | return element(static_cast<size_t>(id)); |
82 | 872k | } |
83 | | |
84 | | inline T& |
85 | | operator[](QPDFObjGen og) |
86 | 535k | { |
87 | 535k | return element(static_cast<size_t>(og.getObj())); |
88 | 535k | } |
89 | | |
90 | | inline T& |
91 | | operator[](QPDFObjectHandle oh) |
92 | 384k | { |
93 | 384k | return element(static_cast<size_t>(oh.getObjectID())); |
94 | 384k | } |
95 | | |
96 | | inline T& |
97 | | operator[](unsigned int id) |
98 | | { |
99 | | return element(id); |
100 | | } |
101 | | |
102 | | // emplace_back to the end of the vector. If there are any conflicting sparse elements, emplace |
103 | | // them to the back of the vector before adding the current element. |
104 | | template <class... Args> |
105 | | inline T& |
106 | | emplace_back(Args&&... args) |
107 | | { |
108 | | if (min_sparse == std::vector<T>::size()) { |
109 | | return emplace_back_large(std::forward<Args&&...>(args...)); |
110 | | } |
111 | | return std::vector<T>::emplace_back(std::forward<Args&&...>(args...)); |
112 | | } |
113 | | |
114 | | void |
115 | | resize(size_t a_size) |
116 | 20.7k | { |
117 | 20.7k | std::vector<T>::resize(a_size); |
118 | 20.7k | if (a_size > min_sparse) { |
119 | 0 | auto it = sparse_elements.begin(); |
120 | 0 | auto end = sparse_elements.end(); |
121 | 0 | while (it != end && it->first < a_size) { |
122 | 0 | std::vector<T>::operator[](it->first) = std::move(it->second); |
123 | 0 | it = sparse_elements.erase(it); |
124 | 0 | } |
125 | 0 | min_sparse = (it == end) ? std::numeric_limits<size_t>::max() : it->first; |
126 | 0 | } |
127 | 20.7k | } ObjTable<QPDFWriter::Object>::resize(unsigned long) Line | Count | Source | 116 | 10.3k | { | 117 | 10.3k | std::vector<T>::resize(a_size); | 118 | 10.3k | if (a_size > min_sparse) { | 119 | 0 | auto it = sparse_elements.begin(); | 120 | 0 | auto end = sparse_elements.end(); | 121 | 0 | while (it != end && it->first < a_size) { | 122 | 0 | std::vector<T>::operator[](it->first) = std::move(it->second); | 123 | 0 | it = sparse_elements.erase(it); | 124 | 0 | } | 125 | 0 | min_sparse = (it == end) ? std::numeric_limits<size_t>::max() : it->first; | 126 | 0 | } | 127 | 10.3k | } |
ObjTable<QPDFWriter::NewObject>::resize(unsigned long) Line | Count | Source | 116 | 10.3k | { | 117 | 10.3k | std::vector<T>::resize(a_size); | 118 | 10.3k | if (a_size > min_sparse) { | 119 | 0 | auto it = sparse_elements.begin(); | 120 | 0 | auto end = sparse_elements.end(); | 121 | 0 | while (it != end && it->first < a_size) { | 122 | 0 | std::vector<T>::operator[](it->first) = std::move(it->second); | 123 | 0 | it = sparse_elements.erase(it); | 124 | 0 | } | 125 | 0 | min_sparse = (it == end) ? std::numeric_limits<size_t>::max() : it->first; | 126 | 0 | } | 127 | 10.3k | } |
|
128 | | |
129 | | inline void |
130 | | forEach(std::function<void(int, const T&)> fn) |
131 | 1.25k | { |
132 | 1.25k | int i = 0; |
133 | 2.20M | for (auto const& item: *this) { |
134 | 2.20M | fn(i++, item); |
135 | 2.20M | } |
136 | 1.25k | for (auto const& [id, item]: sparse_elements) { |
137 | 1.15k | fn(QIntC::to_int(id), item); |
138 | 1.15k | } |
139 | 1.25k | } ObjTable<QPDFWriter::Object>::forEach(std::__1::function<void (int, QPDFWriter::Object const&)>) Line | Count | Source | 131 | 1.25k | { | 132 | 1.25k | int i = 0; | 133 | 2.20M | for (auto const& item: *this) { | 134 | 2.20M | fn(i++, item); | 135 | 2.20M | } | 136 | 1.25k | for (auto const& [id, item]: sparse_elements) { | 137 | 1.15k | fn(QIntC::to_int(id), item); | 138 | 1.15k | } | 139 | 1.25k | } |
Unexecuted instantiation: ObjTable<QPDFWriter::NewObject>::forEach(std::__1::function<void (int, QPDFWriter::NewObject const&)>) |
140 | | |
141 | | private: |
142 | | std::map<size_t, T> sparse_elements; |
143 | | // Smallest id present in sparse_elements. |
144 | | size_t min_sparse{std::numeric_limits<size_t>::max()}; |
145 | | |
146 | | inline T& |
147 | | element(size_t idx) |
148 | 1.79M | { |
149 | 1.79M | if (idx < std::vector<T>::size()) { |
150 | 1.70M | return std::vector<T>::operator[](idx); |
151 | 1.70M | } |
152 | 83.7k | return large_element(idx); |
153 | 1.79M | } ObjTable<QPDFWriter::NewObject>::element(unsigned long) Line | Count | Source | 148 | 872k | { | 149 | 872k | if (idx < std::vector<T>::size()) { | 150 | 854k | return std::vector<T>::operator[](idx); | 151 | 854k | } | 152 | 17.7k | return large_element(idx); | 153 | 872k | } |
ObjTable<QPDFWriter::Object>::element(unsigned long) Line | Count | Source | 148 | 919k | { | 149 | 919k | if (idx < std::vector<T>::size()) { | 150 | 853k | return std::vector<T>::operator[](idx); | 151 | 853k | } | 152 | 65.9k | return large_element(idx); | 153 | 919k | } |
|
154 | | |
155 | | // Must only be called by element. Separated out from element to keep inlined code tight. |
156 | | T& |
157 | | large_element(size_t idx) |
158 | 83.7k | { |
159 | 83.7k | static const size_t max_size = std::vector<T>::max_size(); |
160 | 83.7k | if (idx < min_sparse) { |
161 | 935 | min_sparse = idx; |
162 | 935 | } |
163 | 83.7k | if (idx < max_size) { |
164 | 83.6k | return sparse_elements[idx]; |
165 | 83.6k | } |
166 | 104 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); |
167 | 0 | return element(0); // doesn't return |
168 | 83.7k | } ObjTable<QPDFWriter::NewObject>::large_element(unsigned long) Line | Count | Source | 158 | 17.7k | { | 159 | 17.7k | static const size_t max_size = std::vector<T>::max_size(); | 160 | 17.7k | if (idx < min_sparse) { | 161 | 76 | min_sparse = idx; | 162 | 76 | } | 163 | 17.7k | if (idx < max_size) { | 164 | 17.7k | return sparse_elements[idx]; | 165 | 17.7k | } | 166 | 6 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); | 167 | 0 | return element(0); // doesn't return | 168 | 17.7k | } |
ObjTable<QPDFWriter::Object>::large_element(unsigned long) Line | Count | Source | 158 | 65.9k | { | 159 | 65.9k | static const size_t max_size = std::vector<T>::max_size(); | 160 | 65.9k | if (idx < min_sparse) { | 161 | 859 | min_sparse = idx; | 162 | 859 | } | 163 | 65.9k | if (idx < max_size) { | 164 | 65.9k | return sparse_elements[idx]; | 165 | 65.9k | } | 166 | 98 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); | 167 | 0 | return element(0); // doesn't return | 168 | 65.9k | } |
|
169 | | |
170 | | inline T const& |
171 | | element(size_t idx) const |
172 | 546k | { |
173 | 546k | static const size_t max_size = std::vector<T>::max_size(); |
174 | 546k | if (idx < std::vector<T>::size()) { |
175 | 534k | return std::vector<T>::operator[](idx); |
176 | 534k | } |
177 | 12.7k | if (idx < max_size) { |
178 | 12.7k | return sparse_elements.at(idx); |
179 | 12.7k | } |
180 | 0 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); |
181 | 0 | return element(0); // doesn't return |
182 | 12.7k | } ObjTable<QPDFWriter::Object>::element(unsigned long) const Line | Count | Source | 172 | 415k | { | 173 | 415k | static const size_t max_size = std::vector<T>::max_size(); | 174 | 415k | if (idx < std::vector<T>::size()) { | 175 | 406k | return std::vector<T>::operator[](idx); | 176 | 406k | } | 177 | 8.95k | if (idx < max_size) { | 178 | 8.95k | return sparse_elements.at(idx); | 179 | 8.95k | } | 180 | 0 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); | 181 | 0 | return element(0); // doesn't return | 182 | 8.95k | } |
ObjTable<QPDFWriter::NewObject>::element(unsigned long) const Line | Count | Source | 172 | 131k | { | 173 | 131k | static const size_t max_size = std::vector<T>::max_size(); | 174 | 131k | if (idx < std::vector<T>::size()) { | 175 | 128k | return std::vector<T>::operator[](idx); | 176 | 128k | } | 177 | 3.80k | if (idx < max_size) { | 178 | 3.80k | return sparse_elements.at(idx); | 179 | 3.80k | } | 180 | 0 | throw std::runtime_error("Impossibly large object id encountered accessing ObjTable"); | 181 | 0 | return element(0); // doesn't return | 182 | 3.80k | } |
|
183 | | |
184 | | // Must only be called by emplace_back. Separated out from emplace_back to keep inlined code |
185 | | // tight. |
186 | | template <class... Args> |
187 | | T& |
188 | | emplace_back_large(Args&&... args) |
189 | | { |
190 | | auto it = sparse_elements.begin(); |
191 | | auto end = sparse_elements.end(); |
192 | | while (it != end && it->first == std::vector<T>::size()) { |
193 | | std::vector<T>::emplace_back(std::move(it->second)); |
194 | | it = sparse_elements.erase(it); |
195 | | } |
196 | | min_sparse = (it == end) ? std::numeric_limits<size_t>::max() : it->first; |
197 | | return std::vector<T>::emplace_back(std::forward<Args&&...>(args...)); |
198 | | } |
199 | | }; |
200 | | |
201 | | #endif // OBJTABLE_HH |