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