Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibPDF/XRefTable.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/ByteString.h>
10
#include <AK/Format.h>
11
#include <AK/RefCounted.h>
12
#include <AK/Vector.h>
13
#include <LibPDF/Error.h>
14
15
namespace PDF {
16
17
constexpr u64 invalid_byte_offset = NumericLimits<u64>::max();
18
19
struct XRefEntry {
20
    u64 byte_offset { invalid_byte_offset };
21
    u16 generation_number { 0 };
22
    bool in_use { false };
23
    bool compressed { false };
24
};
25
26
struct XRefSection {
27
    int starting_index;
28
    int count;
29
    Vector<XRefEntry> entries;
30
};
31
32
class XRefTable final : public RefCounted<XRefTable> {
33
public:
34
    PDFErrorOr<void> merge(XRefTable&& other)
35
0
    {
36
0
        auto this_size = m_entries.size();
37
0
        auto other_size = other.m_entries.size();
38
0
        TRY(m_entries.try_ensure_capacity(other_size));
39
40
0
        for (size_t i = 0; i < other_size; i++) {
41
0
            auto other_entry = other.m_entries[i];
42
0
            if (i >= this_size) {
43
0
                m_entries.unchecked_append(other_entry);
44
0
                continue;
45
0
            }
46
47
0
            auto this_entry = m_entries[i];
48
49
            // Only add values that we don't already have.
50
0
            if (this_entry.byte_offset == invalid_byte_offset)
51
0
                m_entries[i] = other_entry;
52
0
        }
53
54
0
        return {};
55
0
    }
56
57
    void add_section(XRefSection const& section)
58
0
    {
59
0
        m_entries.ensure_capacity(section.starting_index + section.count);
60
61
0
        for (int i = static_cast<int>(m_entries.size()); i < section.starting_index; i++)
62
0
            m_entries.append(XRefEntry {});
63
64
0
        for (auto& entry : section.entries)
65
0
            m_entries.append(entry);
66
0
    }
67
68
0
    void set_trailer(RefPtr<DictObject> trailer) { m_trailer = trailer; }
69
70
0
    ALWAYS_INLINE Vector<XRefEntry>& entries() { return m_entries; }
71
72
0
    ALWAYS_INLINE RefPtr<DictObject> const& trailer() const { return m_trailer; }
73
74
    [[nodiscard]] ALWAYS_INLINE bool has_object(size_t index) const
75
0
    {
76
0
        return index < m_entries.size() && m_entries[index].byte_offset != invalid_byte_offset;
77
0
    }
78
79
    [[nodiscard]] ALWAYS_INLINE u64 byte_offset_for_object(size_t index) const
80
0
    {
81
0
        VERIFY(has_object(index));
82
0
        return m_entries[index].byte_offset;
83
0
    }
84
85
    [[nodiscard]] ALWAYS_INLINE u64 object_stream_for_object(size_t index) const
86
0
    {
87
0
        return byte_offset_for_object(index);
88
0
    }
89
90
    [[nodiscard]] ALWAYS_INLINE u16 generation_number_for_object(size_t index) const
91
0
    {
92
0
        VERIFY(has_object(index));
93
0
        return m_entries[index].generation_number;
94
0
    }
95
96
    [[nodiscard]] ALWAYS_INLINE u16 object_stream_index_for_object(size_t index) const
97
0
    {
98
0
        return generation_number_for_object(index);
99
0
    }
100
101
    [[nodiscard]] ALWAYS_INLINE bool is_object_in_use(size_t index) const
102
0
    {
103
0
        VERIFY(has_object(index));
104
0
        return m_entries[index].in_use;
105
0
    }
106
107
    [[nodiscard]] ALWAYS_INLINE bool is_object_compressed(size_t index) const
108
0
    {
109
0
        VERIFY(has_object(index));
110
0
        return m_entries[index].compressed;
111
0
    }
112
113
private:
114
    friend struct AK::Formatter<PDF::XRefTable>;
115
116
    Vector<XRefEntry> m_entries;
117
    RefPtr<DictObject> m_trailer;
118
};
119
120
}
121
122
namespace AK {
123
124
template<>
125
struct Formatter<PDF::XRefEntry> : Formatter<StringView> {
126
    ErrorOr<void> format(FormatBuilder& builder, PDF::XRefEntry const& entry)
127
0
    {
128
0
        return Formatter<StringView>::format(builder,
129
0
            ByteString::formatted("XRefEntry {{ offset={} generation={} used={} }}",
130
0
                entry.byte_offset,
131
0
                entry.generation_number,
132
0
                entry.in_use));
133
0
    }
134
};
135
136
template<>
137
struct Formatter<PDF::XRefTable> : Formatter<StringView> {
138
    ErrorOr<void> format(FormatBuilder& format_builder, PDF::XRefTable const& table)
139
0
    {
140
0
        StringBuilder builder;
141
0
        builder.append("XRefTable {"sv);
142
0
        for (auto& entry : table.m_entries)
143
0
            builder.appendff("\n  {}", entry);
144
0
        builder.append("\n}"sv);
145
0
        return Formatter<StringView>::format(format_builder, builder.to_byte_string());
146
0
    }
147
};
148
149
}