123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- /*
- * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/MemoryStream.h>
- #include <AK/String.h>
- #include <LibTest/TestCase.h>
- TEST_CASE(allocating_memory_stream_empty)
- {
- AllocatingMemoryStream stream;
- EXPECT_EQ(stream.used_buffer_size(), 0ul);
- {
- Array<u8, 32> array;
- auto read_bytes = MUST(stream.read_some(array));
- EXPECT_EQ(read_bytes.size(), 0ul);
- }
- {
- auto offset = MUST(stream.offset_of("test"sv.bytes()));
- EXPECT(!offset.has_value());
- }
- }
- TEST_CASE(allocating_memory_stream_offset_of)
- {
- AllocatingMemoryStream stream;
- MUST(stream.write_until_depleted("Well Hello Friends! :^)"sv.bytes()));
- {
- auto offset = MUST(stream.offset_of(" "sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 4ul);
- }
- {
- auto offset = MUST(stream.offset_of("W"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 0ul);
- }
- {
- auto offset = MUST(stream.offset_of(")"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 22ul);
- }
- {
- auto offset = MUST(stream.offset_of("-"sv.bytes()));
- EXPECT(!offset.has_value());
- }
- MUST(stream.discard(1));
- {
- auto offset = MUST(stream.offset_of("W"sv.bytes()));
- EXPECT(!offset.has_value());
- }
- {
- auto offset = MUST(stream.offset_of("e"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 0ul);
- }
- }
- TEST_CASE(allocating_memory_stream_offset_of_oob)
- {
- AllocatingMemoryStream stream;
- // NOTE: This test is to make sure that offset_of() doesn't read past the end of the "initialized" data.
- // So we have to assume some things about the behavior of this class:
- // - A chunk is moved to the end when it's fully read from
- // - A free chunk is used as-is, no new ones are allocated if one exists.
- // First, fill exactly one chunk (in groups of 16 bytes).
- for (size_t i = 0; i < AllocatingMemoryStream::CHUNK_SIZE / 16; ++i)
- MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
- // Then discard it all.
- MUST(stream.discard(AllocatingMemoryStream::CHUNK_SIZE));
- // Now we can write into this chunk again, knowing that it's initialized to all 'A's.
- MUST(stream.write_until_depleted("Well Hello Friends! :^)"sv.bytes()));
- {
- auto offset = MUST(stream.offset_of("A"sv.bytes()));
- EXPECT(!offset.has_value());
- }
- }
- TEST_CASE(allocating_memory_stream_offset_of_after_chunk_reorder)
- {
- AllocatingMemoryStream stream;
- // First, fill exactly one chunk (in groups of 16 bytes). This chunk will be reordered.
- for (size_t i = 0; i < AllocatingMemoryStream::CHUNK_SIZE / 16; ++i)
- MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
- // Append a few additional bytes to create a second chunk.
- MUST(stream.write_until_depleted("BCDEFGHIJKLMNOPQ"sv.bytes()));
- // Read back the first chunk, which should reorder it to the end of the list.
- // The chunk that we wrote to the second time is now the first one.
- MUST(stream.discard(AllocatingMemoryStream::CHUNK_SIZE));
- {
- auto offset = MUST(stream.offset_of("A"sv.bytes()));
- EXPECT(!offset.has_value());
- }
- {
- auto offset = MUST(stream.offset_of("B"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 0ul);
- }
- {
- auto offset = MUST(stream.offset_of("Q"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), 15ul);
- }
- }
- TEST_CASE(allocating_memory_stream_offset_of_with_write_offset_multiple_of_chunk_size)
- {
- // This tests a specific edge case where we would erroneously trim the last searched block
- // to size 0 if the current write offset is a multiple of the chunk size.
- AllocatingMemoryStream stream;
- // First, fill exactly one chunk (in groups of 16 bytes).
- for (size_t i = 0; i < (AllocatingMemoryStream::CHUNK_SIZE / 16) - 1; ++i)
- MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
- MUST(stream.write_until_depleted("BCDEFGHIJKLMNOPQ"sv.bytes()));
- // Read a few bytes from the beginning to ensure that we are trying to slice into the zero-sized block.
- MUST(stream.discard(32));
- {
- auto offset = MUST(stream.offset_of("B"sv.bytes()));
- EXPECT(offset.has_value());
- EXPECT_EQ(offset.value(), AllocatingMemoryStream::CHUNK_SIZE - 32 - 16);
- }
- {
- auto offset = MUST(stream.offset_of("Q"sv.bytes()));
- EXPECT(offset.has_value());
- 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(stream.write_some(buffer).is_error());
- 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());
- }
- TEST_CASE(fixed_memory_read_in_place)
- {
- constexpr auto some_words = "These are some words"sv;
- FixedMemoryStream readonly_stream { ReadonlyBytes { some_words.bytes() } };
- // Trying to read mutable values from a read-only stream should fail.
- EXPECT(readonly_stream.read_in_place<u8>(1).is_error());
- EXPECT_EQ(readonly_stream.offset(), 0u);
- // Reading const values should succeed.
- auto characters = TRY_OR_FAIL(readonly_stream.read_in_place<u8 const>(20));
- EXPECT_EQ(characters, some_words.bytes());
- EXPECT(readonly_stream.is_eof());
- FixedMemoryStream mutable_stream { Bytes { const_cast<u8*>(some_words.bytes().data()), some_words.bytes().size() }, FixedMemoryStream::Mode::ReadWrite };
- // Trying to read mutable values from a mutable stream should succeed.
- TRY_OR_FAIL(mutable_stream.read_in_place<u8>(1));
- EXPECT_EQ(mutable_stream.offset(), 1u);
- TRY_OR_FAIL(mutable_stream.seek(0));
- // Reading const values should succeed.
- auto characters_again = TRY_OR_FAIL(mutable_stream.read_in_place<u8 const>(20));
- EXPECT_EQ(characters_again, some_words.bytes());
- EXPECT(mutable_stream.is_eof());
- }
|