diff --git a/AK/MemoryStream.cpp b/AK/MemoryStream.cpp index ad4967f370..880d8027c9 100644 --- a/AK/MemoryStream.cpp +++ b/AK/MemoryStream.cpp @@ -81,10 +81,10 @@ ErrorOr FixedMemoryStream::seek(i64 offset, SeekMode seek_mode) m_offset += offset; break; case SeekMode::FromEndPosition: - if (offset > static_cast(m_bytes.size())) + if (-offset > static_cast(m_bytes.size())) return Error::from_string_view_or_print_error_and_return_errno("Offset past the start of the stream memory"sv, EINVAL); - m_offset = m_bytes.size() - offset; + m_offset = m_bytes.size() + offset; break; } return m_offset; diff --git a/Tests/AK/TestMemoryStream.cpp b/Tests/AK/TestMemoryStream.cpp index 8d7e5ae42f..a945bb3cbb 100644 --- a/Tests/AK/TestMemoryStream.cpp +++ b/Tests/AK/TestMemoryStream.cpp @@ -151,3 +151,99 @@ TEST_CASE(allocating_memory_stream_offset_of_with_write_offset_multiple_of_chunk EXPECT_EQ(offset.value(), AllocatingMemoryStream::CHUNK_SIZE - 32 - 1); } } + +TEST_CASE(fixed_memory_read_write) +{ + constexpr auto some_words = "These are some words"sv; + + auto empty = TRY_OR_FAIL(ByteBuffer::create_uninitialized(some_words.length())); + FixedMemoryStream stream { empty.bytes() }; + + ReadonlyBytes buffer { some_words.characters_without_null_termination(), some_words.length() }; + TRY_OR_FAIL(stream.write_some(buffer)); + + EXPECT_EQ(TRY_OR_FAIL(stream.tell()), some_words.length()); + EXPECT(stream.is_eof()); + + TRY_OR_FAIL(stream.seek(0)); + auto contents = TRY_OR_FAIL(stream.read_until_eof()); + EXPECT_EQ(contents.bytes(), some_words.bytes()); +} + +TEST_CASE(fixed_memory_close) +{ + auto empty = TRY_OR_FAIL(ByteBuffer::create_uninitialized(64)); + FixedMemoryStream stream { empty.bytes() }; + EXPECT(stream.is_open()); + stream.close(); + EXPECT(stream.is_open()); +} + +TEST_CASE(fixed_memory_read_only) +{ + constexpr auto some_words = "These are some words"sv; + + FixedMemoryStream stream { ReadonlyBytes { some_words.bytes() } }; + + auto contents = TRY_OR_FAIL(stream.read_until_eof()); + EXPECT_EQ(contents.bytes(), some_words.bytes()); + + TRY_OR_FAIL(stream.seek(0)); + ReadonlyBytes buffer { some_words.characters_without_null_termination(), some_words.length() }; + EXPECT_CRASH("Write protection assert", [&] { + (void)stream.write_some(buffer); + return Test::Crash::Failure::DidNotCrash; + }); + + EXPECT_EQ(TRY_OR_FAIL(stream.tell()), 0ull); + EXPECT(!stream.is_eof()); +} + +TEST_CASE(fixed_memory_seeking_around) +{ + auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(8702ul)); + FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } }; + + auto buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(16)); + + TRY_OR_FAIL(stream.seek(500, SeekMode::SetPosition)); + EXPECT_EQ(stream.tell().release_value(), 500ul); + TRY_OR_FAIL(stream.read_until_filled(buffer)); + + TRY_OR_FAIL(stream.seek(234, SeekMode::FromCurrentPosition)); + EXPECT_EQ(stream.tell().release_value(), 750ul); + TRY_OR_FAIL(stream.read_until_filled(buffer)); + + TRY_OR_FAIL(stream.seek(-105, SeekMode::FromEndPosition)); + EXPECT_EQ(stream.tell().release_value(), 8597ul); + TRY_OR_FAIL(stream.read_until_filled(buffer)); +} + +BENCHMARK_CASE(fixed_memory_tell) +{ + auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(10 * KiB)); + FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } }; + + auto expected_fixed_memory_offset = 0u; + auto ten_byte_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(1)); + for (auto i = 0u; i < 4000; ++i) { + TRY_OR_FAIL(stream.read_until_filled(ten_byte_buffer)); + expected_fixed_memory_offset += 1u; + EXPECT_EQ(expected_fixed_memory_offset, TRY_OR_FAIL(stream.tell())); + } + + for (auto i = 0u; i < 4000; ++i) { + auto seek_fixed_memory_offset = TRY_OR_FAIL(stream.seek(-1, SeekMode::FromCurrentPosition)); + expected_fixed_memory_offset -= 1; + EXPECT_EQ(seek_fixed_memory_offset, TRY_OR_FAIL(stream.tell())); + EXPECT_EQ(expected_fixed_memory_offset, TRY_OR_FAIL(stream.tell())); + } +} + +TEST_CASE(fixed_memory_truncate) +{ + auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(10 * KiB)); + FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } }; + + EXPECT(stream.truncate(999).is_error()); +}