Coverage Report

Created: 2026-01-09 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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