Serenity Operating System
1/*
2 * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>.
3 * Copyright (c) 2022, Tim Schumacher <timschumi@gmx.de>.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/ByteBuffer.h>
9#include <AK/FixedArray.h>
10#include <AK/MemMem.h>
11#include <AK/MemoryStream.h>
12
13namespace AK {
14
15FixedMemoryStream::FixedMemoryStream(Bytes bytes)
16 : m_bytes(bytes)
17{
18}
19
20FixedMemoryStream::FixedMemoryStream(ReadonlyBytes bytes)
21 : m_bytes({ const_cast<u8*>(bytes.data()), bytes.size() })
22 , m_writing_enabled(false)
23{
24}
25
26bool FixedMemoryStream::is_eof() const
27{
28 return m_offset >= m_bytes.size();
29}
30
31bool FixedMemoryStream::is_open() const
32{
33 return true;
34}
35
36void FixedMemoryStream::close()
37{
38 // FIXME: It doesn't make sense to close a memory stream. Therefore, we don't do anything here. Is that fine?
39}
40
41ErrorOr<void> FixedMemoryStream::truncate(size_t)
42{
43 return Error::from_errno(EBADF);
44}
45
46ErrorOr<Bytes> FixedMemoryStream::read_some(Bytes bytes)
47{
48 auto to_read = min(remaining(), bytes.size());
49 if (to_read == 0)
50 return Bytes {};
51
52 m_bytes.slice(m_offset, to_read).copy_to(bytes);
53 m_offset += to_read;
54 return bytes.trim(to_read);
55}
56
57ErrorOr<size_t> FixedMemoryStream::seek(i64 offset, SeekMode seek_mode)
58{
59 switch (seek_mode) {
60 case SeekMode::SetPosition:
61 if (offset > static_cast<i64>(m_bytes.size()))
62 return Error::from_string_view_or_print_error_and_return_errno("Offset past the end of the stream memory"sv, EINVAL);
63
64 m_offset = offset;
65 break;
66 case SeekMode::FromCurrentPosition:
67 if (offset + static_cast<i64>(m_offset) > static_cast<i64>(m_bytes.size()))
68 return Error::from_string_view_or_print_error_and_return_errno("Offset past the end of the stream memory"sv, EINVAL);
69
70 m_offset += offset;
71 break;
72 case SeekMode::FromEndPosition:
73 if (offset > static_cast<i64>(m_bytes.size()))
74 return Error::from_string_view_or_print_error_and_return_errno("Offset past the start of the stream memory"sv, EINVAL);
75
76 m_offset = m_bytes.size() - offset;
77 break;
78 }
79 return m_offset;
80}
81
82ErrorOr<size_t> FixedMemoryStream::write_some(ReadonlyBytes bytes)
83{
84 VERIFY(m_writing_enabled);
85
86 // FIXME: Can this not error?
87 auto const nwritten = bytes.copy_trimmed_to(m_bytes.slice(m_offset));
88 m_offset += nwritten;
89 return nwritten;
90}
91
92ErrorOr<void> FixedMemoryStream::write_until_depleted(ReadonlyBytes bytes)
93{
94 if (remaining() < bytes.size())
95 return Error::from_string_view_or_print_error_and_return_errno("Write of entire buffer ends past the memory area"sv, EINVAL);
96
97 TRY(write_some(bytes));
98 return {};
99}
100
101Bytes FixedMemoryStream::bytes()
102{
103 VERIFY(m_writing_enabled);
104 return m_bytes;
105}
106ReadonlyBytes FixedMemoryStream::bytes() const
107{
108 return m_bytes;
109}
110
111size_t FixedMemoryStream::offset() const
112{
113 return m_offset;
114}
115
116size_t FixedMemoryStream::remaining() const
117{
118 return m_bytes.size() - m_offset;
119}
120
121ErrorOr<Bytes> AllocatingMemoryStream::read_some(Bytes bytes)
122{
123 size_t read_bytes = 0;
124
125 while (read_bytes < bytes.size()) {
126 VERIFY(m_write_offset >= m_read_offset);
127
128 auto range = TRY(next_read_range());
129 if (range.size() == 0)
130 break;
131
132 auto copied_bytes = range.copy_trimmed_to(bytes.slice(read_bytes));
133
134 read_bytes += copied_bytes;
135 m_read_offset += copied_bytes;
136 }
137
138 cleanup_unused_chunks();
139
140 return bytes.trim(read_bytes);
141}
142
143ErrorOr<size_t> AllocatingMemoryStream::write_some(ReadonlyBytes bytes)
144{
145 size_t written_bytes = 0;
146
147 while (written_bytes < bytes.size()) {
148 VERIFY(m_write_offset >= m_read_offset);
149
150 auto range = TRY(next_write_range());
151
152 auto copied_bytes = bytes.slice(written_bytes).copy_trimmed_to(range);
153
154 written_bytes += copied_bytes;
155 m_write_offset += copied_bytes;
156 }
157
158 return written_bytes;
159}
160
161ErrorOr<void> AllocatingMemoryStream::discard(size_t count)
162{
163 VERIFY(m_write_offset >= m_read_offset);
164
165 if (count > used_buffer_size())
166 return Error::from_string_view_or_print_error_and_return_errno("Number of discarded bytes is higher than the number of allocated bytes"sv, EINVAL);
167
168 m_read_offset += count;
169
170 cleanup_unused_chunks();
171
172 return {};
173}
174
175bool AllocatingMemoryStream::is_eof() const
176{
177 return used_buffer_size() == 0;
178}
179
180bool AllocatingMemoryStream::is_open() const
181{
182 return true;
183}
184
185void AllocatingMemoryStream::close()
186{
187}
188
189size_t AllocatingMemoryStream::used_buffer_size() const
190{
191 return m_write_offset - m_read_offset;
192}
193
194ErrorOr<Optional<size_t>> AllocatingMemoryStream::offset_of(ReadonlyBytes needle) const
195{
196 VERIFY(m_write_offset >= m_read_offset);
197
198 if (m_chunks.size() == 0)
199 return Optional<size_t> {};
200
201 // Ensure that we don't have to trim away more than one block.
202 VERIFY(m_read_offset < chunk_size);
203 VERIFY(m_chunks.size() * chunk_size - m_write_offset < chunk_size);
204
205 auto chunk_count = m_chunks.size();
206 auto search_spans = TRY(FixedArray<ReadonlyBytes>::create(chunk_count));
207
208 for (size_t i = 0; i < chunk_count; i++) {
209 search_spans[i] = m_chunks[i].span();
210 }
211
212 // Trimming is done first to ensure that we don't unintentionally shift around if the first and last chunks are the same.
213 search_spans[chunk_count - 1] = search_spans[chunk_count - 1].trim(m_write_offset % chunk_size);
214 search_spans[0] = search_spans[0].slice(m_read_offset);
215
216 return AK::memmem(search_spans.begin(), search_spans.end(), needle);
217}
218
219ErrorOr<ReadonlyBytes> AllocatingMemoryStream::next_read_range()
220{
221 VERIFY(m_write_offset >= m_read_offset);
222
223 size_t const chunk_index = m_read_offset / chunk_size;
224 size_t const chunk_offset = m_read_offset % chunk_size;
225 size_t const read_size = min(chunk_size - m_read_offset % chunk_size, m_write_offset - m_read_offset);
226
227 if (read_size == 0)
228 return ReadonlyBytes { static_cast<u8*>(nullptr), 0 };
229
230 VERIFY(chunk_index < m_chunks.size());
231
232 return ReadonlyBytes { m_chunks[chunk_index].data() + chunk_offset, read_size };
233}
234
235ErrorOr<Bytes> AllocatingMemoryStream::next_write_range()
236{
237 VERIFY(m_write_offset >= m_read_offset);
238
239 size_t const chunk_index = m_write_offset / chunk_size;
240 size_t const chunk_offset = m_write_offset % chunk_size;
241 size_t const write_size = chunk_size - m_write_offset % chunk_size;
242
243 if (chunk_index >= m_chunks.size())
244 TRY(m_chunks.try_append(TRY(Chunk::create_uninitialized(chunk_size))));
245
246 VERIFY(chunk_index < m_chunks.size());
247
248 return Bytes { m_chunks[chunk_index].data() + chunk_offset, write_size };
249}
250
251void AllocatingMemoryStream::cleanup_unused_chunks()
252{
253 // FIXME: Move these all at once.
254 while (m_read_offset >= chunk_size) {
255 VERIFY(m_write_offset >= m_read_offset);
256
257 auto buffer = m_chunks.take_first();
258 m_read_offset -= chunk_size;
259 m_write_offset -= chunk_size;
260
261 m_chunks.append(move(buffer));
262 }
263}
264
265}