Provide means to seed rng

This commit is contained in:
2024-01-19 17:48:27 -08:00
parent 7ad68231a4
commit e6270c049b
4 changed files with 36 additions and 38 deletions

View File

@@ -152,9 +152,6 @@ template <class T> struct ArenaAlloc {
[[nodiscard]] T *allocate(size_t n) {
if (n > 0xfffffffffffffffful / sizeof(T)) { // NOLINT
fprintf(stderr, "Requested bad alloc! sizeof(T): %zu, n: %zu\n",
sizeof(T), n); // NOLINT
fflush(stderr);
abort();
}
@@ -259,13 +256,10 @@ private:
uint64_t inc{};
};
// TODO provide a way to seed this
thread_local inline Random gRandom{0, 0};
template <class Container> void shuffle(Container &x) {
template <class Container> void shuffle(Random &rand, Container &x) {
using std::swap;
for (int i = x.size() - 1; i > 0; --i) {
int j = gRandom.bounded(i + 1);
int j = rand.bounded(i + 1);
if (i != j) {
swap(x[i], x[j]);
}
@@ -433,13 +427,6 @@ uint32_t Arbitrary::bounded(uint32_t s) {
}
}
void initFuzz(const uint8_t *data, size_t size) {
gArbitrary = Arbitrary{{data, size}};
uint64_t state = gArbitrary.next();
uint64_t seq = gArbitrary.next();
gRandom = Random{state, seq};
}
// ==================== END ARBITRARY IMPL ====================
#define SHOW_PRIORITY 0
@@ -501,7 +488,8 @@ struct Node {
};
// Note: `rangeVersion` is left uninitialized.
Node *createNode(const Key &key, Node *parent, int64_t pointVersion) {
Node *createNode(const Key &key, Node *parent, int64_t pointVersion,
Random &rand) {
assert(key.len <= std::numeric_limits<int>::max());
Node *result = (Node *)malloc(sizeof(Node) + key.len);
result->maxVersion = pointVersion;
@@ -509,7 +497,7 @@ Node *createNode(const Key &key, Node *parent, int64_t pointVersion) {
result->child[0] = nullptr;
result->child[1] = nullptr;
result->parent = parent;
result->priority = gRandom.next();
result->priority = rand.next();
#if SHOW_PRIORITY
result->priority &= 0xff;
#endif
@@ -902,10 +890,13 @@ bool checkCorrectness(Node *node, ReferenceImpl &refImpl) {
} // namespace
struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
Random rand;
Node *root;
int64_t oldestVersion;
explicit Impl(int64_t oldestVersion) noexcept
: root(createNode({nullptr, 0}, nullptr, oldestVersion)),
explicit Impl(int64_t oldestVersion, uint64_t seed) noexcept
: rand{seed & 0xfffffffful, seed >> 32},
root(createNode({nullptr, 0}, nullptr, oldestVersion, rand)),
oldestVersion(oldestVersion) {
root->rangeVersion = oldestVersion;
}
@@ -954,11 +945,13 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
Node *parent;
const Key *key;
int64_t writeVersion;
Random *rand;
StepwiseInsert() {}
StepwiseInsert(Node **root, const Key &key, int64_t writeVersion)
: current(root), parent(nullptr), key(&key),
writeVersion(writeVersion) {}
StepwiseInsert(Node **root, const Key &key, int64_t writeVersion,
Random *rand)
: current(root), parent(nullptr), key(&key), writeVersion(writeVersion),
rand(rand) {}
bool step() {
#if DEBUG
fprintf(stderr, "Step insert of %.*s. At node: %.*s\n", key->len, key->p,
@@ -966,7 +959,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
(*current) ? (const char *)((*current) + 1) : "nullptr");
#endif
if (*current == nullptr) {
auto *newNode = createNode(*key, parent, writeVersion);
auto *newNode = createNode(*key, parent, writeVersion, *rand);
*current = newNode;
// We could interleave the iteration in ::next, but we'd need a careful
// analysis for correctness and it's unlikely to be worthwhile.
@@ -1000,7 +993,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
assert(writes[i].end.len == 0);
stepwiseInserts[i] =
StepwiseInsert{&root, writes[i].begin, writes[i].writeVersion};
StepwiseInsert{&root, writes[i].begin, writes[i].writeVersion, &rand};
}
// TODO Descend until queries for front and back diverge
@@ -1008,7 +1001,7 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
// Mitigate potential n^2 behavior of insertion by shuffling the insertion
// order. Not sure how this interacts with interleaved insertion but it's
// probably fine.
shuffle(stepwiseInserts);
shuffle(rand, stepwiseInserts);
runInterleaved(stepwiseInserts);
@@ -1091,8 +1084,8 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
return impl->setOldestVersion(oldestVersion);
}
ConflictSet::ConflictSet(int64_t oldestVersion)
: impl(new(malloc(sizeof(Impl))) Impl{oldestVersion}) {}
ConflictSet::ConflictSet(int64_t oldestVersion, uint64_t seed)
: impl(new(malloc(sizeof(Impl))) Impl{oldestVersion, seed}) {}
ConflictSet::~ConflictSet() {
if (impl) {
@@ -1130,9 +1123,9 @@ ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
}
__attribute__((__visibility__("default"))) void *
ConflictSet_create(int64_t oldestVersion) {
ConflictSet_create(int64_t oldestVersion, uint64_t seed) {
return new (malloc(sizeof(ConflictSet::Impl)))
ConflictSet::Impl{oldestVersion};
ConflictSet::Impl{oldestVersion, seed};
}
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
using Impl = ConflictSet::Impl;
@@ -1217,7 +1210,7 @@ struct ReferenceImpl {
#ifdef ENABLE_TESTS
int main(void) {
int64_t writeVersion = 0;
ConflictSet::Impl cs{writeVersion};
ConflictSet::Impl cs{writeVersion, 0};
ReferenceImpl refImpl{writeVersion};
Arena arena;
constexpr int kNumKeys = 10;
@@ -1239,10 +1232,13 @@ int main(void) {
#ifdef ENABLE_FUZZ
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// TODO call setOldestVersion, and check range writes/reads
initFuzz(data, size);
gArbitrary = Arbitrary{{data, size}};
uint64_t state = gArbitrary.next();
uint64_t seq = gArbitrary.next();
auto rand = Random{state, seq};
int64_t writeVersion = 0;
ConflictSet::Impl cs{writeVersion};
ConflictSet::Impl cs{writeVersion, rand.next()};
ReferenceImpl refImpl{writeVersion};
while (gArbitrary.hasEntropy()) {
@@ -1254,7 +1250,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::set<int, std::less<int>, ArenaAlloc<int>> keys{
ArenaAlloc<int>(&arena)};
while (int(keys.size()) < numWrites) {
keys.insert(gRandom.bounded(100));
keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100)
: rand.bounded(100));
}
auto iter = keys.begin();
for (int i = 0; i < numWrites; ++i) {
@@ -1276,7 +1273,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
std::set<int, std::less<int>, ArenaAlloc<int>> keys{
ArenaAlloc<int>(&arena)};
while (int(keys.size()) < numReads) {
keys.insert(gRandom.bounded(100));
keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100)
: rand.bounded(100));
}
auto iter = keys.begin();
for (int i = 0; i < numReads; ++i) {