use std::collections::BTreeMap;
use substrate_data::{Map, Ref, Value};
use crate::v1::V1Writer;
pub(crate) struct Rng(u64, pub(crate) BTreeMap<&'static str, usize>);
impl Rng {
pub(crate) fn new(seed: u64) -> Rng {
Rng(seed, BTreeMap::new())
}
pub(crate) fn note(&mut self, what: &'static str) {
*self.1.entry(what).or_default() += 1;
}
fn next(&mut self) -> u64 {
self.0 = self
.0
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
self.0 >> 24
}
fn below(&mut self, n: usize) -> usize {
usize::try_from(self.next() % n as u64).expect("a small number")
}
}
const WORDS: [&str; 12] = [
"name",
"id",
"kind",
"x",
"y",
"tags",
"owner",
"@@at",
"@file:x",
"é",
"",
"a longer word",
];
const KINDS: [&str; 4] = ["file", "user", "doc", "project_2"];
fn int(rng: &mut Rng) -> i64 {
match rng.below(6) {
0 => i64::try_from(rng.below(300)).expect("small") - 20,
1 => [-17, -16, -15, 110, 111, 112, 127, 128, 0, 1][rng.below(10)],
2 => i64::MIN + i64::try_from(rng.below(3)).expect("small"),
3 => i64::MAX - i64::try_from(rng.below(3)).expect("small"),
_ => rng.next().cast_signed(),
}
}
fn float(rng: &mut Rng) -> f64 {
match rng.below(6) {
0 => f64::from_bits(rng.next() << 20 | rng.next()),
1 => [
0.0,
-0.0,
0.5,
1.0,
-1.5,
f64::INFINITY,
f64::NEG_INFINITY,
1e16,
][rng.below(8)],
2 => f64::from(f32::from_bits(
u32::try_from(rng.next() & 0xffff_ffff).expect("32 bits"),
)),
3 => f64::NAN,
4 => f64::from_bits(0x7ff0_0000_0000_0001 + rng.next() % 1000),
_ => f64::from(u32::try_from(rng.next() % 1000).expect("small")) / 8.0,
}
}
fn word(rng: &mut Rng) -> String {
if rng.below(8) == 0 {
"w".repeat(rng.below(300))
} else {
WORDS[rng.below(WORDS.len())].to_owned()
}
}
fn generate(rng: &mut Rng, w: &mut V1Writer, depth: usize) -> Value {
let top = if depth == 0 { 8 } else { 15 };
match rng.below(top) {
0 => {
rng.note("null");
w.null();
Value::Null
}
1 => {
let b = rng.below(2) == 0;
w.bool(b);
Value::Bool(b)
}
2 => {
let n = int(rng);
w.int(n);
Value::Int(n)
}
3 => {
let f = float(rng);
w.float(f);
Value::Float(f)
}
4 => {
rng.note("float32");
let f = f32::from_bits(u32::try_from(rng.next() & 0xffff_ffff).expect("32 bits"));
w.float32(f);
Value::Float(f64::from(f))
}
5 => {
let s = word(rng);
w.str(&s);
Value::Str(s)
}
6 | 7 => bytes_or_ref(rng, w),
_ => composite(rng, w, depth),
}
}
fn bytes_or_ref(rng: &mut Rng, w: &mut V1Writer) -> Value {
if rng.below(2) == 0 {
rng.note("bytes");
let bytes: Vec<u8> = (0..rng.below(20))
.map(|_| rng.next().to_le_bytes()[0])
.collect();
w.bytes(&bytes);
return Value::Bytes(bytes);
}
rng.note("ref");
let kind = KINDS[rng.below(KINDS.len())];
let id = format!("{}:{}", word(rng).replace(' ', "_"), rng.below(5));
w.reference(kind, &id);
Value::Ref(Ref::parse(kind, &id).expect("a reference"))
}
fn composite(rng: &mut Rng, w: &mut V1Writer, depth: usize) -> Value {
match rng.below(8) {
7 => records(rng, w, depth),
0 => {
let n = rng.below(5);
w.list(n);
Value::List((0..n).map(|_| generate(rng, w, depth - 1)).collect())
}
1 => {
rng.note("ints");
let items: Vec<i64> = (0..rng.below(8)).map(|_| int(rng)).collect();
w.ints(&items);
Value::List(items.into_iter().map(Value::Int).collect())
}
2 => {
rng.note("floats");
let items: Vec<f64> = (0..rng.below(8)).map(|_| float(rng)).collect();
w.floats(&items);
Value::List(items.into_iter().map(Value::Float).collect())
}
3 => {
rng.note("floats32");
let items: Vec<f32> = (0..rng.below(8))
.map(|_| f32::from_bits(u32::try_from(rng.next() & 0xffff_ffff).expect("32 bits")))
.collect();
w.floats32(&items);
Value::List(
items
.into_iter()
.map(|f| Value::Float(f64::from(f)))
.collect(),
)
}
4 => stream(rng, w, depth),
_ => map(rng, w, depth),
}
}
fn records(rng: &mut Rng, w: &mut V1Writer, depth: usize) -> Value {
rng.note("records");
let mut keys: Vec<String> = Vec::new();
let width = 1 + rng.below(4);
while keys.len() < width {
let key = word(rng);
if !keys.contains(&key) {
keys.push(key);
}
}
let count = 1 + rng.below(5);
w.list(count);
let mut items = Vec::new();
for _ in 0..count {
let mut order = keys.clone();
if rng.below(6) == 0 {
order.reverse();
}
w.map(order.len());
let mut m = Map::new();
for key in order {
w.key(&key);
m.insert(key, generate(rng, w, depth - 1));
}
items.push(Value::Map(m));
}
Value::List(items)
}
fn stream(rng: &mut Rng, w: &mut V1Writer, depth: usize) -> Value {
rng.note("stream");
let mut body = V1Writer::body();
let items: Vec<Value> = (0..rng.below(4))
.map(|_| generate(rng, &mut body, depth - 1))
.collect();
w.stream(items.len(), &body.finish());
Value::List(items)
}
fn map(rng: &mut Rng, w: &mut V1Writer, depth: usize) -> Value {
rng.note("map");
let mut keys: Vec<String> = Vec::new();
let n = if rng.below(10) == 0 {
18 + rng.below(8)
} else {
rng.below(5)
};
while keys.len() < n {
let key = if n > 16 {
format!("k{}", keys.len())
} else {
word(rng)
};
if !keys.contains(&key) {
keys.push(key);
}
}
w.map(keys.len());
let mut m = Map::new();
for key in keys {
w.key(&key);
m.insert(key, generate(rng, w, depth - 1));
}
Value::Map(m)
}
pub(crate) fn document(rng: &mut Rng) -> (Vec<u8>, Value) {
let mut w = V1Writer::new();
let value = generate(rng, &mut w, 4);
(w.finish(), value)
}