TestMemoryStream.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*
  2. * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/MemoryStream.h>
  7. #include <AK/String.h>
  8. #include <LibTest/TestCase.h>
  9. TEST_CASE(allocating_memory_stream_empty)
  10. {
  11. AllocatingMemoryStream stream;
  12. EXPECT_EQ(stream.used_buffer_size(), 0ul);
  13. {
  14. Array<u8, 32> array;
  15. auto read_bytes = MUST(stream.read_some(array));
  16. EXPECT_EQ(read_bytes.size(), 0ul);
  17. }
  18. {
  19. auto offset = MUST(stream.offset_of("test"sv.bytes()));
  20. EXPECT(!offset.has_value());
  21. }
  22. }
  23. TEST_CASE(allocating_memory_stream_offset_of)
  24. {
  25. AllocatingMemoryStream stream;
  26. MUST(stream.write_until_depleted("Well Hello Friends! :^)"sv.bytes()));
  27. {
  28. auto offset = MUST(stream.offset_of(" "sv.bytes()));
  29. EXPECT(offset.has_value());
  30. EXPECT_EQ(offset.value(), 4ul);
  31. }
  32. {
  33. auto offset = MUST(stream.offset_of("W"sv.bytes()));
  34. EXPECT(offset.has_value());
  35. EXPECT_EQ(offset.value(), 0ul);
  36. }
  37. {
  38. auto offset = MUST(stream.offset_of(")"sv.bytes()));
  39. EXPECT(offset.has_value());
  40. EXPECT_EQ(offset.value(), 22ul);
  41. }
  42. {
  43. auto offset = MUST(stream.offset_of("-"sv.bytes()));
  44. EXPECT(!offset.has_value());
  45. }
  46. MUST(stream.discard(1));
  47. {
  48. auto offset = MUST(stream.offset_of("W"sv.bytes()));
  49. EXPECT(!offset.has_value());
  50. }
  51. {
  52. auto offset = MUST(stream.offset_of("e"sv.bytes()));
  53. EXPECT(offset.has_value());
  54. EXPECT_EQ(offset.value(), 0ul);
  55. }
  56. }
  57. TEST_CASE(allocating_memory_stream_offset_of_oob)
  58. {
  59. AllocatingMemoryStream stream;
  60. // NOTE: This test is to make sure that offset_of() doesn't read past the end of the "initialized" data.
  61. // So we have to assume some things about the behavior of this class:
  62. // - A chunk is moved to the end when it's fully read from
  63. // - A free chunk is used as-is, no new ones are allocated if one exists.
  64. // First, fill exactly one chunk (in groups of 16 bytes).
  65. for (size_t i = 0; i < AllocatingMemoryStream::CHUNK_SIZE / 16; ++i)
  66. MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
  67. // Then discard it all.
  68. MUST(stream.discard(AllocatingMemoryStream::CHUNK_SIZE));
  69. // Now we can write into this chunk again, knowing that it's initialized to all 'A's.
  70. MUST(stream.write_until_depleted("Well Hello Friends! :^)"sv.bytes()));
  71. {
  72. auto offset = MUST(stream.offset_of("A"sv.bytes()));
  73. EXPECT(!offset.has_value());
  74. }
  75. }
  76. TEST_CASE(allocating_memory_stream_offset_of_after_chunk_reorder)
  77. {
  78. AllocatingMemoryStream stream;
  79. // First, fill exactly one chunk (in groups of 16 bytes). This chunk will be reordered.
  80. for (size_t i = 0; i < AllocatingMemoryStream::CHUNK_SIZE / 16; ++i)
  81. MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
  82. // Append a few additional bytes to create a second chunk.
  83. MUST(stream.write_until_depleted("BCDEFGHIJKLMNOPQ"sv.bytes()));
  84. // Read back the first chunk, which should reorder it to the end of the list.
  85. // The chunk that we wrote to the second time is now the first one.
  86. MUST(stream.discard(AllocatingMemoryStream::CHUNK_SIZE));
  87. {
  88. auto offset = MUST(stream.offset_of("A"sv.bytes()));
  89. EXPECT(!offset.has_value());
  90. }
  91. {
  92. auto offset = MUST(stream.offset_of("B"sv.bytes()));
  93. EXPECT(offset.has_value());
  94. EXPECT_EQ(offset.value(), 0ul);
  95. }
  96. {
  97. auto offset = MUST(stream.offset_of("Q"sv.bytes()));
  98. EXPECT(offset.has_value());
  99. EXPECT_EQ(offset.value(), 15ul);
  100. }
  101. }
  102. TEST_CASE(allocating_memory_stream_offset_of_with_write_offset_multiple_of_chunk_size)
  103. {
  104. // This tests a specific edge case where we would erroneously trim the last searched block
  105. // to size 0 if the current write offset is a multiple of the chunk size.
  106. AllocatingMemoryStream stream;
  107. // First, fill exactly one chunk (in groups of 16 bytes).
  108. for (size_t i = 0; i < (AllocatingMemoryStream::CHUNK_SIZE / 16) - 1; ++i)
  109. MUST(stream.write_until_depleted("AAAAAAAAAAAAAAAA"sv.bytes()));
  110. MUST(stream.write_until_depleted("BCDEFGHIJKLMNOPQ"sv.bytes()));
  111. // Read a few bytes from the beginning to ensure that we are trying to slice into the zero-sized block.
  112. MUST(stream.discard(32));
  113. {
  114. auto offset = MUST(stream.offset_of("B"sv.bytes()));
  115. EXPECT(offset.has_value());
  116. EXPECT_EQ(offset.value(), AllocatingMemoryStream::CHUNK_SIZE - 32 - 16);
  117. }
  118. {
  119. auto offset = MUST(stream.offset_of("Q"sv.bytes()));
  120. EXPECT(offset.has_value());
  121. EXPECT_EQ(offset.value(), AllocatingMemoryStream::CHUNK_SIZE - 32 - 1);
  122. }
  123. }
  124. TEST_CASE(fixed_memory_read_write)
  125. {
  126. constexpr auto some_words = "These are some words"sv;
  127. auto empty = TRY_OR_FAIL(ByteBuffer::create_uninitialized(some_words.length()));
  128. FixedMemoryStream stream { empty.bytes() };
  129. ReadonlyBytes buffer { some_words.characters_without_null_termination(), some_words.length() };
  130. TRY_OR_FAIL(stream.write_some(buffer));
  131. EXPECT_EQ(TRY_OR_FAIL(stream.tell()), some_words.length());
  132. EXPECT(stream.is_eof());
  133. TRY_OR_FAIL(stream.seek(0));
  134. auto contents = TRY_OR_FAIL(stream.read_until_eof());
  135. EXPECT_EQ(contents.bytes(), some_words.bytes());
  136. }
  137. TEST_CASE(fixed_memory_close)
  138. {
  139. auto empty = TRY_OR_FAIL(ByteBuffer::create_uninitialized(64));
  140. FixedMemoryStream stream { empty.bytes() };
  141. EXPECT(stream.is_open());
  142. stream.close();
  143. EXPECT(stream.is_open());
  144. }
  145. TEST_CASE(fixed_memory_read_only)
  146. {
  147. constexpr auto some_words = "These are some words"sv;
  148. FixedMemoryStream stream { ReadonlyBytes { some_words.bytes() } };
  149. auto contents = TRY_OR_FAIL(stream.read_until_eof());
  150. EXPECT_EQ(contents.bytes(), some_words.bytes());
  151. TRY_OR_FAIL(stream.seek(0));
  152. ReadonlyBytes buffer { some_words.characters_without_null_termination(), some_words.length() };
  153. EXPECT(stream.write_some(buffer).is_error());
  154. EXPECT_EQ(TRY_OR_FAIL(stream.tell()), 0ull);
  155. EXPECT(!stream.is_eof());
  156. }
  157. TEST_CASE(fixed_memory_seeking_around)
  158. {
  159. auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(8702ul));
  160. FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } };
  161. auto buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(16));
  162. TRY_OR_FAIL(stream.seek(500, SeekMode::SetPosition));
  163. EXPECT_EQ(stream.tell().release_value(), 500ul);
  164. TRY_OR_FAIL(stream.read_until_filled(buffer));
  165. TRY_OR_FAIL(stream.seek(234, SeekMode::FromCurrentPosition));
  166. EXPECT_EQ(stream.tell().release_value(), 750ul);
  167. TRY_OR_FAIL(stream.read_until_filled(buffer));
  168. TRY_OR_FAIL(stream.seek(-105, SeekMode::FromEndPosition));
  169. EXPECT_EQ(stream.tell().release_value(), 8597ul);
  170. TRY_OR_FAIL(stream.read_until_filled(buffer));
  171. }
  172. BENCHMARK_CASE(fixed_memory_tell)
  173. {
  174. auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(10 * KiB));
  175. FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } };
  176. auto expected_fixed_memory_offset = 0u;
  177. auto ten_byte_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(1));
  178. for (auto i = 0u; i < 4000; ++i) {
  179. TRY_OR_FAIL(stream.read_until_filled(ten_byte_buffer));
  180. expected_fixed_memory_offset += 1u;
  181. EXPECT_EQ(expected_fixed_memory_offset, TRY_OR_FAIL(stream.tell()));
  182. }
  183. for (auto i = 0u; i < 4000; ++i) {
  184. auto seek_fixed_memory_offset = TRY_OR_FAIL(stream.seek(-1, SeekMode::FromCurrentPosition));
  185. expected_fixed_memory_offset -= 1;
  186. EXPECT_EQ(seek_fixed_memory_offset, TRY_OR_FAIL(stream.tell()));
  187. EXPECT_EQ(expected_fixed_memory_offset, TRY_OR_FAIL(stream.tell()));
  188. }
  189. }
  190. TEST_CASE(fixed_memory_truncate)
  191. {
  192. auto stream_buffer = TRY_OR_FAIL(ByteBuffer::create_uninitialized(10 * KiB));
  193. FixedMemoryStream stream { ReadonlyBytes { stream_buffer.bytes() } };
  194. EXPECT(stream.truncate(999).is_error());
  195. }
  196. TEST_CASE(fixed_memory_read_in_place)
  197. {
  198. constexpr auto some_words = "These are some words"sv;
  199. FixedMemoryStream readonly_stream { ReadonlyBytes { some_words.bytes() } };
  200. // Trying to read mutable values from a read-only stream should fail.
  201. EXPECT(readonly_stream.read_in_place<u8>(1).is_error());
  202. EXPECT_EQ(readonly_stream.offset(), 0u);
  203. // Reading const values should succeed.
  204. auto characters = TRY_OR_FAIL(readonly_stream.read_in_place<u8 const>(20));
  205. EXPECT_EQ(characters, some_words.bytes());
  206. EXPECT(readonly_stream.is_eof());
  207. FixedMemoryStream mutable_stream { Bytes { const_cast<u8*>(some_words.bytes().data()), some_words.bytes().size() }, FixedMemoryStream::Mode::ReadWrite };
  208. // Trying to read mutable values from a mutable stream should succeed.
  209. TRY_OR_FAIL(mutable_stream.read_in_place<u8>(1));
  210. EXPECT_EQ(mutable_stream.offset(), 1u);
  211. TRY_OR_FAIL(mutable_stream.seek(0));
  212. // Reading const values should succeed.
  213. auto characters_again = TRY_OR_FAIL(mutable_stream.read_in_place<u8 const>(20));
  214. EXPECT_EQ(characters_again, some_words.bytes());
  215. EXPECT(mutable_stream.is_eof());
  216. }