Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/AK/MemoryStream.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>.
3
 * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org>.
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/ByteBuffer.h>
11
#include <AK/Error.h>
12
#include <AK/OwnPtr.h>
13
#include <AK/Stream.h>
14
#include <AK/Vector.h>
15
16
namespace AK {
17
18
/// A stream class that allows for reading/writing on a preallocated memory area
19
/// using a single read/write head.
20
class FixedMemoryStream : public SeekableStream {
21
public:
22
    enum class Mode {
23
        ReadOnly,
24
        ReadWrite,
25
    };
26
27
    explicit FixedMemoryStream(Bytes bytes, Mode mode = Mode::ReadWrite);
28
    explicit FixedMemoryStream(ReadonlyBytes bytes);
29
30
    virtual bool is_eof() const override;
31
    virtual bool is_open() const override;
32
    virtual void close() override;
33
    virtual ErrorOr<void> truncate(size_t) override;
34
    virtual ErrorOr<Bytes> read_some(Bytes bytes) override;
35
    virtual ErrorOr<void> read_until_filled(Bytes bytes) override;
36
37
    virtual ErrorOr<size_t> seek(i64 offset, SeekMode seek_mode = SeekMode::SetPosition) override;
38
39
    virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override;
40
    virtual ErrorOr<void> write_until_depleted(ReadonlyBytes bytes) override;
41
42
    size_t offset() const;
43
    size_t remaining() const;
44
45
    /// Read a value, but referring to the stream's underlying data instead of copying it.
46
    /// Of course, only use this if you know the lifetime of the data will exceed the value's.
47
    // FIXME: Would be nicer to be able to return T& but Variant (and thus ErrorOr) can't hold references.
48
    template<typename T>
49
    requires(Traits<T>::is_trivially_serializable())
50
    ErrorOr<T*> read_in_place()
51
1.06M
    {
52
1.06M
        if constexpr (!IsConst<T>) {
53
4
            if (!m_writing_enabled)
54
4
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
4
        }
56
57
0
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
1.06M
        TRY(discard(sizeof(T)));
59
1.06M
        return value;
60
1.06M
    }
Unexecuted instantiation: AK::ErrorOr<Gfx::FontFileHeader const*, AK::Error> AK::FixedMemoryStream::read_in_place<Gfx::FontFileHeader const>() requires Traits<Gfx::FontFileHeader const>::is_trivially_serializable()
AK::ErrorOr<OpenType::TableDirectory const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::TableDirectory const>() requires Traits<OpenType::TableDirectory const>::is_trivially_serializable()
Line
Count
Source
51
3.62k
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
3.62k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
3.62k
        TRY(discard(sizeof(T)));
59
3.61k
        return value;
60
3.62k
    }
AK::ErrorOr<OpenType::TableRecord const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::TableRecord const>() requires Traits<OpenType::TableRecord const>::is_trivially_serializable()
Line
Count
Source
51
81.4k
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
81.4k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
81.4k
        TRY(discard(sizeof(T)));
59
80.5k
        return value;
60
81.4k
    }
AK::ErrorOr<OpenType::TTCHeaderV1*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::TTCHeaderV1>() requires Traits<OpenType::TTCHeaderV1>::is_trivially_serializable()
Line
Count
Source
51
4
    {
52
4
        if constexpr (!IsConst<T>) {
53
4
            if (!m_writing_enabled)
54
4
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
4
        }
56
57
0
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
4
        TRY(discard(sizeof(T)));
59
4
        return value;
60
4
    }
AK::ErrorOr<OpenType::Kern::Header const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::Kern::Header const>() requires Traits<OpenType::Kern::Header const>::is_trivially_serializable()
Line
Count
Source
51
267
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
267
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
267
        TRY(discard(sizeof(T)));
59
266
        return value;
60
267
    }
AK::ErrorOr<OpenType::Kern::SubtableHeader const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::Kern::SubtableHeader const>() requires Traits<OpenType::Kern::SubtableHeader const>::is_trivially_serializable()
Line
Count
Source
51
980k
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
980k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
980k
        TRY(discard(sizeof(T)));
59
980k
        return value;
60
980k
    }
AK::ErrorOr<OpenType::Kern::Format0 const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::Kern::Format0 const>() requires Traits<OpenType::Kern::Format0 const>::is_trivially_serializable()
Line
Count
Source
51
571
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
571
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
571
        TRY(discard(sizeof(T)));
59
569
        return value;
60
571
    }
AK::ErrorOr<OpenType::GPOS::Version1_0 const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::GPOS::Version1_0 const>() requires Traits<OpenType::GPOS::Version1_0 const>::is_trivially_serializable()
Line
Count
Source
51
695
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
695
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
695
        TRY(discard(sizeof(T)));
59
681
        return value;
60
695
    }
AK::ErrorOr<OpenType::ScriptList const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::ScriptList const>() requires Traits<OpenType::ScriptList const>::is_trivially_serializable()
Line
Count
Source
51
652
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
652
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
652
        TRY(discard(sizeof(T)));
59
649
        return value;
60
652
    }
AK::ErrorOr<OpenType::FeatureList const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::FeatureList const>() requires Traits<OpenType::FeatureList const>::is_trivially_serializable()
Line
Count
Source
51
614
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
614
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
614
        TRY(discard(sizeof(T)));
59
611
        return value;
60
614
    }
AK::ErrorOr<OpenType::LookupList const*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::LookupList const>() requires Traits<OpenType::LookupList const>::is_trivially_serializable()
Line
Count
Source
51
591
    {
52
        if constexpr (!IsConst<T>) {
53
            if (!m_writing_enabled)
54
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const reference from a read-only FixedMemoryStream"sv, EINVAL);
55
        }
56
57
591
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
591
        TRY(discard(sizeof(T)));
59
588
        return value;
60
591
    }
61
62
    /// Read a span of values, referring to the stream's underlying data instead of copying it.
63
    /// Of course, only use this if you know the lifetime of the data will exceed the span's.
64
    template<typename T>
65
    requires(Traits<T>::is_trivially_serializable())
66
    ErrorOr<Span<T>> read_in_place(size_t count)
67
22.4k
    {
68
22.4k
        if constexpr (!IsConst<T>) {
69
14.6k
            if (!m_writing_enabled)
70
0
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
14.6k
        }
72
73
14.6k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
22.4k
        TRY(discard(sizeof(T) * count));
75
22.2k
        return span;
76
22.4k
    }
AK::ErrorOr<AK::Span<unsigned char const>, AK::Error> AK::FixedMemoryStream::read_in_place<unsigned char const>(unsigned long) requires Traits<unsigned char const>::is_trivially_serializable()
Line
Count
Source
67
5.38k
    {
68
        if constexpr (!IsConst<T>) {
69
            if (!m_writing_enabled)
70
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
        }
72
73
5.38k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
5.38k
        TRY(discard(sizeof(T) * count));
75
5.33k
        return span;
76
5.38k
    }
AK::ErrorOr<AK::Span<OpenType::Kern::Format0Pair const>, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::Kern::Format0Pair const>(unsigned long) requires Traits<OpenType::Kern::Format0Pair const>::is_trivially_serializable()
Line
Count
Source
67
569
    {
68
        if constexpr (!IsConst<T>) {
69
            if (!m_writing_enabled)
70
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
        }
72
73
569
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
569
        TRY(discard(sizeof(T) * count));
75
544
        return span;
76
569
    }
AK::ErrorOr<AK::Span<OpenType::ScriptRecord const>, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::ScriptRecord const>(unsigned long) requires Traits<OpenType::ScriptRecord const>::is_trivially_serializable()
Line
Count
Source
67
649
    {
68
        if constexpr (!IsConst<T>) {
69
            if (!m_writing_enabled)
70
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
        }
72
73
649
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
649
        TRY(discard(sizeof(T) * count));
75
624
        return span;
76
649
    }
AK::ErrorOr<AK::Span<OpenType::FeatureRecord const>, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::FeatureRecord const>(unsigned long) requires Traits<OpenType::FeatureRecord const>::is_trivially_serializable()
Line
Count
Source
67
611
    {
68
        if constexpr (!IsConst<T>) {
69
            if (!m_writing_enabled)
70
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
        }
72
73
611
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
611
        TRY(discard(sizeof(T) * count));
75
604
        return span;
76
611
    }
AK::ErrorOr<AK::Span<AK::BigEndian<unsigned short> const>, AK::Error> AK::FixedMemoryStream::read_in_place<AK::BigEndian<unsigned short> const>(unsigned long) requires Traits<AK::BigEndian<unsigned short> const>::is_trivially_serializable()
Line
Count
Source
67
588
    {
68
        if constexpr (!IsConst<T>) {
69
            if (!m_writing_enabled)
70
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
        }
72
73
588
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
588
        TRY(discard(sizeof(T) * count));
75
584
        return span;
76
588
    }
AK::ErrorOr<AK::Span<unsigned char>, AK::Error> AK::FixedMemoryStream::read_in_place<unsigned char>(unsigned long) requires Traits<unsigned char>::is_trivially_serializable()
Line
Count
Source
67
14.6k
    {
68
14.6k
        if constexpr (!IsConst<T>) {
69
14.6k
            if (!m_writing_enabled)
70
0
                return Error::from_string_view_or_print_error_and_return_errno("Tried to obtain a non-const span from a read-only FixedMemoryStream"sv, EINVAL);
71
14.6k
        }
72
73
14.6k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
14.6k
        TRY(discard(sizeof(T) * count));
75
14.5k
        return span;
76
14.6k
    }
77
78
private:
79
    Bytes m_bytes;
80
    size_t m_offset { 0 };
81
    bool m_writing_enabled { true };
82
};
83
84
/// A stream class that allows for writing to an automatically allocating memory area
85
/// and reading back the written data afterwards.
86
class AllocatingMemoryStream final : public Stream {
87
public:
88
    static constexpr size_t CHUNK_SIZE = 4096;
89
90
    virtual ErrorOr<Bytes> read_some(Bytes) override;
91
    virtual ErrorOr<size_t> write_some(ReadonlyBytes) override;
92
    virtual ErrorOr<void> discard(size_t) override;
93
    virtual bool is_eof() const override;
94
    virtual bool is_open() const override;
95
    virtual void close() override;
96
97
    size_t used_buffer_size() const;
98
99
    ErrorOr<Optional<size_t>> offset_of(ReadonlyBytes needle) const;
100
101
private:
102
    // Note: We set the inline buffer capacity to zero to make moving chunks as efficient as possible.
103
    using Chunk = AK::Detail::ByteBuffer<0>;
104
105
    ErrorOr<ReadonlyBytes> next_read_range();
106
    ErrorOr<Bytes> next_write_range();
107
    void cleanup_unused_chunks();
108
109
    Vector<Chunk> m_chunks;
110
    size_t m_read_offset = 0;
111
    size_t m_write_offset = 0;
112
};
113
114
}
115
116
#if USING_AK_GLOBALLY
117
using AK::AllocatingMemoryStream;
118
using AK::FixedMemoryStream;
119
#endif