ssz_rs 0.9.0

ethereum's simple serialize
Documentation
use sha2::{Digest, Sha256};
use std::{env, fs::File, io::Write, path::Path};

const TARGET_FILE: &str = "context.rs";
const MAX_MERKLE_TREE_DEPTH: usize = 64;
const BYTES_PER_CHUNK: usize = 32;

fn hash_nodes(hasher: &mut Sha256, a: &[u8], b: &[u8], out: &mut [u8]) {
    hasher.update(a);
    hasher.update(b);
    out.copy_from_slice(&hasher.finalize_reset());
}

fn compute_zero_hashes() -> [u8; MAX_MERKLE_TREE_DEPTH * BYTES_PER_CHUNK] {
    let mut hasher = Sha256::new();
    let mut buffer = [0u8; MAX_MERKLE_TREE_DEPTH * BYTES_PER_CHUNK];
    for i in 0..MAX_MERKLE_TREE_DEPTH - 1 {
        let focus_range = i * BYTES_PER_CHUNK..(i + 2) * BYTES_PER_CHUNK;
        let focus = &mut buffer[focus_range];
        let (source, target) = focus.split_at_mut(BYTES_PER_CHUNK);
        hash_nodes(&mut hasher, source, source, target);
    }
    buffer
}

// This function derives a set of bytes corresponding to "zero hashes" at build-time
// in lieu of needing to declare any sort of runtime static memory or similar technique.
// If any of the hashing code changes significantly for the SSZ accumulator scheme,
// this code will need to be updated as well.
fn generate() -> std::io::Result<()> {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join(TARGET_FILE);
    let mut f = File::create(dest_path)?;
    let data = compute_zero_hashes();
    write!(
        f,
        "
        // Generated by build.rs

        static CONTEXT: Context = Context {{
            zero_hashes: {data:?},
        }};",
    )
    .unwrap();
    Ok(())
}

fn main() -> std::io::Result<()> {
    generate()?;
    println!("cargo:rerun-if-changed=build.rs");
    Ok(())
}