Serenity Operating System
at master 265 lines 7.1 kB view raw
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}