#include "include/llvm-libc-types/ENTRY.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/HashTable/table.h"
#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
static constexpr size_t INITIAL_HEADER_SIZE =
2 * (sizeof(uint16_t) + sizeof(uint64_t));
extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size, size_t max_size);
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *data, size_t size,
size_t max_size, unsigned int seed) {
size = LLVMFuzzerMutate(data, size, max_size);
if (size < INITIAL_HEADER_SIZE)
return 0;
size_t i = INITIAL_HEADER_SIZE;
while (i < size) {
if (static_cast<uint8_t>(data[i]) % 5 == 4) {
++i;
continue;
}
if (i + 2 >= max_size)
return i;
++i;
while (i < max_size && data[i] != 0)
++i;
if (i == max_size && data[i - 1] != 0) {
data[i - 1] = 0;
return max_size;
}
++i;
}
return i;
}
struct Action {
enum class Tag { Find, Insert, CrossCheck } tag;
cpp::string_view key;
};
static struct {
size_t remaining;
const char *buffer;
template <typename T> T next() {
static_assert(cpp::is_integral<T>::value, "T must be an integral type");
union {
T result;
char data[sizeof(T)];
};
for (size_t i = 0; i < sizeof(result); i++)
data[i] = buffer[i];
buffer += sizeof(result);
remaining -= sizeof(result);
return result;
}
cpp::string_view next_string() {
cpp::string_view result(buffer);
buffer = result.end() + 1;
remaining -= result.size() + 1;
return result;
}
Action next_action() {
uint8_t byte = next<uint8_t>();
switch (byte % 5) {
case 4:
return {Action::Tag::CrossCheck, {}};
case 3:
return {Action::Tag::Find, next_string()};
default:
return {Action::Tag::Insert, next_string()};
}
}
} global_status;
class HashTable {
internal::HashTable *table;
public:
HashTable(uint64_t size, uint64_t seed)
: table(internal::HashTable::allocate(size, seed)) {}
HashTable(internal::HashTable *table) : table(table) {}
~HashTable() { internal::HashTable::deallocate(table); }
HashTable(HashTable &&other) : table(other.table) { other.table = nullptr; }
bool is_valid() const { return table != nullptr; }
ENTRY *find(const char *key) { return table->find(key); }
ENTRY *insert(const ENTRY &entry) {
return internal::HashTable::insert(this->table, entry);
}
using iterator = internal::HashTable::iterator;
iterator begin() const { return table->begin(); }
iterator end() const { return table->end(); }
};
HashTable next_hashtable() {
size_t size = global_status.next<uint16_t>();
uint64_t seed = global_status.next<uint64_t>();
return HashTable(size, seed);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
global_status.buffer = reinterpret_cast<const char *>(data);
global_status.remaining = size;
if (global_status.remaining < INITIAL_HEADER_SIZE)
return 0;
HashTable table_a = next_hashtable();
HashTable table_b = next_hashtable();
for (;;) {
if (global_status.remaining == 0)
break;
Action action = global_status.next_action();
switch (action.tag) {
case Action::Tag::Find: {
if (static_cast<bool>(table_a.find(action.key.data())) !=
static_cast<bool>(table_b.find(action.key.data())))
__builtin_trap();
break;
}
case Action::Tag::Insert: {
char *ptr = const_cast<char *>(action.key.data());
ENTRY *a = table_a.insert(ENTRY{ptr, ptr});
ENTRY *b = table_b.insert(ENTRY{ptr, ptr});
if (a->data != b->data)
__builtin_trap();
break;
}
case Action::Tag::CrossCheck: {
for (ENTRY a : table_a)
if (const ENTRY *b = table_b.find(a.key); a.data != b->data)
__builtin_trap();
for (ENTRY b : table_b)
if (const ENTRY *a = table_a.find(b.key); a->data != b.data)
__builtin_trap();
break;
}
}
}
return 0;
}
}