diff options
author | asynts <asynts@gmail.com> | 2020-09-01 11:43:32 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-01 17:25:26 +0200 |
commit | b68a8730678c07d597eefd4d3a8b796412416a82 (patch) | |
tree | 826e2ab8a9458ff9465faeedd382e1b31528bc53 /AK | |
parent | f9516a99bf824839e48283dda02f2913c6143e4a (diff) | |
download | serenity-b68a8730678c07d597eefd4d3a8b796412416a82.zip |
AK: Move memory streams into their own header.
Diffstat (limited to 'AK')
-rw-r--r-- | AK/MemoryStream.h | 299 | ||||
-rw-r--r-- | AK/Stream.h | 289 | ||||
-rw-r--r-- | AK/Tests/TestMemoryStream.cpp (renamed from AK/Tests/TestStream.cpp) | 4 |
3 files changed, 302 insertions, 290 deletions
diff --git a/AK/MemoryStream.h b/AK/MemoryStream.h new file mode 100644 index 0000000000..96df59f3c9 --- /dev/null +++ b/AK/MemoryStream.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/ByteBuffer.h> +#include <AK/MemMem.h> +#include <AK/Stream.h> +#include <AK/Vector.h> + +namespace AK { + +class InputMemoryStream final : public InputStream { +public: + InputMemoryStream(ReadonlyBytes bytes) + : m_bytes(bytes) + { + } + + bool eof() const override { return m_offset >= m_bytes.size(); } + + size_t read(Bytes bytes) override + { + const auto count = min(bytes.size(), remaining()); + __builtin_memcpy(bytes.data(), m_bytes.data() + m_offset, count); + m_offset += count; + return count; + } + + bool read_or_error(Bytes bytes) override + { + if (remaining() < bytes.size()) { + set_recoverable_error(); + return false; + } + + __builtin_memcpy(bytes.data(), m_bytes.data() + m_offset, bytes.size()); + m_offset += bytes.size(); + return true; + } + + bool discard_or_error(size_t count) override + { + if (remaining() < count) { + set_recoverable_error(); + return false; + } + + m_offset += count; + return true; + } + + void seek(size_t offset) + { + ASSERT(offset < m_bytes.size()); + m_offset = offset; + } + + u8 peek_or_error() const + { + if (remaining() == 0) { + set_recoverable_error(); + return 0; + } + + return m_bytes[m_offset]; + } + + // LEB128 is a variable-length encoding for integers + bool read_LEB128_unsigned(size_t& result) + { + const auto backup = m_offset; + + result = 0; + size_t num_bytes = 0; + while (true) { + // Note. The implementation in AK::BufferStream::read_LEB128_unsigned read one + // past the end, this is fixed here. + if (eof()) { + m_offset = backup; + set_recoverable_error(); + return false; + } + + const u8 byte = m_bytes[m_offset]; + result = (result) | (static_cast<size_t>(byte & ~(1 << 7)) << (num_bytes * 7)); + ++m_offset; + if (!(byte & (1 << 7))) + break; + ++num_bytes; + } + + return true; + } + + // LEB128 is a variable-length encoding for integers + bool read_LEB128_signed(ssize_t& result) + { + const auto backup = m_offset; + + result = 0; + size_t num_bytes = 0; + u8 byte = 0; + + do { + // Note. The implementation in AK::BufferStream::read_LEB128_unsigned read one + // past the end, this is fixed here. + if (eof()) { + m_offset = backup; + set_recoverable_error(); + return false; + } + + byte = m_bytes[m_offset]; + result = (result) | (static_cast<size_t>(byte & ~(1 << 7)) << (num_bytes * 7)); + ++m_offset; + ++num_bytes; + } while (byte & (1 << 7)); + + if (num_bytes * 7 < sizeof(size_t) * 4 && (byte & 0x40)) { + // sign extend + result |= ((size_t)(-1) << (num_bytes * 7)); + } + + return true; + } + + ReadonlyBytes bytes() const { return m_bytes; } + size_t offset() const { return m_offset; } + size_t remaining() const { return m_bytes.size() - m_offset; } + +private: + ReadonlyBytes m_bytes; + size_t m_offset { 0 }; +}; + +// All data written to this stream can be read from it. Reading and writing is done +// using different offsets, meaning that it is not necessary to seek to the start +// before reading; this behaviour differs from BufferStream. +class DuplexMemoryStream final : public DuplexStream { +public: + static constexpr size_t chunk_size = 4 * 1024; + + bool eof() const override { return m_write_offset == m_read_offset; } + + bool discard_or_error(size_t count) override + { + if (m_write_offset - m_read_offset < count) { + set_recoverable_error(); + return false; + } + + m_read_offset += count; + try_discard_chunks(); + return true; + } + + Optional<size_t> offset_of(ReadonlyBytes value) const + { + if (value.size() > remaining()) + return {}; + + // First, find which chunk we're in. + auto chunk_index = (m_read_offset - m_base_offset) / chunk_size; + auto last_written_chunk_index = (m_write_offset - m_base_offset) / chunk_size; + auto first_chunk_index = chunk_index; + auto last_written_chunk_offset = m_write_offset % chunk_size; + auto first_chunk_offset = m_read_offset % chunk_size; + size_t last_chunk_offset = 0; + auto found_value = false; + + for (; chunk_index <= last_written_chunk_index; ++chunk_index) { + auto chunk_bytes = m_chunks[chunk_index].bytes(); + size_t chunk_offset = 0; + if (chunk_index == last_written_chunk_index) { + chunk_bytes = chunk_bytes.slice(0, last_written_chunk_offset); + } + if (chunk_index == first_chunk_index) { + chunk_bytes = chunk_bytes.slice(first_chunk_offset); + chunk_offset = first_chunk_offset; + } + + // See if 'value' is in this chunk, + auto position = AK::memmem(chunk_bytes.data(), chunk_bytes.size(), value.data(), value.size()); + if (!position) + continue; // Not in this chunk either :( + + // We found it! + found_value = true; + last_chunk_offset = (const u8*)position - chunk_bytes.data() + chunk_offset; + break; + } + + if (found_value) { + if (first_chunk_index == chunk_index) + return last_chunk_offset - first_chunk_offset; + + return (chunk_index - first_chunk_index) * chunk_size + last_chunk_offset - first_chunk_offset; + } + + // No dice. + return {}; + } + + size_t read(Bytes bytes) override + { + size_t nread = 0; + while (bytes.size() - nread > 0 && m_write_offset - m_read_offset - nread > 0) { + const auto chunk_index = (m_read_offset - m_base_offset) / chunk_size; + const auto chunk_bytes = m_chunks[chunk_index].bytes().slice(m_read_offset % chunk_size).trim(m_write_offset - m_read_offset - nread); + nread += chunk_bytes.copy_trimmed_to(bytes.slice(nread)); + } + + m_read_offset += nread; + + try_discard_chunks(); + + return nread; + } + + bool read_or_error(Bytes bytes) override + { + if (m_write_offset - m_read_offset < bytes.size()) { + set_recoverable_error(); + return false; + } + + read(bytes); + return true; + } + + size_t write(ReadonlyBytes bytes) override + { + size_t nwritten = 0; + while (bytes.size() - nwritten > 0) { + if ((m_write_offset + nwritten) % chunk_size == 0) + m_chunks.append(ByteBuffer::create_uninitialized(chunk_size)); + + nwritten += bytes.copy_trimmed_to(m_chunks.last().bytes().slice(m_write_offset % chunk_size)); + } + + m_write_offset += nwritten; + return nwritten; + } + + bool write_or_error(ReadonlyBytes bytes) override + { + write(bytes); + return true; + } + + size_t roffset() const { return m_read_offset; } + size_t woffset() const { return m_write_offset; } + + size_t remaining() const { return m_write_offset - m_read_offset; } + +private: + void try_discard_chunks() + { + while (m_read_offset - m_base_offset >= chunk_size) { + m_chunks.take_first(); + m_base_offset += chunk_size; + } + } + + Vector<ByteBuffer> m_chunks; + size_t m_write_offset { 0 }; + size_t m_read_offset { 0 }; + size_t m_base_offset { 0 }; +}; + +} + +using AK::DuplexMemoryStream; +using AK::InputMemoryStream; +using AK::InputStream; diff --git a/AK/Stream.h b/AK/Stream.h index 12adc83b09..c8fd053822 100644 --- a/AK/Stream.h +++ b/AK/Stream.h @@ -26,15 +26,12 @@ #pragma once -#include <AK/ByteBuffer.h> #include <AK/Concepts.h> #include <AK/Endian.h> #include <AK/Forward.h> -#include <AK/MemMem.h> #include <AK/Optional.h> #include <AK/Span.h> #include <AK/StdLibExtras.h> -#include <AK/Vector.h> namespace AK::Detail { @@ -116,6 +113,7 @@ OutputStream& operator<<(OutputStream& stream, LittleEndian<T> value) { return stream << ReadonlyBytes { &value.m_value, sizeof(value.m_value) }; } + template<typename T> InputStream& operator>>(InputStream& stream, BigEndian<T>& value) { @@ -136,22 +134,13 @@ InputStream& operator>>(InputStream& stream, Optional<T>& value) return stream; } -#if defined(__cpp_concepts) && !defined(__COVERITY__) -template<Concepts::Integral Integral> -#else template<typename Integral, typename EnableIf<IsIntegral<Integral>::value, int>::Type = 0> -#endif InputStream& operator>>(InputStream& stream, Integral& value) { stream.read_or_error({ &value, sizeof(value) }); return stream; } - -#if defined(__cpp_concepts) && !defined(__COVERITY__) -template<Concepts::Integral Integral> -#else template<typename Integral, typename EnableIf<IsIntegral<Integral>::value, int>::Type = 0> -#endif OutputStream& operator<<(OutputStream& stream, Integral value) { stream.write_or_error({ &value, sizeof(value) }); @@ -160,24 +149,13 @@ OutputStream& operator<<(OutputStream& stream, Integral value) #ifndef KERNEL -// FIXME: clang-format adds spaces before the #if for some reason. -// clang-format off -#if defined(__cpp_concepts) && !defined(__COVERITY__) -template<Concepts::FloatingPoint FloatingPoint> -#else template<typename FloatingPoint, typename EnableIf<IsFloatingPoint<FloatingPoint>::value, int>::Type = 0> -#endif InputStream& operator>>(InputStream& stream, FloatingPoint& value) { stream.read_or_error({ &value, sizeof(value) }); return stream; } - -#if defined(__cpp_concepts) && !defined(__COVERITY__) -template<Concepts::FloatingPoint FloatingPoint> -#else template<typename FloatingPoint, typename EnableIf<IsFloatingPoint<FloatingPoint>::value, int>::Type = 0> -#endif OutputStream& operator<<(OutputStream& stream, FloatingPoint value) { stream.write_or_error({ &value, sizeof(value) }); @@ -185,281 +163,16 @@ OutputStream& operator<<(OutputStream& stream, FloatingPoint value) } #endif -// clang-format on inline InputStream& operator>>(InputStream& stream, bool& value) { stream.read_or_error({ &value, sizeof(value) }); return stream; } - inline OutputStream& operator<<(OutputStream& stream, bool value) { stream.write_or_error({ &value, sizeof(value) }); return stream; } -class InputMemoryStream final : public InputStream { -public: - InputMemoryStream(ReadonlyBytes bytes) - : m_bytes(bytes) - { - } - - bool eof() const override { return m_offset >= m_bytes.size(); } - - size_t read(Bytes bytes) override - { - const auto count = min(bytes.size(), remaining()); - __builtin_memcpy(bytes.data(), m_bytes.data() + m_offset, count); - m_offset += count; - return count; - } - - bool read_or_error(Bytes bytes) override - { - if (remaining() < bytes.size()) { - set_recoverable_error(); - return false; - } - - __builtin_memcpy(bytes.data(), m_bytes.data() + m_offset, bytes.size()); - m_offset += bytes.size(); - return true; - } - - bool discard_or_error(size_t count) override - { - if (remaining() < count) { - set_recoverable_error(); - return false; - } - - m_offset += count; - return true; - } - - void seek(size_t offset) - { - ASSERT(offset < m_bytes.size()); - m_offset = offset; - } - - u8 peek_or_error() const - { - if (remaining() == 0) { - set_recoverable_error(); - return 0; - } - - return m_bytes[m_offset]; - } - - // LEB128 is a variable-length encoding for integers - bool read_LEB128_unsigned(size_t& result) - { - const auto backup = m_offset; - - result = 0; - size_t num_bytes = 0; - while (true) { - // Note. The implementation in AK::BufferStream::read_LEB128_unsigned read one - // past the end, this is fixed here. - if (eof()) { - m_offset = backup; - set_recoverable_error(); - return false; - } - - const u8 byte = m_bytes[m_offset]; - result = (result) | (static_cast<size_t>(byte & ~(1 << 7)) << (num_bytes * 7)); - ++m_offset; - if (!(byte & (1 << 7))) - break; - ++num_bytes; - } - - return true; - } - - // LEB128 is a variable-length encoding for integers - bool read_LEB128_signed(ssize_t& result) - { - const auto backup = m_offset; - - result = 0; - size_t num_bytes = 0; - u8 byte = 0; - - do { - // Note. The implementation in AK::BufferStream::read_LEB128_unsigned read one - // past the end, this is fixed here. - if (eof()) { - m_offset = backup; - set_recoverable_error(); - return false; - } - - byte = m_bytes[m_offset]; - result = (result) | (static_cast<size_t>(byte & ~(1 << 7)) << (num_bytes * 7)); - ++m_offset; - ++num_bytes; - } while (byte & (1 << 7)); - - if (num_bytes * 7 < sizeof(size_t) * 4 && (byte & 0x40)) { - // sign extend - result |= ((size_t)(-1) << (num_bytes * 7)); - } - - return true; - } - - ReadonlyBytes bytes() const { return m_bytes; } - size_t offset() const { return m_offset; } - size_t remaining() const { return m_bytes.size() - m_offset; } - -private: - ReadonlyBytes m_bytes; - size_t m_offset { 0 }; -}; - -// All data written to this stream can be read from it. Reading and writing is done -// using different offsets, meaning that it is not necessary to seek to the start -// before reading; this behaviour differs from BufferStream. -class DuplexMemoryStream final : public DuplexStream { -public: - static constexpr size_t chunk_size = 4 * 1024; - - bool eof() const override { return m_write_offset == m_read_offset; } - - bool discard_or_error(size_t count) override - { - if (m_write_offset - m_read_offset < count) { - set_recoverable_error(); - return false; - } - - m_read_offset += count; - try_discard_chunks(); - return true; - } - - Optional<size_t> offset_of(ReadonlyBytes value) const - { - if (value.size() > remaining()) - return {}; - - // First, find which chunk we're in. - auto chunk_index = (m_read_offset - m_base_offset) / chunk_size; - auto last_written_chunk_index = (m_write_offset - m_base_offset) / chunk_size; - auto first_chunk_index = chunk_index; - auto last_written_chunk_offset = m_write_offset % chunk_size; - auto first_chunk_offset = m_read_offset % chunk_size; - size_t last_chunk_offset = 0; - auto found_value = false; - - for (; chunk_index <= last_written_chunk_index; ++chunk_index) { - auto chunk_bytes = m_chunks[chunk_index].bytes(); - size_t chunk_offset = 0; - if (chunk_index == last_written_chunk_index) { - chunk_bytes = chunk_bytes.slice(0, last_written_chunk_offset); - } - if (chunk_index == first_chunk_index) { - chunk_bytes = chunk_bytes.slice(first_chunk_offset); - chunk_offset = first_chunk_offset; - } - - // See if 'value' is in this chunk, - auto position = AK::memmem(chunk_bytes.data(), chunk_bytes.size(), value.data(), value.size()); - if (!position) - continue; // Not in this chunk either :( - - // We found it! - found_value = true; - last_chunk_offset = (const u8*)position - chunk_bytes.data() + chunk_offset; - break; - } - - if (found_value) { - if (first_chunk_index == chunk_index) - return last_chunk_offset - first_chunk_offset; - - return (chunk_index - first_chunk_index) * chunk_size + last_chunk_offset - first_chunk_offset; - } - - // No dice. - return {}; - } - - size_t read(Bytes bytes) override - { - size_t nread = 0; - while (bytes.size() - nread > 0 && m_write_offset - m_read_offset - nread > 0) { - const auto chunk_index = (m_read_offset - m_base_offset) / chunk_size; - const auto chunk_bytes = m_chunks[chunk_index].bytes().slice(m_read_offset % chunk_size).trim(m_write_offset - m_read_offset - nread); - nread += chunk_bytes.copy_trimmed_to(bytes.slice(nread)); - } - - m_read_offset += nread; - - try_discard_chunks(); - - return nread; - } - - bool read_or_error(Bytes bytes) override - { - if (m_write_offset - m_read_offset < bytes.size()) { - set_recoverable_error(); - return false; - } - - read(bytes); - return true; - } - - size_t write(ReadonlyBytes bytes) override - { - size_t nwritten = 0; - while (bytes.size() - nwritten > 0) { - if ((m_write_offset + nwritten) % chunk_size == 0) - m_chunks.append(ByteBuffer::create_uninitialized(chunk_size)); - - nwritten += bytes.copy_trimmed_to(m_chunks.last().bytes().slice(m_write_offset % chunk_size)); - } - - m_write_offset += nwritten; - return nwritten; - } - - bool write_or_error(ReadonlyBytes bytes) override - { - write(bytes); - return true; - } - - size_t roffset() const { return m_read_offset; } - size_t woffset() const { return m_write_offset; } - - size_t remaining() const { return m_write_offset - m_read_offset; } - -private: - void try_discard_chunks() - { - while (m_read_offset - m_base_offset >= chunk_size) { - m_chunks.take_first(); - m_base_offset += chunk_size; - } - } - - Vector<ByteBuffer> m_chunks; - size_t m_write_offset { 0 }; - size_t m_read_offset { 0 }; - size_t m_base_offset { 0 }; -}; - } - -using AK::DuplexMemoryStream; -using AK::InputMemoryStream; -using AK::InputStream; diff --git a/AK/Tests/TestStream.cpp b/AK/Tests/TestMemoryStream.cpp index d10d45bc5c..611b24ef60 100644 --- a/AK/Tests/TestStream.cpp +++ b/AK/Tests/TestMemoryStream.cpp @@ -27,7 +27,7 @@ #include <AK/TestSuite.h> #include <AK/FixedArray.h> -#include <AK/Stream.h> +#include <AK/MemoryStream.h> static bool compare(ReadonlyBytes lhs, ReadonlyBytes rhs) { @@ -160,4 +160,4 @@ TEST_CASE(read_endian_values) EXPECT_EQ(value2, 0x04050607u); } -TEST_MAIN(Stream) +TEST_MAIN(MemoryStream) |