/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 | | } |