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