/// Deterministic benchmark scenarios without wall-clock dependencies.
///
/// The benchmark reports work in bytes and operations so it can be compared
/// across Native, JavaScript, and WebAssembly targets in CI.
pub(all) struct BenchmarkInput {
name : String
bytes : Array[Byte]
description : String
} derive(Show)
pub fn BenchmarkInput::name(self : BenchmarkInput) -> String { self.name }
pub fn BenchmarkInput::bytes(self : BenchmarkInput) -> Array[Byte] { self.bytes.copy() }
pub fn BenchmarkInput::description(self : BenchmarkInput) -> String { self.description }
pub(all) struct BenchmarkResult {
name : String
input_bytes : Int
chunks : Int
digest_operations : Int
manifest_bytes : Int
restored_bytes : Int
reused_bytes : Int
uploaded_bytes : Int
checksum : String
} derive(Show)
pub fn BenchmarkResult::name(self : BenchmarkResult) -> String { self.name }
pub fn BenchmarkResult::input(self : BenchmarkResult) -> Int { self.input_bytes }
pub fn BenchmarkResult::chunks(self : BenchmarkResult) -> Int { self.chunks }
pub fn BenchmarkResult::digest_ops(self : BenchmarkResult) -> Int { self.digest_operations }
pub fn BenchmarkResult::manifest_size(self : BenchmarkResult) -> Int { self.manifest_bytes }
pub fn BenchmarkResult::restored(self : BenchmarkResult) -> Int { self.restored_bytes }
pub fn BenchmarkResult::reused(self : BenchmarkResult) -> Int { self.reused_bytes }
pub fn BenchmarkResult::uploaded(self : BenchmarkResult) -> Int { self.uploaded_bytes }
pub fn BenchmarkResult::checksum(self : BenchmarkResult) -> String { self.checksum }
fn deterministic_byte(seed : UInt, index : Int) -> Byte {
let mut x = seed + index.reinterpret_as_uint() * 0x9e3779b9U
x = x ^ (x >> 16)
x = x * 0x7feb352dU
x = x ^ (x >> 15)
(x & 0xffU).to_int().to_byte()
}
pub fn synthetic_input(name : String, size : Int, seed : UInt) -> BenchmarkInput {
let count = if size < 0 { 0 } else { size }
let data = Array::makei(count, i => deterministic_byte(seed, i))
{ name, bytes: data, description: "deterministic pseudo-random bytes" }
}
pub fn repetitive_input(name : String, size : Int, pattern : String) -> BenchmarkInput {
let _ = pattern
let count = if size < 0 { 0 } else { size }
let data : Array[Byte] = []
let repeated : Byte = b'x'
for _ in 0.. BenchmarkInput {
let paragraph = "MoonChunk content-defined boundaries support backups, caches, and edge replicas.\n"
let out = StringBuilder::new()
let count = if paragraphs < 0 { 0 } else { paragraphs }
for _ in 0.. BenchmarkInput {
let data = input.bytes()
let first = if start < 0 { 0 } else if start > data.length() { data.length() } else { start }
let last = if length < 0 { first } else if first + length > data.length() { data.length() } else { first + length }
for i in first.. BenchmarkInput {
let source = input.bytes()
let position = if at < 0 { 0 } else if at > source.length() { source.length() } else { at }
let out = []
for i in 0.. BenchmarkInput {
let source = input.bytes()
let first = if start < 0 { 0 } else if start > source.length() { source.length() } else { start }
let last = if length < 0 { first } else if first + length > source.length() { source.length() } else { first + length }
let out = []
for i in 0.. BenchmarkResult {
let data = input.bytes()
let chunks = chunk_bytes(data, config)
let manifest = manifest_from_chunks(input.name(), chunks)
let encoded = encode_manifest(manifest)
let store = ChunkStore::new()
let mut digest_operations = 0
for chunk in chunks {
let _ = store.put(chunk)
digest_operations = digest_operations + 1
}
let restored = match restore_manifest(store, manifest) {
Ok(value) => value.length()
Err(_) => 0
}
{ name: input.name(), input_bytes: data.length(), chunks: chunks.length(), digest_operations, manifest_bytes: encoded.to_bytes().length(), restored_bytes: restored, reused_bytes: data.length(), uploaded_bytes: 0, checksum: digest_bytes(data) }
}
pub fn run_comparison(old : BenchmarkInput, next : BenchmarkInput, config : ChunkerConfig) -> BenchmarkResult {
let old_chunks = chunk_bytes(old.bytes(), config)
let next_chunks = chunk_bytes(next.bytes(), config)
let old_manifest = manifest_from_chunks(old.name(), old_chunks)
let next_manifest = manifest_from_chunks(next.name(), next_chunks)
let plan = diff_manifests(old_manifest, next_manifest)
let encoded = encode_manifest(next_manifest)
let store = ChunkStore::new()
let _ = store_chunks(store, next_chunks)
let restored = match restore_manifest(store, next_manifest) {
Ok(value) => value.length()
Err(_) => 0
}
{ name: old.name() + " -> " + next.name(), input_bytes: next.bytes().length(), chunks: next_chunks.length(), digest_operations: old_chunks.length() + next_chunks.length(), manifest_bytes: encoded.to_bytes().length(), restored_bytes: restored, reused_bytes: plan.reused(), uploaded_bytes: plan.uploaded(), checksum: next_manifest.root_digest() }
}
pub fn benchmark_scenarios() -> Array[BenchmarkInput] {
[
synthetic_input("random-4k", 4096, 1U),
synthetic_input("random-32k", 32768, 2U),
repetitive_input("repeat-16k", 16384, "moonbit"),
text_input("docs-8k", 100),
]
}
pub fn benchmark_all(config? : ChunkerConfig = ChunkerConfig::new()) -> Array[BenchmarkResult] {
let results = []
for input in benchmark_scenarios() { results.push(run_benchmark(input, config)) }
results
}
pub fn benchmark_total_input(results : Array[BenchmarkResult]) -> Int {
let mut total = 0
for result in results { total = total + result.input() }
total
}
pub fn benchmark_total_chunks(results : Array[BenchmarkResult]) -> Int {
let mut total = 0
for result in results { total = total + result.chunks() }
total
}
pub fn benchmark_total_manifest_bytes(results : Array[BenchmarkResult]) -> Int {
let mut total = 0
for result in results { total = total + result.manifest_size() }
total
}
pub fn benchmark_report(results : Array[BenchmarkResult]) -> String {
let out = StringBuilder::new()
for result in results {
out.write_string(result.name())
out.write_string(" input=")
out.write_string(result.input().to_string())
out.write_string(" chunks=")
out.write_string(result.chunks().to_string())
out.write_string(" restored=")
out.write_string(result.restored().to_string())
out.write_string(" checksum=")
out.write_string(result.checksum())
out.write_string("\n")
}
out.to_string()
}
pub fn comparison_report(result : BenchmarkResult) -> String {
"{result.name()} reused={result.reused()} uploaded={result.uploaded()} chunks={result.chunks()}"
}
pub fn benchmark_round_trip_ok(result : BenchmarkResult) -> Bool {
result.input() == result.restored() && result.input() >= 0
}
pub fn benchmark_inputs_are_deterministic() -> Bool {
let left = synthetic_input("x", 128, 9U)
let right = synthetic_input("x", 128, 9U)
digest_equal(digest_bytes(left.bytes()), digest_bytes(right.bytes()))
}
pub fn benchmark_mutation_overlap(config : ChunkerConfig) -> Int {
let original = synthetic_input("original", 4096, 4U)
let changed = mutate_region(original, 1700, 16, 99U)
let left = chunk_bytes(original.bytes(), config)
let right = chunk_bytes(changed.bytes(), config)
content_overlap(left, right)
}
pub fn benchmark_insertion_overlap(config : ChunkerConfig) -> Int {
let original = text_input("original", 80)
let changed = insert_region(original, 200, [b'x', b'y', b'z'])
content_overlap(chunk_bytes(original.bytes(), config), chunk_bytes(changed.bytes(), config))
}
pub fn benchmark_deletion_overlap(config : ChunkerConfig) -> Int {
let original = text_input("original", 80)
let changed = delete_region(original, 200, 3)
content_overlap(chunk_bytes(original.bytes(), config), chunk_bytes(changed.bytes(), config))
}
pub fn benchmark_checksum_chain(inputs : Array[BenchmarkInput]) -> String {
let mut current = ""
for input in inputs { current = digest_string(current + digest_bytes(input.bytes())) }
current
}