Coverage Report

Created: 2025-12-18 07:52

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.19M
    {
52
1.19M
        if constexpr (!IsConst<T>) {
53
5
            if (!m_writing_enabled)
54
5
                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
5
        }
56
57
0
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
1.19M
        TRY(discard(sizeof(T)));
59
1.19M
        return value;
60
1.19M
    }
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.13k
    {
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.13k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
3.13k
        TRY(discard(sizeof(T)));
59
3.12k
        return value;
60
3.13k
    }
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
78.2k
    {
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
78.2k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
78.2k
        TRY(discard(sizeof(T)));
59
77.4k
        return value;
60
78.2k
    }
AK::ErrorOr<OpenType::TTCHeaderV1*, AK::Error> AK::FixedMemoryStream::read_in_place<OpenType::TTCHeaderV1>() requires Traits<OpenType::TTCHeaderV1>::is_trivially_serializable()
Line
Count
Source
51
5
    {
52
5
        if constexpr (!IsConst<T>) {
53
5
            if (!m_writing_enabled)
54
5
                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
5
        }
56
57
0
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
5
        TRY(discard(sizeof(T)));
59
5
        return value;
60
5
    }
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
205
    {
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
205
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
205
        TRY(discard(sizeof(T)));
59
204
        return value;
60
205
    }
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
1.10M
    {
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
1.10M
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
1.10M
        TRY(discard(sizeof(T)));
59
1.10M
        return value;
60
1.10M
    }
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
2.75k
    {
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
2.75k
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
2.75k
        TRY(discard(sizeof(T)));
59
2.75k
        return value;
60
2.75k
    }
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
725
    {
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
725
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
725
        TRY(discard(sizeof(T)));
59
713
        return value;
60
725
    }
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
680
    {
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
680
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
680
        TRY(discard(sizeof(T)));
59
677
        return value;
60
680
    }
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
643
    {
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
643
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
643
        TRY(discard(sizeof(T)));
59
640
        return value;
60
643
    }
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
623
    {
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
623
        T* value = reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset));
58
623
        TRY(discard(sizeof(T)));
59
620
        return value;
60
623
    }
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
24.0k
    {
68
24.0k
        if constexpr (!IsConst<T>) {
69
11.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
11.6k
        }
72
73
11.6k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
24.0k
        TRY(discard(sizeof(T) * count));
75
23.8k
        return span;
76
24.0k
    }
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
7.74k
    {
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
7.74k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
7.74k
        TRY(discard(sizeof(T) * count));
75
7.54k
        return span;
76
7.74k
    }
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
2.75k
    {
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
2.75k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
2.75k
        TRY(discard(sizeof(T) * count));
75
2.73k
        return span;
76
2.75k
    }
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
677
    {
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
677
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
677
        TRY(discard(sizeof(T) * count));
75
654
        return span;
76
677
    }
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
640
    {
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
640
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
640
        TRY(discard(sizeof(T) * count));
75
633
        return span;
76
640
    }
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
620
    {
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
620
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
620
        TRY(discard(sizeof(T) * count));
75
614
        return span;
76
620
    }
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
11.6k
    {
68
11.6k
        if constexpr (!IsConst<T>) {
69
11.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
11.6k
        }
72
73
11.6k
        Span<T> span { reinterpret_cast<T*>(m_bytes.offset_pointer(m_offset)), count };
74
11.6k
        TRY(discard(sizeof(T) * count));
75
11.6k
        return span;
76
11.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