/src/simdutf/fuzz/safe_conversion.cpp
Line | Count | Source |
1 | | #include <algorithm> |
2 | | #include <cassert> |
3 | | #include <cstdlib> |
4 | | #include <type_traits> |
5 | | #include <vector> |
6 | | |
7 | | #include "simdutf.h" |
8 | | |
9 | | void test_latin1_to_utf8(std::span<const uint8_t> input_bytes, |
10 | 730 | std::size_t output_size) { |
11 | 730 | std::vector<char> output(output_size); |
12 | 730 | const auto written_bytes_safe = |
13 | 730 | simdutf::convert_latin1_to_utf8_safe(input_bytes, output); |
14 | 730 | if (written_bytes_safe > output_size) { |
15 | 0 | std::abort(); |
16 | 0 | } |
17 | 730 | const auto needed_size = simdutf::utf8_length_from_latin1(input_bytes); |
18 | 730 | std::vector<char> reference(needed_size); |
19 | 730 | const auto written_bytes_unsafe = |
20 | 730 | simdutf::convert_latin1_to_utf8(input_bytes, reference); |
21 | 730 | if (written_bytes_unsafe != needed_size) { |
22 | 0 | std::abort(); |
23 | 0 | } |
24 | 730 | if (written_bytes_safe > needed_size) { |
25 | | // convert_latin1_to_utf8_safe wrote more output buffer than the unsafe |
26 | | // version needed! |
27 | 0 | std::abort(); |
28 | 0 | } |
29 | | // ensure output is equal to the beginning of reference |
30 | 730 | if (!std::ranges::equal( |
31 | 730 | std::span(output).subspan(0, written_bytes_safe), |
32 | 730 | std::span(reference).subspan(0, written_bytes_safe))) { |
33 | 0 | std::abort(); |
34 | 0 | } |
35 | 730 | } |
36 | | |
37 | | void test_utf16_to_utf8(std::span<const char16_t> input, |
38 | 1.01k | std::size_t output_size) { |
39 | 1.01k | std::vector<char> output(output_size); |
40 | 1.01k | const auto written_bytes_safe = |
41 | 1.01k | simdutf::convert_utf16_to_utf8_safe(input, output); |
42 | 1.01k | if (written_bytes_safe > output_size) { |
43 | 0 | std::abort(); |
44 | 0 | } |
45 | | // result is implementation defined in case of garbage input |
46 | 1.01k | const auto unreliable_needed_size = simdutf::utf8_length_from_utf16(input); |
47 | 1.01k | std::vector<char> reference(unreliable_needed_size); |
48 | 1.01k | const auto written_bytes_unsafe = |
49 | 1.01k | simdutf::convert_utf16_to_utf8(input, reference); |
50 | | |
51 | | // ensure output is equal to the beginning of reference |
52 | 1.01k | const auto Ncompare = |
53 | 1.01k | simdutf::detail::min(written_bytes_safe, written_bytes_unsafe); |
54 | 1.01k | const auto matches = |
55 | 1.01k | std::ranges::equal(std::span(output).subspan(0, Ncompare), |
56 | 1.01k | std::span(reference).subspan(0, Ncompare)); |
57 | 1.01k | assert(matches); |
58 | 1.01k | if (!matches) { |
59 | 0 | std::abort(); |
60 | 0 | } |
61 | 1.01k | } |
62 | | |
63 | 1.74k | void select_implementation(auto index) { |
64 | 1.74k | static const auto implementations = []() { |
65 | 1 | const auto list = simdutf::get_available_implementations(); |
66 | 1 | using Impl = std::decay_t<decltype(*list.begin())>; |
67 | 1 | std::vector<Impl> ret; |
68 | 4 | for (auto& e : list) { |
69 | 4 | if (e->supported_by_runtime_system()) { |
70 | 3 | ret.push_back(e); |
71 | 3 | } |
72 | 4 | } |
73 | 1 | return ret; |
74 | 1 | }(); |
75 | 1.74k | assert(!implementations.empty()); |
76 | 1.74k | simdutf::get_active_implementation() = |
77 | 1.74k | implementations.at(index % implementations.size()); |
78 | 1.74k | } |
79 | | |
80 | 4.85k | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
81 | | |
82 | 4.85k | if (size < 4) { |
83 | 6 | return 0; |
84 | 6 | } |
85 | | |
86 | 4.84k | const auto action = data[0] & 0x1; |
87 | 4.84k | const auto output_size = (data[1] << 8 | data[2]); |
88 | 4.84k | const auto implementation_index = data[3] & 0b0111; |
89 | 4.84k | data += 4; |
90 | 4.84k | size -= 4; |
91 | | |
92 | 4.84k | const std::span<const uint8_t> input_bytes{data, data + size}; |
93 | | |
94 | 4.84k | select_implementation(implementation_index); |
95 | | |
96 | 4.84k | switch (action) { |
97 | 2.32k | case 0: |
98 | 2.32k | test_latin1_to_utf8(input_bytes, output_size); |
99 | 2.32k | break; |
100 | 2.52k | case 1: { |
101 | 2.52k | const auto* ptr = reinterpret_cast<const char16_t*>(input_bytes.data()); |
102 | 2.52k | test_utf16_to_utf8(std::span(ptr, ptr + input_bytes.size() / 2), |
103 | 2.52k | output_size); |
104 | 2.52k | } break; |
105 | 4.84k | } |
106 | | |
107 | 4.84k | return 0; |
108 | 4.84k | } |