summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibPDF/Reader.h
blob: 862decb8e1bc7a7fd370c6b9fe03d8412d1ede1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * Copyright (c) 2021, Matthew Olsson <mattco@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Debug.h>
#include <AK/Function.h>
#include <AK/ScopeGuard.h>
#include <AK/Span.h>
#include <AK/String.h>
#include <AK/Vector.h>

namespace PDF {

class Reader {
public:
    explicit Reader(ReadonlyBytes bytes)
        : m_bytes(bytes)
    {
    }

    ALWAYS_INLINE ReadonlyBytes bytes() const { return m_bytes; }
    ALWAYS_INLINE size_t offset() const { return m_offset; }

    bool done() const
    {
        if (m_forwards)
            return offset() >= bytes().size();
        return m_offset < 0;
    }

    size_t remaining() const
    {
        if (done())
            return 0;

        if (m_forwards)
            return bytes().size() - offset() - 1;
        return offset() + 1;
    }

    void move_by(size_t count)
    {
        if (m_forwards) {
            m_offset += static_cast<ssize_t>(count);
        } else {
            m_offset -= static_cast<ssize_t>(count);
        }
    }

    template<typename T = char>
    T read()
    {
        T value = reinterpret_cast<const T*>(m_bytes.offset(m_offset))[0];
        move_by(sizeof(T));
        return value;
    }

    char peek(size_t shift = 0) const
    {
        auto offset = m_offset + shift * (m_forwards ? 1 : -1);
        return static_cast<char>(m_bytes.at(offset));
    }

    template<typename... T>
    bool matches_any(T... elements) const
    {
        if (done())
            return false;
        auto ch = peek();
        return ((ch == elements) || ...);
    }

    bool matches(char ch) const
    {
        return !done() && peek() == ch;
    }

    bool matches(char const* chars) const
    {
        String string(chars);
        if (remaining() < string.length())
            return false;

        if (!m_forwards)
            string = string.reverse();

        for (size_t i = 0; i < string.length(); i++) {
            if (peek(i) != string[i])
                return false;
        }

        return true;
    }

    template<typename T = char>
    void move_to(size_t offset)
    {
        VERIFY(offset < m_bytes.size());
        m_offset = static_cast<ssize_t>(offset);
    }

    void move_until(char ch)
    {
        while (!done() && peek() != ch)
            move_by(1);
    }

    void move_until(Function<bool(char)> predicate)
    {
        while (!done() && !predicate(peek()))
            move_by(1);
    }

    ALWAYS_INLINE void move_while(Function<bool(char)> predicate)
    {
        move_until([&predicate](char t) { return !predicate(t); });
    }

    ALWAYS_INLINE void set_reading_forwards() { m_forwards = true; }
    ALWAYS_INLINE void set_reading_backwards() { m_forwards = false; }

    ALWAYS_INLINE void save() { m_saved_offsets.append(m_offset); }
    ALWAYS_INLINE void load() { m_offset = m_saved_offsets.take_last(); }
    ALWAYS_INLINE void discard() { m_saved_offsets.take_last(); }

#ifdef PDF_DEBUG
    void dump_state() const
    {
        dbgln("Reader State (offset={} size={})", offset(), bytes().size());

        size_t from = max(0, static_cast<int>(offset()) - 10);
        size_t to = min(bytes().size() - 1, offset() + 10);

        for (auto i = from; i <= to; i++) {
            char value = static_cast<char>(bytes().at(i));
            auto line = String::formatted("  {}: '{}' (value={:3d}) ", i, value, static_cast<u8>(value));
            if (i == offset()) {
                dbgln("{} <<< current location, forwards={}", line, m_forwards);
            } else {
                dbgln("{}", line);
            }
        }
        dbgln();
    }
#endif

private:
    ReadonlyBytes m_bytes;
    ssize_t m_offset { 0 };
    Vector<ssize_t> m_saved_offsets;
    bool m_forwards { true };
};

}