/src/llvm-project/libcxx/test/libcxx/fuzzing/fuzz.h
Line | Count | Source |
1 | | //===----------------------------------------------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #ifndef TEST_LIBCXX_FUZZING_FUZZ_H |
10 | | #define TEST_LIBCXX_FUZZING_FUZZ_H |
11 | | |
12 | | #include <cassert> |
13 | | #include <cstddef> |
14 | | #include <cstdint> |
15 | | #include <cstring> // std::strlen |
16 | | #include <iterator> |
17 | | #include <type_traits> |
18 | | #include <utility> // std::swap |
19 | | |
20 | | |
21 | | // This is a struct we can use to test the stable_XXX algorithms. |
22 | | // Perform the operation on the key, then check the order of the payload. |
23 | | struct ByteWithPayload { |
24 | | std::uint8_t key; |
25 | | std::size_t payload; |
26 | | |
27 | 0 | ByteWithPayload(std::uint8_t k) : key(k), payload(0) { } |
28 | 51.5M | ByteWithPayload(std::uint8_t k, std::size_t p) : key(k), payload(p) { } |
29 | | |
30 | 0 | friend bool operator==(ByteWithPayload const& x, ByteWithPayload const& y) { |
31 | 0 | return x.key == y.key && x.payload == y.payload; |
32 | 0 | } |
33 | | |
34 | 0 | friend bool operator!=(ByteWithPayload const& x, ByteWithPayload const& y) { |
35 | 0 | return !(x == y); |
36 | 0 | } |
37 | | |
38 | | struct key_less { |
39 | | bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const |
40 | 511M | { return x.key < y.key; } |
41 | | }; |
42 | | |
43 | | struct payload_less { |
44 | | bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const |
45 | 22.0M | { return x.payload < y.payload; } |
46 | | }; |
47 | | |
48 | | struct total_less { |
49 | 29.5M | bool operator()(ByteWithPayload const& x, ByteWithPayload const& y) const { |
50 | 29.5M | return x.key == y.key ? x.payload < y.payload : x.key < y.key; |
51 | 29.5M | } |
52 | | }; |
53 | | |
54 | 11 | friend void swap(ByteWithPayload& lhs, ByteWithPayload& rhs) { |
55 | 11 | std::swap(lhs.key, rhs.key); |
56 | 11 | std::swap(lhs.payload, rhs.payload); |
57 | 11 | } |
58 | | }; |
59 | | |
60 | | // Faster version of std::is_permutation |
61 | | // |
62 | | // Builds a set of buckets for each of the key values, and sums all the payloads. |
63 | | // Not 100% perfect, but _way_ faster. |
64 | | template <typename Iter1, typename Iter2, typename = typename std::enable_if< |
65 | | std::is_same<typename std::iterator_traits<Iter1>::value_type, ByteWithPayload>::value && |
66 | | std::is_same<typename std::iterator_traits<Iter2>::value_type, ByteWithPayload>::value |
67 | | >::type> |
68 | 468 | bool fast_is_permutation(Iter1 first1, Iter1 last1, Iter2 first2) { |
69 | 468 | std::size_t xBuckets[256] = {0}; |
70 | 468 | std::size_t xPayloads[256] = {0}; |
71 | 468 | std::size_t yBuckets[256] = {0}; |
72 | 468 | std::size_t yPayloads[256] = {0}; |
73 | | |
74 | 51.5M | for (; first1 != last1; ++first1, ++first2) { |
75 | 51.5M | xBuckets[first1->key]++; |
76 | 51.5M | xPayloads[first1->key] += first1->payload; |
77 | | |
78 | 51.5M | yBuckets[first2->key]++; |
79 | 51.5M | yPayloads[first2->key] += first2->payload; |
80 | 51.5M | } |
81 | | |
82 | 120k | for (std::size_t i = 0; i < 256; ++i) { |
83 | 119k | if (xBuckets[i] != yBuckets[i]) |
84 | 0 | return false; |
85 | 119k | if (xPayloads[i] != yPayloads[i]) |
86 | 0 | return false; |
87 | 119k | } |
88 | | |
89 | 468 | return true; |
90 | 468 | } |
91 | | |
92 | | template <typename Iter1, typename Iter2, typename = void, typename = typename std::enable_if< |
93 | | std::is_same<typename std::iterator_traits<Iter1>::value_type, std::uint8_t>::value && |
94 | | std::is_same<typename std::iterator_traits<Iter2>::value_type, std::uint8_t>::value |
95 | | >::type> |
96 | 1.08k | bool fast_is_permutation(Iter1 first1, Iter1 last1, Iter2 first2) { |
97 | 1.08k | std::size_t xBuckets[256] = {0}; |
98 | 1.08k | std::size_t yBuckets[256] = {0}; |
99 | | |
100 | 58.7M | for (; first1 != last1; ++first1, ++first2) { |
101 | 58.7M | xBuckets[*first1]++; |
102 | 58.7M | yBuckets[*first2]++; |
103 | 58.7M | } |
104 | | |
105 | 279k | for (std::size_t i = 0; i < 256; ++i) |
106 | 278k | if (xBuckets[i] != yBuckets[i]) |
107 | 0 | return false; |
108 | | |
109 | 1.08k | return true; |
110 | 1.08k | } |
111 | | |
112 | | // When running inside OSS-Fuzz, we link against a fuzzing library that defines |
113 | | // main() and calls LLVMFuzzerTestOneInput. |
114 | | // |
115 | | // Otherwise, when e.g. running the Lit tests, we define main() to run fuzzing |
116 | | // tests on a few inputs. |
117 | | #if !defined(LIBCPP_OSS_FUZZ) |
118 | | extern "C" int LLVMFuzzerTestOneInput(const std::uint8_t*, std::size_t); |
119 | | |
120 | | int main(int, char**) { |
121 | | const char* test_cases[] = { |
122 | | "", |
123 | | "s", |
124 | | "bac", |
125 | | "bacasf", |
126 | | "lkajseravea", |
127 | | "adsfkajdsfjkas;lnc441324513,34535r34525234", |
128 | | "b*c", |
129 | | "ba?sf", |
130 | | "lka*ea", |
131 | | "adsf*kas;lnc441[0-9]1r34525234" |
132 | | }; |
133 | | |
134 | | for (const char* tc : test_cases) { |
135 | | const std::size_t size = std::strlen(tc); |
136 | | const std::uint8_t* data = reinterpret_cast<const std::uint8_t*>(tc); |
137 | | int result = LLVMFuzzerTestOneInput(data, size); |
138 | | assert(result == 0); |
139 | | } |
140 | | |
141 | | return 0; |
142 | | } |
143 | | #endif // !LIBCPP_OSS_FUZZ |
144 | | |
145 | | #endif // TEST_LIBCXX_FUZZING_FUZZ_H |