From 3d8a54d3841a69f16dec9e061c3a8b1b19a9e0c1 Mon Sep 17 00:00:00 2001 From: Ethiraric Date: Wed, 14 Feb 2024 00:35:41 +0100 Subject: [PATCH] Add a generator for nested objects. --- saphyr/examples/gen_large_yaml/main.rs | 6 +- saphyr/examples/gen_large_yaml/nested.rs | 108 +++++++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 saphyr/examples/gen_large_yaml/nested.rs diff --git a/saphyr/examples/gen_large_yaml/main.rs b/saphyr/examples/gen_large_yaml/main.rs index 4ba7ad9..cfc8d70 100644 --- a/saphyr/examples/gen_large_yaml/main.rs +++ b/saphyr/examples/gen_large_yaml/main.rs @@ -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(()) } diff --git a/saphyr/examples/gen_large_yaml/nested.rs b/saphyr/examples/gen_large_yaml/nested.rs new file mode 100644 index 0000000..3977901 --- /dev/null +++ b/saphyr/examples/gen_large_yaml/nested.rs @@ -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(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>, + /// Array of all the nodes in the tree, including the root node. + nodes: Vec>>, + /// The RNG state. + rng: ThreadRng, +} + +/// A node in a tree. +struct Node { + /// All the children of the node. + children: Vec>>, +} + +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(&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> { + Rc::new(RefCell::new(Self::new())) + } + + /// Append a child to the node. + fn push_child(&mut self, child: Rc>) { + self.children.push(child); + } + + /// Write the YAML representation of the node to `writer`. + fn write_to(&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(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(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(()) +}