Provide means to seed rng
This commit is contained in:
+32
-34
@@ -152,9 +152,6 @@ template <class T> struct ArenaAlloc {
|
|||||||
|
|
||||||
[[nodiscard]] T *allocate(size_t n) {
|
[[nodiscard]] T *allocate(size_t n) {
|
||||||
if (n > 0xfffffffffffffffful / sizeof(T)) { // NOLINT
|
if (n > 0xfffffffffffffffful / sizeof(T)) { // NOLINT
|
||||||
fprintf(stderr, "Requested bad alloc! sizeof(T): %zu, n: %zu\n",
|
|
||||||
sizeof(T), n); // NOLINT
|
|
||||||
fflush(stderr);
|
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,13 +256,10 @@ private:
|
|||||||
uint64_t inc{};
|
uint64_t inc{};
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO provide a way to seed this
|
template <class Container> void shuffle(Random &rand, Container &x) {
|
||||||
thread_local inline Random gRandom{0, 0};
|
|
||||||
|
|
||||||
template <class Container> void shuffle(Container &x) {
|
|
||||||
using std::swap;
|
using std::swap;
|
||||||
for (int i = x.size() - 1; i > 0; --i) {
|
for (int i = x.size() - 1; i > 0; --i) {
|
||||||
int j = gRandom.bounded(i + 1);
|
int j = rand.bounded(i + 1);
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
swap(x[i], x[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 ====================
|
// ==================== END ARBITRARY IMPL ====================
|
||||||
|
|
||||||
#define SHOW_PRIORITY 0
|
#define SHOW_PRIORITY 0
|
||||||
@@ -501,7 +488,8 @@ struct Node {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Note: `rangeVersion` is left uninitialized.
|
// 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());
|
assert(key.len <= std::numeric_limits<int>::max());
|
||||||
Node *result = (Node *)malloc(sizeof(Node) + key.len);
|
Node *result = (Node *)malloc(sizeof(Node) + key.len);
|
||||||
result->maxVersion = pointVersion;
|
result->maxVersion = pointVersion;
|
||||||
@@ -509,7 +497,7 @@ Node *createNode(const Key &key, Node *parent, int64_t pointVersion) {
|
|||||||
result->child[0] = nullptr;
|
result->child[0] = nullptr;
|
||||||
result->child[1] = nullptr;
|
result->child[1] = nullptr;
|
||||||
result->parent = parent;
|
result->parent = parent;
|
||||||
result->priority = gRandom.next();
|
result->priority = rand.next();
|
||||||
#if SHOW_PRIORITY
|
#if SHOW_PRIORITY
|
||||||
result->priority &= 0xff;
|
result->priority &= 0xff;
|
||||||
#endif
|
#endif
|
||||||
@@ -902,10 +890,13 @@ bool checkCorrectness(Node *node, ReferenceImpl &refImpl) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
|
struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
|
||||||
|
Random rand;
|
||||||
Node *root;
|
Node *root;
|
||||||
int64_t oldestVersion;
|
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) {
|
oldestVersion(oldestVersion) {
|
||||||
root->rangeVersion = oldestVersion;
|
root->rangeVersion = oldestVersion;
|
||||||
}
|
}
|
||||||
@@ -954,11 +945,13 @@ struct __attribute__((__visibility__("hidden"))) ConflictSet::Impl {
|
|||||||
Node *parent;
|
Node *parent;
|
||||||
const Key *key;
|
const Key *key;
|
||||||
int64_t writeVersion;
|
int64_t writeVersion;
|
||||||
|
Random *rand;
|
||||||
|
|
||||||
StepwiseInsert() {}
|
StepwiseInsert() {}
|
||||||
StepwiseInsert(Node **root, const Key &key, int64_t writeVersion)
|
StepwiseInsert(Node **root, const Key &key, int64_t writeVersion,
|
||||||
: current(root), parent(nullptr), key(&key),
|
Random *rand)
|
||||||
writeVersion(writeVersion) {}
|
: current(root), parent(nullptr), key(&key), writeVersion(writeVersion),
|
||||||
|
rand(rand) {}
|
||||||
bool step() {
|
bool step() {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
fprintf(stderr, "Step insert of %.*s. At node: %.*s\n", key->len, key->p,
|
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");
|
(*current) ? (const char *)((*current) + 1) : "nullptr");
|
||||||
#endif
|
#endif
|
||||||
if (*current == nullptr) {
|
if (*current == nullptr) {
|
||||||
auto *newNode = createNode(*key, parent, writeVersion);
|
auto *newNode = createNode(*key, parent, writeVersion, *rand);
|
||||||
*current = newNode;
|
*current = newNode;
|
||||||
// We could interleave the iteration in ::next, but we'd need a careful
|
// We could interleave the iteration in ::next, but we'd need a careful
|
||||||
// analysis for correctness and it's unlikely to be worthwhile.
|
// 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);
|
assert(writes[i].end.len == 0);
|
||||||
|
|
||||||
stepwiseInserts[i] =
|
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
|
// 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
|
// Mitigate potential n^2 behavior of insertion by shuffling the insertion
|
||||||
// order. Not sure how this interacts with interleaved insertion but it's
|
// order. Not sure how this interacts with interleaved insertion but it's
|
||||||
// probably fine.
|
// probably fine.
|
||||||
shuffle(stepwiseInserts);
|
shuffle(rand, stepwiseInserts);
|
||||||
|
|
||||||
runInterleaved(stepwiseInserts);
|
runInterleaved(stepwiseInserts);
|
||||||
|
|
||||||
@@ -1091,8 +1084,8 @@ void ConflictSet::setOldestVersion(int64_t oldestVersion) {
|
|||||||
return impl->setOldestVersion(oldestVersion);
|
return impl->setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConflictSet::ConflictSet(int64_t oldestVersion)
|
ConflictSet::ConflictSet(int64_t oldestVersion, uint64_t seed)
|
||||||
: impl(new(malloc(sizeof(Impl))) Impl{oldestVersion}) {}
|
: impl(new(malloc(sizeof(Impl))) Impl{oldestVersion, seed}) {}
|
||||||
|
|
||||||
ConflictSet::~ConflictSet() {
|
ConflictSet::~ConflictSet() {
|
||||||
if (impl) {
|
if (impl) {
|
||||||
@@ -1130,9 +1123,9 @@ ConflictSet_setOldestVersion(void *cs, int64_t oldestVersion) {
|
|||||||
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
((ConflictSet::Impl *)cs)->setOldestVersion(oldestVersion);
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void *
|
__attribute__((__visibility__("default"))) void *
|
||||||
ConflictSet_create(int64_t oldestVersion) {
|
ConflictSet_create(int64_t oldestVersion, uint64_t seed) {
|
||||||
return new (malloc(sizeof(ConflictSet::Impl)))
|
return new (malloc(sizeof(ConflictSet::Impl)))
|
||||||
ConflictSet::Impl{oldestVersion};
|
ConflictSet::Impl{oldestVersion, seed};
|
||||||
}
|
}
|
||||||
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
__attribute__((__visibility__("default"))) void ConflictSet_destroy(void *cs) {
|
||||||
using Impl = ConflictSet::Impl;
|
using Impl = ConflictSet::Impl;
|
||||||
@@ -1217,7 +1210,7 @@ struct ReferenceImpl {
|
|||||||
#ifdef ENABLE_TESTS
|
#ifdef ENABLE_TESTS
|
||||||
int main(void) {
|
int main(void) {
|
||||||
int64_t writeVersion = 0;
|
int64_t writeVersion = 0;
|
||||||
ConflictSet::Impl cs{writeVersion};
|
ConflictSet::Impl cs{writeVersion, 0};
|
||||||
ReferenceImpl refImpl{writeVersion};
|
ReferenceImpl refImpl{writeVersion};
|
||||||
Arena arena;
|
Arena arena;
|
||||||
constexpr int kNumKeys = 10;
|
constexpr int kNumKeys = 10;
|
||||||
@@ -1239,10 +1232,13 @@ int main(void) {
|
|||||||
#ifdef ENABLE_FUZZ
|
#ifdef ENABLE_FUZZ
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
// TODO call setOldestVersion, and check range writes/reads
|
// 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;
|
int64_t writeVersion = 0;
|
||||||
ConflictSet::Impl cs{writeVersion};
|
ConflictSet::Impl cs{writeVersion, rand.next()};
|
||||||
ReferenceImpl refImpl{writeVersion};
|
ReferenceImpl refImpl{writeVersion};
|
||||||
|
|
||||||
while (gArbitrary.hasEntropy()) {
|
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{
|
std::set<int, std::less<int>, ArenaAlloc<int>> keys{
|
||||||
ArenaAlloc<int>(&arena)};
|
ArenaAlloc<int>(&arena)};
|
||||||
while (int(keys.size()) < numWrites) {
|
while (int(keys.size()) < numWrites) {
|
||||||
keys.insert(gRandom.bounded(100));
|
keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100)
|
||||||
|
: rand.bounded(100));
|
||||||
}
|
}
|
||||||
auto iter = keys.begin();
|
auto iter = keys.begin();
|
||||||
for (int i = 0; i < numWrites; ++i) {
|
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{
|
std::set<int, std::less<int>, ArenaAlloc<int>> keys{
|
||||||
ArenaAlloc<int>(&arena)};
|
ArenaAlloc<int>(&arena)};
|
||||||
while (int(keys.size()) < numReads) {
|
while (int(keys.size()) < numReads) {
|
||||||
keys.insert(gRandom.bounded(100));
|
keys.insert(gArbitrary.hasEntropy() ? gArbitrary.bounded(100)
|
||||||
|
: rand.bounded(100));
|
||||||
}
|
}
|
||||||
auto iter = keys.begin();
|
auto iter = keys.begin();
|
||||||
for (int i = 0; i < numReads; ++i) {
|
for (int i = 0; i < numReads; ++i) {
|
||||||
|
|||||||
+2
-2
@@ -54,7 +54,7 @@ struct __attribute__((__visibility__("default"))) ConflictSet {
|
|||||||
|
|
||||||
/// Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
/// Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
||||||
/// no writes initially.
|
/// no writes initially.
|
||||||
explicit ConflictSet(int64_t oldestVersion);
|
explicit ConflictSet(int64_t oldestVersion, uint64_t seed);
|
||||||
~ConflictSet();
|
~ConflictSet();
|
||||||
|
|
||||||
#if __cplusplus > 199711L
|
#if __cplusplus > 199711L
|
||||||
@@ -126,7 +126,7 @@ void ConflictSet_addWrites(ConflictSet *cs,
|
|||||||
void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion);
|
void ConflictSet_setOldestVersion(ConflictSet *cs, int64_t oldestVersion);
|
||||||
/// Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
/// Reads where readVersion < oldestVersion will result in `TooOld`. There are
|
||||||
/// no writes initially.
|
/// no writes initially.
|
||||||
ConflictSet *ConflictSet_create(int64_t oldestVersion);
|
ConflictSet *ConflictSet_create(int64_t oldestVersion, uint64_t seed);
|
||||||
void ConflictSet_destroy(ConflictSet *cs);
|
void ConflictSet_destroy(ConflictSet *cs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
ConflictSet *cs = ConflictSet_create(0);
|
ConflictSet *cs = ConflictSet_create(0, 0);
|
||||||
ConflictSet_WriteRange w;
|
ConflictSet_WriteRange w;
|
||||||
ConflictSet_Result result;
|
ConflictSet_Result result;
|
||||||
ConflictSet_ReadRange r;
|
ConflictSet_ReadRange r;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
ConflictSet cs(0);
|
ConflictSet cs(0, 0);
|
||||||
ConflictSet::WriteRange w;
|
ConflictSet::WriteRange w;
|
||||||
w.begin.p = (const uint8_t *)"0000";
|
w.begin.p = (const uint8_t *)"0000";
|
||||||
w.begin.len = 4;
|
w.begin.len = 4;
|
||||||
|
|||||||
Reference in New Issue
Block a user