#![allow(dead_code)]
use oxihuman_core::capnproto::{
encode_list_ptr, encode_struct_ptr, write_frame, CapnMessage, CapnSegment, ElementSize,
ListPointer, Message, StructPointer,
};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CapnpField {
pub name: String,
pub field_type: String,
pub index: usize,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CapnpStruct {
pub name: String,
pub fields: Vec<CapnpField>,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct CapnpExport {
pub file_id: u64,
pub structs: Vec<CapnpStruct>,
}
#[allow(dead_code)]
pub fn new_capnp_export(file_id: u64) -> CapnpExport {
CapnpExport {
file_id,
structs: Vec::new(),
}
}
#[allow(dead_code)]
pub fn add_capnp_struct(doc: &mut CapnpExport, name: &str) {
doc.structs.push(CapnpStruct {
name: name.to_string(),
fields: Vec::new(),
});
}
#[allow(dead_code)]
pub fn add_capnp_field(doc: &mut CapnpExport, name: &str, field_type: &str) {
if let Some(s) = doc.structs.last_mut() {
let idx = s.fields.len();
s.fields.push(CapnpField {
name: name.to_string(),
field_type: field_type.to_string(),
index: idx,
});
}
}
#[allow(dead_code)]
pub fn capnp_struct_count(doc: &CapnpExport) -> usize {
doc.structs.len()
}
#[allow(dead_code)]
pub fn capnp_last_struct_field_count(doc: &CapnpExport) -> usize {
doc.structs.last().map_or(0, |s| s.fields.len())
}
#[allow(dead_code)]
pub fn to_capnp_schema(doc: &CapnpExport) -> String {
let mut out = format!("@0x{:016x};\n\n", doc.file_id);
for st in &doc.structs {
out.push_str(&format!("struct {} {{\n", st.name));
for f in &st.fields {
out.push_str(&format!(" {} @{} :{},\n", f.name, f.index, f.field_type));
}
out.push_str("}\n\n");
}
out
}
#[allow(dead_code)]
pub fn export_mesh_capnp_schema(vertex_count: usize, index_count: usize) -> String {
let mut doc = new_capnp_export(0xdeadbeefcafe0001);
add_capnp_struct(&mut doc, "Mesh");
add_capnp_field(&mut doc, "vertexCount", "UInt32");
add_capnp_field(&mut doc, "indexCount", "UInt32");
add_capnp_field(&mut doc, "positions", "List(Float32)");
let mut comment = format!(
"# vertexCount={}, indexCount={}\n",
vertex_count, index_count
);
comment.push_str(&to_capnp_schema(&doc));
comment
}
#[allow(dead_code)]
pub fn find_capnp_struct<'a>(doc: &'a CapnpExport, name: &str) -> Option<&'a CapnpStruct> {
doc.structs.iter().find(|s| s.name == name)
}
#[allow(dead_code)]
pub fn to_capnp_bytes(doc: &CapnpExport) -> Vec<u8> {
let mut seg = CapnSegment::with_capacity(3);
let root_ptr = StructPointer {
offset_words: 0,
data_words: 2,
pointer_words: 0,
};
seg.push_word(encode_struct_ptr(&root_ptr));
seg.push_word(doc.file_id);
seg.push_word(doc.structs.len() as u64);
let mut capn_msg = CapnMessage::new();
capn_msg.add_segment(seg);
let mut raw_bytes: Vec<u8> = Vec::with_capacity(capn_msg.total_bytes());
for seg_idx in 0..capn_msg.segment_count() {
if let Some(s) = capn_msg.segment(seg_idx) {
for &word in s.words() {
raw_bytes.extend_from_slice(&word.to_le_bytes());
}
}
}
let framed_msg = Message {
segments: vec![raw_bytes],
};
write_frame(&framed_msg)
}
#[allow(dead_code)]
pub fn export_mesh_capnp_binary(positions: &[[f32; 3]], indices: &[u32]) -> Vec<u8> {
let pos_element_count = positions.len() * 3;
let pos_words = pos_element_count.div_ceil(2);
let idx_element_count = indices.len();
let idx_words = idx_element_count.div_ceil(2);
let capacity = 3 + pos_words + idx_words;
let mut seg = CapnSegment::with_capacity(capacity);
let root_ptr = StructPointer {
offset_words: 0,
data_words: 0,
pointer_words: 2,
};
seg.push_word(encode_struct_ptr(&root_ptr));
let pos_list_ptr = ListPointer {
offset_words: 1,
element_size: ElementSize::FourBytes,
element_count: pos_element_count as u32,
};
seg.push_word(encode_list_ptr(&pos_list_ptr));
let idx_list_ptr = ListPointer {
offset_words: pos_words as i32,
element_size: ElementSize::FourBytes,
element_count: idx_element_count as u32,
};
seg.push_word(encode_list_ptr(&idx_list_ptr));
{
let flat: Vec<f32> = positions
.iter()
.flat_map(|&[x, y, z]| [x, y, z])
.collect();
let mut i = 0usize;
while i < flat.len() {
let lo = flat[i].to_bits() as u64;
let hi = if i + 1 < flat.len() {
flat[i + 1].to_bits() as u64
} else {
0u64
};
seg.push_word(lo | (hi << 32));
i += 2;
}
}
{
let mut i = 0usize;
while i < indices.len() {
let lo = indices[i] as u64;
let hi = if i + 1 < indices.len() {
indices[i + 1] as u64
} else {
0u64
};
seg.push_word(lo | (hi << 32));
i += 2;
}
}
let mut capn_msg = CapnMessage::new();
capn_msg.add_segment(seg);
let mut raw_bytes: Vec<u8> = Vec::with_capacity(capn_msg.total_bytes());
for seg_idx in 0..capn_msg.segment_count() {
if let Some(s) = capn_msg.segment(seg_idx) {
for &word in s.words() {
raw_bytes.extend_from_slice(&word.to_le_bytes());
}
}
}
let framed_msg = Message {
segments: vec![raw_bytes],
};
write_frame(&framed_msg)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_capnp_export_empty() {
let doc = new_capnp_export(1);
assert_eq!(capnp_struct_count(&doc), 0);
}
#[test]
fn test_add_struct() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "Vertex");
assert_eq!(capnp_struct_count(&doc), 1);
}
#[test]
fn test_add_field() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "Mesh");
add_capnp_field(&mut doc, "count", "UInt32");
assert_eq!(capnp_last_struct_field_count(&doc), 1);
}
#[test]
fn test_to_capnp_schema_contains_struct() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "MyStruct");
let s = to_capnp_schema(&doc);
assert!(s.contains("struct MyStruct"));
}
#[test]
fn test_to_capnp_schema_contains_field() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "M");
add_capnp_field(&mut doc, "vertices", "UInt32");
let s = to_capnp_schema(&doc);
assert!(s.contains("vertices"));
}
#[test]
fn test_to_capnp_schema_has_file_id() {
let doc = new_capnp_export(0x1234);
let s = to_capnp_schema(&doc);
assert!(s.contains("@0x"));
}
#[test]
fn test_export_mesh_capnp_schema() {
let s = export_mesh_capnp_schema(100, 300);
assert!(s.contains("Mesh"));
assert!(s.contains("vertexCount=100"));
}
#[test]
fn test_find_capnp_struct() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "Bone");
assert!(find_capnp_struct(&doc, "Bone").is_some());
}
#[test]
fn test_find_missing_struct() {
let doc = new_capnp_export(1);
assert!(find_capnp_struct(&doc, "Ghost").is_none());
}
#[test]
fn test_field_index_increments() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "M");
add_capnp_field(&mut doc, "a", "Int32");
add_capnp_field(&mut doc, "b", "Int32");
let st = find_capnp_struct(&doc, "M").expect("should succeed");
assert_eq!(st.fields[1].index, 1);
}
#[test]
fn test_to_capnp_bytes_segment_count_word_is_zero() {
let doc = new_capnp_export(0xdeadbeef_cafebabe);
let bytes = to_capnp_bytes(&doc);
assert!(bytes.len() >= 4, "output must have at least 4 bytes");
let seg_count_minus_one =
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
assert_eq!(seg_count_minus_one, 0, "single-segment framing: count-1 == 0");
}
#[test]
fn test_to_capnp_bytes_non_empty_for_empty_doc() {
let doc = CapnpExport::default();
let bytes = to_capnp_bytes(&doc);
assert!(!bytes.is_empty(), "to_capnp_bytes must produce at least some bytes");
}
#[test]
fn test_to_capnp_bytes_deterministic() {
let mut doc = new_capnp_export(0x1111_2222_3333_4444);
add_capnp_struct(&mut doc, "Bone");
add_capnp_field(&mut doc, "length", "Float32");
let bytes_a = to_capnp_bytes(&doc);
let bytes_b = to_capnp_bytes(&doc);
assert_eq!(bytes_a, bytes_b, "encoding must be deterministic");
}
#[test]
fn test_to_capnp_bytes_root_struct_ptr_kind_bits() {
let doc = new_capnp_export(1);
let bytes = to_capnp_bytes(&doc);
assert!(bytes.len() >= 16, "must have header (8 B) + at least 1 word");
let ptr_word = u64::from_le_bytes(
bytes[8..16].try_into().expect("slice must be exactly 8 bytes"),
);
assert_eq!(ptr_word & 0b11, 0b00, "root pointer kind must be struct (0b00)");
let data_words = ((ptr_word >> 32) & 0xFFFF) as u16;
assert_eq!(data_words, 2, "root struct must have data_words=2");
let pointer_words = (ptr_word >> 48) as u16;
assert_eq!(pointer_words, 0, "root struct must have pointer_words=0");
}
#[test]
fn test_to_capnp_bytes_file_id_word() {
let file_id: u64 = 0xfeed_face_dead_beef;
let doc = new_capnp_export(file_id);
let bytes = to_capnp_bytes(&doc);
assert!(bytes.len() >= 24, "must have at least 3 words + header");
let word1 = u64::from_le_bytes(
bytes[16..24].try_into().expect("slice must be exactly 8 bytes"),
);
assert_eq!(word1, file_id, "data word 0 must equal file_id");
}
#[test]
fn test_to_capnp_bytes_struct_count_word() {
let mut doc = new_capnp_export(1);
add_capnp_struct(&mut doc, "A");
add_capnp_struct(&mut doc, "B");
add_capnp_struct(&mut doc, "C");
let bytes = to_capnp_bytes(&doc);
assert!(bytes.len() >= 32, "must have at least 4 words + header");
let word2 = u64::from_le_bytes(
bytes[24..32].try_into().expect("slice must be exactly 8 bytes"),
);
assert_eq!(word2 & 0xFFFF_FFFF, 3, "low 32 bits of word 2 must be struct count");
}
#[test]
fn test_to_capnp_bytes_zero_file_id_no_structs() {
let doc = CapnpExport::default();
let bytes = to_capnp_bytes(&doc);
assert!(bytes.len() >= 32, "minimum valid output is 32 bytes");
let seg_count_minus_one =
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
assert_eq!(seg_count_minus_one, 0);
let declared_words =
u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as usize;
let actual_words = (bytes.len() - 8) / 8;
assert_eq!(declared_words, actual_words, "declared word count must match body size");
}
#[test]
fn test_mesh_binary_non_empty() {
let positions: &[[f32; 3]] = &[];
let indices: &[u32] = &[];
let bytes = export_mesh_capnp_binary(positions, indices);
assert!(!bytes.is_empty());
}
#[test]
fn test_mesh_binary_single_segment_framing() {
let positions = [[0.0f32, 1.0, 2.0], [3.0, 4.0, 5.0]];
let indices = [0u32, 1, 0];
let bytes = export_mesh_capnp_binary(&positions, &indices);
assert!(bytes.len() >= 4);
let seg_count_minus_one =
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
assert_eq!(seg_count_minus_one, 0);
}
#[test]
fn test_mesh_binary_root_ptr_shape() {
let positions = [[1.0f32, 0.0, 0.0]];
let indices = [0u32];
let bytes = export_mesh_capnp_binary(&positions, &indices);
assert!(bytes.len() >= 16);
let ptr_word = u64::from_le_bytes(
bytes[8..16].try_into().expect("slice 8 bytes"),
);
assert_eq!(ptr_word & 0b11, 0b00, "root ptr must be struct kind");
let data_words = ((ptr_word >> 32) & 0xFFFF) as u16;
assert_eq!(data_words, 0, "no data section in mesh root struct");
let pointer_words = (ptr_word >> 48) as u16;
assert_eq!(pointer_words, 2, "mesh root struct has 2 pointer words");
}
#[test]
fn test_mesh_binary_pos_list_ptr_kind() {
let positions = [[0.5f32, 0.5, 0.5]];
let indices = [0u32];
let bytes = export_mesh_capnp_binary(&positions, &indices);
assert!(bytes.len() >= 24);
let ptr_word = u64::from_le_bytes(
bytes[16..24].try_into().expect("slice 8 bytes"),
);
assert_eq!(ptr_word & 0b11, 0b01, "positions list pointer kind must be 0b01");
}
#[test]
fn test_mesh_binary_idx_list_ptr_kind() {
let positions = [[0.0f32, 0.0, 0.0]];
let indices = [0u32, 1, 2];
let bytes = export_mesh_capnp_binary(&positions, &indices);
assert!(bytes.len() >= 32);
let ptr_word = u64::from_le_bytes(
bytes[24..32].try_into().expect("slice 8 bytes"),
);
assert_eq!(ptr_word & 0b11, 0b01, "indices list pointer kind must be 0b01");
}
#[test]
fn test_mesh_binary_empty_mesh_element_counts() {
let bytes = export_mesh_capnp_binary(&[], &[]);
assert!(bytes.len() >= 32);
let pos_ptr = u64::from_le_bytes(bytes[16..24].try_into().expect("8 bytes"));
let pos_count = ((pos_ptr >> 32) >> 3) & 0x1FFF_FFFF;
assert_eq!(pos_count, 0, "empty positions list must have element_count=0");
let idx_ptr = u64::from_le_bytes(bytes[24..32].try_into().expect("8 bytes"));
let idx_count = ((idx_ptr >> 32) >> 3) & 0x1FFF_FFFF;
assert_eq!(idx_count, 0, "empty indices list must have element_count=0");
}
#[test]
fn test_mesh_binary_positions_element_count() {
let positions = [[1.0f32, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
let indices: &[u32] = &[];
let bytes = export_mesh_capnp_binary(&positions, indices);
assert!(bytes.len() >= 24);
let pos_ptr = u64::from_le_bytes(bytes[16..24].try_into().expect("8 bytes"));
let pos_count = ((pos_ptr >> 32) >> 3) & 0x1FFF_FFFF;
assert_eq!(
pos_count as usize,
positions.len() * 3,
"element_count must be vertex_count * 3"
);
}
#[test]
fn test_mesh_binary_indices_element_count() {
let positions: &[[f32; 3]] = &[];
let indices = [10u32, 20, 30, 40, 50, 60];
let bytes = export_mesh_capnp_binary(positions, &indices);
assert!(bytes.len() >= 32);
let idx_ptr = u64::from_le_bytes(bytes[24..32].try_into().expect("8 bytes"));
let idx_count = ((idx_ptr >> 32) >> 3) & 0x1FFF_FFFF;
assert_eq!(idx_count as usize, indices.len(), "element_count must be indices.len()");
}
#[test]
fn test_mesh_binary_deterministic() {
let positions = [[0.1f32, 0.2, 0.3], [1.0, 2.0, 3.0]];
let indices = [0u32, 1, 2];
let a = export_mesh_capnp_binary(&positions, &indices);
let b = export_mesh_capnp_binary(&positions, &indices);
assert_eq!(a, b, "encoding must be deterministic");
}
#[test]
fn test_mesh_binary_declared_word_count_matches_body() {
let positions = [[0.0f32, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]];
let indices = [0u32, 1, 2, 0, 2, 3];
let bytes = export_mesh_capnp_binary(&positions, &indices);
assert!(bytes.len() >= 8);
let declared_words =
u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as usize;
let body_bytes = bytes.len() - 8;
assert_eq!(
body_bytes % 8,
0,
"body must be word-aligned"
);
assert_eq!(
declared_words,
body_bytes / 8,
"declared word count must match actual segment body"
);
}
}