Coverage Report

Created: 2026-08-31 06:55

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