* @brief Validates that `sz::memcpy`, `sz::memset`, and `sz::memmove` work similar to their `std::` counterparts. * * Uses a large heap-allocated buffer to ensure that operations optimized for @b larger-than-L2-cache memory * regions are tested. Covers various chunk sizes, overlapping regions, and both forward and backward traversals. */
| 1255 | * regions are tested. Covers various chunk sizes, overlapping regions, and both forward and backward traversals. |
| 1256 | */ |
| 1257 | void test_memory_utilities(std::size_t max_l2_size = 1024ull * 1024ull) { |
| 1258 | |
| 1259 | // We will be mirroring the operations on both standard and StringZilla strings. |
| 1260 | std::string text_stl(max_l2_size, '-'); |
| 1261 | std::string text_sz(max_l2_size, '-'); |
| 1262 | expect_equality(text_stl.data(), text_sz.data(), max_l2_size); |
| 1263 | |
| 1264 | // The traditional `memset` and `memcpy` functions are undefined for zero-length buffers and NULL pointers |
| 1265 | // for older C standards. However, with the N3322 proposal for C2y, that issue has been resolved. |
| 1266 | // https://developers.redhat.com/articles/2024/12/11/making-memcpynull-null-0-well-defined |
| 1267 | // |
| 1268 | // Let's make sure, that our versions don't trigger any undefined behavior. |
| 1269 | sz::memset(NULL, 0, 0); |
| 1270 | sz::memcpy(NULL, NULL, 0); |
| 1271 | sz::memmove(NULL, NULL, 0); |
| 1272 | |
| 1273 | // First start with simple deterministic tests. |
| 1274 | // Let's use `memset` to fill the strings with a pattern like "122333444455555...00000000000011111111111..." |
| 1275 | std::size_t count_groups = 0; |
| 1276 | for (std::size_t offset = 0, fill_length = 1; offset < max_l2_size; |
| 1277 | offset += fill_length, ++fill_length, ++count_groups) { |
| 1278 | char fill_value = '0' + fill_length % 10; |
| 1279 | fill_length = offset + fill_length > max_l2_size ? max_l2_size - offset : fill_length; |
| 1280 | std::memset((void *)(text_stl.data() + offset), fill_value, fill_length); |
| 1281 | sz::memset((void *)(text_sz.data() + offset), fill_value, fill_length); |
| 1282 | expect_equality(text_stl.data(), text_sz.data(), max_l2_size); |
| 1283 | } |
| 1284 | |
| 1285 | // Let's copy those chunks to an empty buffer one by one, validating the overall equivalency after every copy. |
| 1286 | std::string copy_stl(max_l2_size, '-'); |
| 1287 | std::string copy_sz(max_l2_size, '-'); |
| 1288 | for (std::size_t offset = 0, fill_length = 1; offset < max_l2_size; offset += fill_length, ++fill_length) { |
| 1289 | fill_length = offset + fill_length > max_l2_size ? max_l2_size - offset : fill_length; |
| 1290 | std::memcpy((void *)(copy_stl.data() + offset), (void *)(text_stl.data() + offset), fill_length); |
| 1291 | sz::memcpy((void *)(copy_sz.data() + offset), (void *)(text_sz.data() + offset), fill_length); |
| 1292 | expect_equality(copy_stl.data(), copy_sz.data(), max_l2_size); |
| 1293 | } |
| 1294 | expect_equality(text_stl.data(), copy_stl.data(), max_l2_size); |
| 1295 | expect_equality(text_sz.data(), copy_sz.data(), max_l2_size); |
| 1296 | |
| 1297 | // Let's simulate a realistic `memmove` workloads, compacting parts of this buffer, removing all odd values, |
| 1298 | // so the buffer will look like "224444666666..." |
| 1299 | for (std::size_t offset = 0, fill_length = 1; offset < max_l2_size; offset += fill_length, ++fill_length) { |
| 1300 | if (fill_length % 2 == 0) continue; // Skip even chunks |
| 1301 | if (offset + fill_length >= max_l2_size) break; // This is the last & there are no more even chunks to shift |
| 1302 | |
| 1303 | // Make sure we don't overflow the buffer |
| 1304 | std::size_t next_offset = offset + fill_length; |
| 1305 | std::size_t next_fill_length = fill_length + 1; |
| 1306 | next_fill_length = next_offset + next_fill_length > max_l2_size ? max_l2_size - next_offset : next_fill_length; |
| 1307 | |
| 1308 | std::memmove((void *)(text_stl.data() + offset), (void *)(text_stl.data() + next_offset), next_fill_length); |
| 1309 | sz::memmove((void *)(text_sz.data() + offset), (void *)(text_sz.data() + next_offset), next_fill_length); |
| 1310 | expect_equality(text_stl.data(), text_sz.data(), max_l2_size); |
| 1311 | } |
| 1312 | |
| 1313 | // Now the opposite workload, expanding the buffer, inserting a dash "-" before every group of equal characters. |
| 1314 | // We will need to navigate right-to left to avoid overwriting the groups. |
no test coverage detected
searching dependent graphs…