Add a generator for nested objects.

This commit is contained in:
Ethiraric 2024-02-14 00:35:41 +01:00
parent 765f2bb672
commit 3d8a54d384
2 changed files with 112 additions and 2 deletions

View file

@ -1,6 +1,7 @@
#![allow(dead_code)]
mod gen;
mod nested;
use std::collections::HashMap;
@ -8,8 +9,9 @@ use rand::{rngs::ThreadRng, Rng};
fn main() -> std::fmt::Result {
let mut s = String::new();
let mut g = Generator::new();
g.gen_strings_array(&mut s, 1_300_000, 1_300_001, 10, 40)?;
// let mut g = Generator::new();
// g.gen_strings_array(&mut s, 1_300_000, 1_300_001, 10, 40)?;
nested::create_deep_object(&mut s, 5_000_000)?;
println!("{s}");
Ok(())
}

View file

@ -0,0 +1,108 @@
use std::{cell::RefCell, rc::Rc};
use rand::{rngs::ThreadRng, Rng};
/// Create a deep object with the given amount of nodes.
pub fn create_deep_object<W: std::fmt::Write>(writer: &mut W, n_nodes: usize) -> std::fmt::Result {
let mut tree = Tree::new();
for _ in 0..n_nodes {
tree.push_node();
}
tree.write_to(writer)
}
/// An n-tree.
///
/// The algorithm used to generate a potentially deep object is to create a tree, one node at a
/// time, where each node is put as a child of a random existing node in the tree.
struct Tree {
/// The tree-view of the tree.
root: Rc<RefCell<Node>>,
/// Array of all the nodes in the tree, including the root node.
nodes: Vec<Rc<RefCell<Node>>>,
/// The RNG state.
rng: ThreadRng,
}
/// A node in a tree.
struct Node {
/// All the children of the node.
children: Vec<Rc<RefCell<Node>>>,
}
impl Tree {
/// Create a new tree.
fn new() -> Self {
let root = Node::new_rc_refcell();
Tree {
root: root.clone(),
nodes: vec![root],
rng: rand::thread_rng(),
}
}
/// Add a new node as a child of a random node in the tree.
fn push_node(&mut self) {
let new_node = Node::new_rc_refcell();
let n_nodes = self.nodes.len();
let parent = &mut self.nodes[self.rng.gen_range(0..n_nodes)];
(**parent).borrow_mut().push_child(new_node.clone());
self.nodes.push(new_node);
}
/// Write the YAML representation of the tree to `writer`.
fn write_to<W: std::fmt::Write>(&self, writer: &mut W) -> std::fmt::Result {
(*self.root).borrow().write_to(writer, 0)
}
}
impl Node {
/// Create a new node.
fn new() -> Self {
Node { children: vec![] }
}
fn new_rc_refcell() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new()))
}
/// Append a child to the node.
fn push_child(&mut self, child: Rc<RefCell<Self>>) {
self.children.push(child);
}
/// Write the YAML representation of the node to `writer`.
fn write_to<W: std::fmt::Write>(&self, writer: &mut W, indent: usize) -> std::fmt::Result {
if self.children.is_empty() {
write_n(writer, ' ', indent)?;
writer.write_str("a: 1\n")?;
} else {
for (n, child) in self.children.iter().enumerate() {
write_n(writer, ' ', indent)?;
write_id_for_number(writer, n)?;
writer.write_str(":\n")?;
(**child).borrow().write_to(writer, indent + 2)?;
}
}
Ok(())
}
}
/// Write `n` times `c` to `out`.
fn write_n<W: std::fmt::Write>(out: &mut W, c: char, n: usize) -> std::fmt::Result {
for _ in 0..n {
out.write_char(c)?;
}
Ok(())
}
/// Create a valid identifier for the given number.
fn write_id_for_number<W: std::fmt::Write>(out: &mut W, mut n: usize) -> std::fmt::Result {
const DIGITS: &[u8] = b"_abcdefghijklmnopqrstuvwxyz";
n += 1;
while n > 0 {
out.write_char(DIGITS[n % DIGITS.len()] as char)?;
n /= DIGITS.len();
}
Ok(())
}