Coverage Report

Created: 2025-09-05 06:52

/src/serenity/Userland/Libraries/LibPDF/Reader.h
Line
Count
Source (jump to first uncovered line)
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
15
        : m_bytes(bytes)
23
15
    {
24
15
    }
25
26
7.15k
    ALWAYS_INLINE ReadonlyBytes bytes() const { return m_bytes; }
27
345k
    ALWAYS_INLINE size_t offset() const { return m_offset; }
28
29
    bool done() const
30
32.4M
    {
31
32.4M
        if (m_forwards)
32
6.27k
            return offset() >= bytes().size();
33
32.4M
        return m_offset < 0;
34
32.4M
    }
35
36
    size_t remaining() const
37
339k
    {
38
339k
        if (done())
39
0
            return 0;
40
41
339k
        if (m_forwards)
42
688
            return bytes().size() - offset();
43
338k
        return offset() + 1;
44
339k
    }
45
46
    void move_by(ssize_t count)
47
15.7M
    {
48
15.7M
        if (m_forwards) {
49
1.86k
            m_offset += count;
50
15.7M
        } else {
51
15.7M
            m_offset -= count;
52
15.7M
        }
53
15.7M
    }
54
55
    template<typename T = char>
56
    T read()
57
191k
    {
58
191k
        T value = reinterpret_cast<T const*>(m_bytes.offset(m_offset))[0];
59
191k
        move_by(sizeof(T));
60
191k
        return value;
61
191k
    }
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
32.1M
    {
75
32.1M
        auto offset = m_offset + shift * (m_forwards ? 1 : -1);
76
32.1M
        return static_cast<char>(m_bytes.at(offset));
77
32.1M
    }
78
79
    template<typename... T>
80
    bool matches_any(T... elements) const
81
534
    {
82
534
        if (done())
83
0
            return false;
84
534
        auto ch = peek();
85
9.43k
        return ((ch == elements) || ...);
86
534
    }
bool PDF::Reader::matches_any<char, char>(char, char) const
Line
Count
Source
81
11
    {
82
11
        if (done())
83
0
            return false;
84
11
        auto ch = peek();
85
22
        return ((ch == elements) || ...);
86
11
    }
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
523
    {
82
523
        if (done())
83
0
            return false;
84
523
        auto ch = peek();
85
9.41k
        return ((ch == elements) || ...);
86
523
    }
87
88
    bool matches(char ch) const
89
1.76k
    {
90
1.76k
        return !done() && peek() == ch;
91
1.76k
    }
92
93
    bool matches(char const* chars) const
94
339k
    {
95
339k
        ByteString string(chars);
96
339k
        if (remaining() < string.length())
97
0
            return false;
98
99
339k
        if (!m_forwards)
100
338k
            string = string.reverse();
101
102
450k
        for (size_t i = 0; i < string.length(); i++) {
103
447k
            if (peek(i) != string[i])
104
336k
                return false;
105
447k
        }
106
107
3.05k
        return true;
108
339k
    }
109
110
    template<typename T = char>
111
    void move_to(size_t offset)
112
50
    {
113
50
        VERIFY(offset <= m_bytes.size());
114
50
        m_offset = static_cast<ssize_t>(offset);
115
50
    }
116
117
    void move_until(char ch)
118
1
    {
119
136
        while (!done() && peek() != ch)
120
135
            move_by(1);
121
1
    }
122
123
    void move_until(AK::Function<bool(char)> predicate)
124
169k
    {
125
15.6M
        while (!done() && !predicate(peek()))
126
15.5M
            move_by(1);
127
169k
    }
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
25
    ALWAYS_INLINE void set_reading_forwards() { m_forwards = true; }
153
25
    ALWAYS_INLINE void set_reading_backwards() { m_forwards = false; }
154
155
25
    ALWAYS_INLINE void save() { m_saved_offsets.append(m_offset); }
156
9
    ALWAYS_INLINE void load() { m_offset = m_saved_offsets.take_last(); }
157
16
    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
}