Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibPDF/Reader.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, 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/Debug.h>
11
#include <AK/Function.h>
12
#include <AK/ScopeGuard.h>
13
#include <AK/Span.h>
14
#include <AK/Vector.h>
15
#include <LibPDF/Error.h>
16
17
namespace PDF {
18
19
class Reader {
20
public:
21
    explicit Reader(ReadonlyBytes bytes)
22
11
        : m_bytes(bytes)
23
11
    {
24
11
    }
25
26
4.39k
    ALWAYS_INLINE ReadonlyBytes bytes() const { return m_bytes; }
27
233k
    ALWAYS_INLINE size_t offset() const { return m_offset; }
28
29
    bool done() const
30
23.7M
    {
31
23.7M
        if (m_forwards)
32
3.82k
            return offset() >= bytes().size();
33
23.7M
        return m_offset < 0;
34
23.7M
    }
35
36
    size_t remaining() const
37
228k
    {
38
228k
        if (done())
39
0
            return 0;
40
41
228k
        if (m_forwards)
42
442
            return bytes().size() - offset();
43
228k
        return offset() + 1;
44
228k
    }
45
46
    void move_by(ssize_t count)
47
11.5M
    {
48
11.5M
        if (m_forwards) {
49
1.09k
            m_offset += count;
50
11.5M
        } else {
51
11.5M
            m_offset -= count;
52
11.5M
        }
53
11.5M
    }
54
55
    template<typename T = char>
56
    T read()
57
133k
    {
58
133k
        T value = reinterpret_cast<T const*>(m_bytes.offset(m_offset))[0];
59
133k
        move_by(sizeof(T));
60
133k
        return value;
61
133k
    }
62
63
    template<typename T = char>
64
    PDFErrorOr<T> try_read()
65
    {
66
        if (sizeof(T) + m_offset > m_bytes.size()) {
67
            auto message = ByteString::formatted("Cannot read {} bytes at offset {} of ReadonlyBytes of size {}", sizeof(T), m_offset, m_bytes.size());
68
            return Error { Error::Type::Parse, message };
69
        }
70
        return read<T>();
71
    }
72
73
    char peek(size_t shift = 0) const
74
23.5M
    {
75
23.5M
        auto offset = m_offset + shift * (m_forwards ? 1 : -1);
76
23.5M
        return static_cast<char>(m_bytes.at(offset));
77
23.5M
    }
78
79
    template<typename... T>
80
    bool matches_any(T... elements) const
81
314
    {
82
314
        if (done())
83
0
            return false;
84
314
        auto ch = peek();
85
5.52k
        return ((ch == elements) || ...);
86
314
    }
bool PDF::Reader::matches_any<char, char>(char, char) const
Line
Count
Source
81
8
    {
82
8
        if (done())
83
0
            return false;
84
8
        auto ch = peek();
85
16
        return ((ch == elements) || ...);
86
8
    }
bool PDF::Reader::matches_any<char, char, char, char, char, char, char, char, char, char>(char, char, char, char, char, char, char, char, char, char) const
Line
Count
Source
81
306
    {
82
306
        if (done())
83
0
            return false;
84
306
        auto ch = peek();
85
5.50k
        return ((ch == elements) || ...);
86
306
    }
87
88
    bool matches(char ch) const
89
1.10k
    {
90
1.10k
        return !done() && peek() == ch;
91
1.10k
    }
92
93
    bool matches(char const* chars) const
94
228k
    {
95
228k
        ByteString string(chars);
96
228k
        if (remaining() < string.length())
97
0
            return false;
98
99
228k
        if (!m_forwards)
100
228k
            string = string.reverse();
101
102
299k
        for (size_t i = 0; i < string.length(); i++) {
103
296k
            if (peek(i) != string[i])
104
226k
                return false;
105
296k
        }
106
107
2.61k
        return true;
108
228k
    }
109
110
    template<typename T = char>
111
    void move_to(size_t offset)
112
34
    {
113
34
        VERIFY(offset <= m_bytes.size());
114
34
        m_offset = static_cast<ssize_t>(offset);
115
34
    }
116
117
    void move_until(char ch)
118
0
    {
119
0
        while (!done() && peek() != ch)
120
0
            move_by(1);
121
0
    }
122
123
    void move_until(AK::Function<bool(char)> predicate)
124
114k
    {
125
11.5M
        while (!done() && !predicate(peek()))
126
11.3M
            move_by(1);
127
114k
    }
128
129
    ALWAYS_INLINE void move_while(AK::Function<bool(char)> predicate)
130
0
    {
131
0
        move_until([&predicate](char t) { return !predicate(t); });
132
0
    }
133
134
    static bool is_eol(char);
135
    static bool is_whitespace(char);
136
    static bool is_non_eol_whitespace(char);
137
138
    bool matches_eol() const;
139
    bool matches_whitespace() const;
140
    bool matches_non_eol_whitespace() const;
141
    bool matches_number() const;
142
    bool matches_delimiter() const;
143
    bool matches_regular_character() const;
144
145
    bool consume_eol();
146
    bool consume_whitespace();
147
    bool consume_non_eol_whitespace();
148
    char consume();
149
    void consume(int amount);
150
    bool consume(char);
151
152
17
    ALWAYS_INLINE void set_reading_forwards() { m_forwards = true; }
153
17
    ALWAYS_INLINE void set_reading_backwards() { m_forwards = false; }
154
155
16
    ALWAYS_INLINE void save() { m_saved_offsets.append(m_offset); }
156
6
    ALWAYS_INLINE void load() { m_offset = m_saved_offsets.take_last(); }
157
10
    ALWAYS_INLINE void discard() { m_saved_offsets.take_last(); }
158
159
#ifdef PDF_DEBUG
160
    void dump_state() const
161
0
    {
162
0
        dbgln("Reader State (offset={} size={})", offset(), bytes().size());
163
0
164
0
        size_t from = max(0, static_cast<int>(offset()) - 10);
165
0
        size_t to = min(bytes().size() - 1, offset() + 10);
166
0
167
0
        for (auto i = from; i <= to; i++) {
168
0
            char value = static_cast<char>(bytes().at(i));
169
0
            auto line = ByteString::formatted("  {}: '{}' (value={:3d}) ", i, value, static_cast<u8>(value));
170
0
            if (i == offset()) {
171
0
                dbgln("{} <<< current location, forwards={}", line, m_forwards);
172
0
            } else {
173
0
                dbgln("{}", line);
174
0
            }
175
0
        }
176
0
        dbgln();
177
0
    }
178
#endif
179
180
private:
181
    ReadonlyBytes m_bytes;
182
    ssize_t m_offset { 0 };
183
    Vector<ssize_t> m_saved_offsets;
184
    bool m_forwards { true };
185
};
186
187
}