Provide means to seed rng
This commit is contained in:
@@ -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) {
|
||||
|
Reference in New Issue
Block a user