made a few improvements

This commit is contained in:
2026-04-26 22:09:38 -04:00
parent 4eccf5a9ec
commit 2fe8f1a287
3 changed files with 102 additions and 63 deletions
+1
View File
@@ -1 +1,2 @@
/target
state.txt
+62 -24
View File
@@ -1,73 +1,111 @@
use std::fs;
use rand::rngs::OsRng;
use rand::Rng;
use std::fmt::Write;
const FILENAME : &'static str = "state.txt";
const FILENAME : &str = "state.txt"; // 'static is implied, due to literal
const FILENAME_WIP : &str = "state.wip";
fn random_index(rng: &mut OsRng) -> usize {
rng.gen_range(0..25)
struct LetterCounts([u32; 26]); // tuple
impl LetterCounts {
fn add(&mut self, i : usize) {
self.0[i] += 1;
}
fn serialize_data(data: &[u32]) -> String {
// todo: use serde?
fn serialize(&self) -> String {
println!("serializing...");
let mut serialization = String::new();
for (i, count) in data.iter().enumerate() {
let mut serialization = String::with_capacity(26*4); // pre-allocate
for (i, count) in self.0.iter().enumerate() {
let c = (i as u8 + 97) as char;
let s = format!("{} {}\n", c, count.to_string());
serialization.push_str(&s)
// note that we write directly to serialization without unnecessary allocation
// writeln expands to use format_args,
// which uses a small struct (stack-only) to track the inputs before
// they are fed into the target String.
// So we've saved not only the copying itself but the overhead of also
// the use of malloc, which MAY do a syscall to request more memory
let _ = writeln!(serialization, "{} {}", c, count);
}
// only stack object (ptr, len, capacity) is copied on the way out
serialization
}
fn deserialize(s: &str, data: &mut [u32]) {
// todo: use serde?
fn deserialize(&mut self, s: &str) -> Result<(), Vec<String>> {
println!("deserializing...");
// assumes no duplicate (and disgreeing) lines
for line in s.split('\n') {
let mut error_strings = Vec::new();
// todo: don't just assume no duplicate (and disagreeing) lines
for line in s.lines() {
if let Some((letter, val)) = line.split_once(' ') {
let c = match letter.chars().next() {
Some(c) => c,
None => {
println!("ERROR: invalid line format, missing character");
error_strings.push("ERROR: invalid line format, missing character.".to_string());
continue;
}
};
let index = (c as u32) - 97;
if ('a'..='z').contains(&c) { // protect against non-lower alpha chars
let index = (c as u8 - b'a') as usize;
let count = match val.parse::<u32>() {
Ok(i) => i,
Err(_) => {
println!("ERROR: invalid line format, missing count");
Err(e) => {
// todo: don't store strings, unnecessary allocation
error_strings.push(format!("ERROR: invalid line format, missing count. ({})", e));
continue;
}
};
data[index as usize] = count;
self.0[index] = count;
} else {
error_strings.push("ERROR: encountered non-lowercase alphabetic char".to_string());
continue; // we just drop the char/line
}
}
}
if !error_strings.is_empty() {
return Err(error_strings);
}
Ok(())
}
}
fn random_index<R: Rng + ?Sized>(rng: &mut R) -> usize {
rng.gen_range(0..26)
}
fn main() {
// considered using mutable static array for data
// but that requires unsafe, so we just use non-static memory
let mut data : [u32; 26] = [0; 26];
let mut data = LetterCounts([0; 26]);
// deserialize file contents into data structure
if let Ok(contents) = fs::read_to_string(FILENAME) {
deserialize(&contents, &mut data);
if let Err(v) = data.deserialize(&contents) {
v.iter().for_each(|s| eprintln!("{}",s));
}
} else {
println!("ERROR: failed to read file");
eprintln!("ERROR: failed to read file");
}
// generate random char and give to data structure
let mut rng = OsRng;
let index = random_index(&mut rng);
data[index] += 1;
println!("generated new char {}", (index + 97) as u8 as char);
data.add(index);
println!("generated new char {}", (index as u8 + b'a') as char);
// todo: way to only update a single line at a time?
// write data structure to file
let s_data = serialize_data(&data);
let s_data = data.serialize();
println!("writing...");
if let Err(_) = fs::write(FILENAME, s_data) {
println!("ERROR: Couldn't write to {}", FILENAME);
// if write fails we may corrupt the original file,
// so use a temp file and then rename
if let Err(e) = fs::write(FILENAME_WIP, s_data) {
eprintln!("ERROR: Couldn't write to {} ({})", FILENAME_WIP, e);
return;
}
if let Err(e) = fs::rename(FILENAME_WIP, FILENAME) {
eprintln!("ERROR: Couldn't replace {} with {} ({})", FILENAME, FILENAME_WIP, e);
return;
}
}
+20 -20
View File
@@ -1,26 +1,26 @@
a 1
b 2
c 1
d 0
e 1
f 2
g 2
h 1
a 0
b 3
c 2
d 1
e 2
f 3
g 3
h 3
i 1
j 0
k 1
l 1
m 4
l 2
m 5
n 2
o 3
p 1
q 3
r 1
s 2
t 3
u 0
o 4
p 3
q 4
r 2
s 3
t 4
u 1
v 1
w 4
w 6
x 0
y 1
z 0
y 2
z 1