use justcode_core::{config, Decode, Encode};
use std::mem;
trait MemorySize {
fn memory_size(&self) -> usize;
}
impl MemorySize for u8 {
fn memory_size(&self) -> usize {
mem::size_of::<u8>()
}
}
impl MemorySize for u16 {
fn memory_size(&self) -> usize {
mem::size_of::<u16>()
}
}
impl MemorySize for u32 {
fn memory_size(&self) -> usize {
mem::size_of::<u32>()
}
}
impl MemorySize for u64 {
fn memory_size(&self) -> usize {
mem::size_of::<u64>()
}
}
impl MemorySize for i8 {
fn memory_size(&self) -> usize {
mem::size_of::<i8>()
}
}
impl MemorySize for i16 {
fn memory_size(&self) -> usize {
mem::size_of::<i16>()
}
}
impl MemorySize for i32 {
fn memory_size(&self) -> usize {
mem::size_of::<i32>()
}
}
impl MemorySize for i64 {
fn memory_size(&self) -> usize {
mem::size_of::<i64>()
}
}
impl MemorySize for f32 {
fn memory_size(&self) -> usize {
mem::size_of::<f32>()
}
}
impl MemorySize for f64 {
fn memory_size(&self) -> usize {
mem::size_of::<f64>()
}
}
impl MemorySize for bool {
fn memory_size(&self) -> usize {
mem::size_of::<bool>()
}
}
impl MemorySize for char {
fn memory_size(&self) -> usize {
mem::size_of::<char>()
}
}
impl<T> MemorySize for Vec<T>
where
T: MemorySize,
{
fn memory_size(&self) -> usize {
let vec_overhead = mem::size_of::<Vec<T>>();
let data_size = self.capacity() * mem::size_of::<T>();
let element_extra_heap: usize = self
.iter()
.map(|e| {
let element_total = e.memory_size();
let element_stack = mem::size_of::<T>();
element_total.saturating_sub(element_stack)
})
.sum();
vec_overhead + data_size + element_extra_heap
}
}
impl MemorySize for String {
fn memory_size(&self) -> usize {
let string_overhead = mem::size_of::<String>();
let data_size = self.capacity();
string_overhead + data_size
}
}
impl<T> MemorySize for Option<T>
where
T: MemorySize,
{
fn memory_size(&self) -> usize {
match self {
Some(value) => mem::size_of::<Option<T>>() + value.memory_size(),
None => mem::size_of::<Option<T>>(),
}
}
}
#[derive(Encode, Decode, PartialEq, Debug, Clone)]
struct Point {
x: f32,
y: f32,
}
impl MemorySize for Point {
fn memory_size(&self) -> usize {
mem::size_of::<Point>()
}
}
#[derive(Encode, Decode, PartialEq, Debug, Clone)]
struct Entity {
id: u32,
position: Point,
health: f32,
active: bool,
}
impl MemorySize for Entity {
fn memory_size(&self) -> usize {
mem::size_of::<Entity>()
}
}
#[derive(Encode, Decode, PartialEq, Debug, Clone)]
struct World {
entities: Vec<Entity>,
name: String,
}
impl MemorySize for World {
fn memory_size(&self) -> usize {
let struct_size = mem::size_of::<World>();
let entities_size = self.entities.memory_size();
let name_size = self.name.memory_size();
struct_size + entities_size + name_size - mem::size_of::<Vec<Entity>>() - mem::size_of::<String>()
}
}
fn verify_size_claim<T: Encode + MemorySize>(value: &T, name: &str, config: config::Config) {
let memory_size = value.memory_size();
let encoded = justcode_core::encode_to_vec(value, config).unwrap();
let encoded_size = encoded.len();
println!(
" {}: memory={} bytes, encoded={} bytes, ratio={:.2}%",
name,
memory_size,
encoded_size,
(encoded_size as f64 / memory_size as f64) * 100.0
);
assert!(
encoded_size <= memory_size,
"Encoded size ({}) exceeds memory size ({}) for {}",
encoded_size,
memory_size,
name
);
}
#[test]
fn test_primitives_memory_size() {
println!("\n=== Testing Primitives ===");
let config = config::standard();
verify_size_claim(&42u8, "u8", config);
verify_size_claim(&42u16, "u16", config);
verify_size_claim(&42u32, "u32", config);
verify_size_claim(&42u64, "u64", config);
verify_size_claim(&42i8, "i8", config);
verify_size_claim(&42i16, "i16", config);
verify_size_claim(&42i32, "i32", config);
verify_size_claim(&42i64, "i64", config);
verify_size_claim(&3.14f32, "f32", config);
verify_size_claim(&3.14f64, "f64", config);
verify_size_claim(&true, "bool", config);
verify_size_claim(&'A', "char", config);
}
#[test]
fn test_vec_primitives_memory_size() {
println!("\n=== Testing Vec of Primitives ===");
let config = config::standard();
let empty_vec: Vec<u32> = vec![];
verify_size_claim(&empty_vec, "Vec<u32> (empty)", config);
let small_vec = vec![1u32, 2, 3, 4, 5];
verify_size_claim(&small_vec, "Vec<u32> (5 elements)", config);
let medium_vec: Vec<u32> = (0..100).collect();
verify_size_claim(&medium_vec, "Vec<u32> (100 elements)", config);
let large_vec: Vec<u32> = (0..1000).collect();
verify_size_claim(&large_vec, "Vec<u32> (1000 elements)", config);
let vec_u8: Vec<u8> = (0..255).collect();
verify_size_claim(&vec_u8, "Vec<u8> (255 elements)", config);
}
#[test]
fn test_string_memory_size() {
println!("\n=== Testing String ===");
let config = config::standard();
let empty_string = String::new();
verify_size_claim(&empty_string, "String (empty)", config);
let small_string = "Hello".to_string();
verify_size_claim(&small_string, "String (small)", config);
let medium_string = "Hello, World! This is a medium-sized string.".to_string();
verify_size_claim(&medium_string, "String (medium)", config);
let large_string = "A".repeat(1000);
verify_size_claim(&large_string, "String (1000 chars)", config);
}
#[test]
fn test_option_memory_size() {
println!("\n=== Testing Option ===");
let config = config::standard();
let some_u32 = Some(42u32);
verify_size_claim(&some_u32, "Option<u32> (Some)", config);
let none_u32: Option<u32> = None;
verify_size_claim(&none_u32, "Option<u32> (None)", config);
let some_string = Some("Hello".to_string());
verify_size_claim(&some_string, "Option<String> (Some)", config);
let none_string: Option<String> = None;
verify_size_claim(&none_string, "Option<String> (None)", config);
}
#[test]
fn test_structs_memory_size() {
println!("\n=== Testing Structs ===");
let config = config::standard();
let point = Point { x: 1.0, y: 2.0 };
verify_size_claim(&point, "Point", config);
let entity = Entity {
id: 1,
position: Point { x: 0.0, y: 4.0 },
health: 100.0,
active: true,
};
verify_size_claim(&entity, "Entity", config);
let world = World {
entities: vec![
Entity {
id: 1,
position: Point { x: 0.0, y: 4.0 },
health: 100.0,
active: true,
},
Entity {
id: 2,
position: Point { x: 10.0, y: 20.5 },
health: 75.5,
active: false,
},
],
name: "Test World".to_string(),
};
verify_size_claim(&world, "World (2 entities)", config);
let large_world = World {
entities: (0..100)
.map(|i| Entity {
id: i,
position: Point {
x: i as f32,
y: (i * 2) as f32,
},
health: 100.0,
active: i % 2 == 0,
})
.collect(),
name: "Large World".to_string(),
};
verify_size_claim(&large_world, "World (100 entities)", config);
}
#[test]
fn test_nested_collections_memory_size() {
println!("\n=== Testing Nested Collections ===");
let config = config::standard();
let vec_of_strings = vec![
"Hello".to_string(),
"World".to_string(),
"Test".to_string(),
];
verify_size_claim(&vec_of_strings, "Vec<String> (3 elements)", config);
let vec_of_vecs: Vec<Vec<u32>> = vec![
vec![1, 2, 3],
vec![4, 5, 6],
vec![7, 8, 9],
];
verify_size_claim(&vec_of_vecs, "Vec<Vec<u32>> (3x3)", config);
let vec_of_options: Vec<Option<String>> = vec![
Some("Hello".to_string()),
None,
Some("World".to_string()),
];
verify_size_claim(&vec_of_options, "Vec<Option<String>> (3 elements)", config);
}
#[test]
fn test_comprehensive_memory_verification() {
println!("\n=== Comprehensive Memory Verification ===");
let config = config::standard();
let u32_val = 42u32;
verify_size_claim(&u32_val, "u32 (comprehensive)", config);
let vec_val = vec![1u32, 2, 3];
verify_size_claim(&vec_val, "Vec<u32> (3, comprehensive)", config);
let string_val = "Hello".to_string();
verify_size_claim(&string_val, "String (5, comprehensive)", config);
let option_val = Some(42u32);
verify_size_claim(&option_val, "Option<u32> (Some, comprehensive)", config);
}
#[test]
fn test_varint_encoding_benefit() {
println!("\n=== Testing Varint Encoding Benefit ===");
let config_with_varint = config::standard().with_variable_int_encoding(true);
let config_without_varint = config::standard().with_variable_int_encoding(false);
let small_vec: Vec<u32> = (0..10).collect();
let encoded_with = justcode_core::encode_to_vec(&small_vec, config_with_varint).unwrap();
let encoded_without = justcode_core::encode_to_vec(&small_vec, config_without_varint).unwrap();
println!(
" Vec<u32> (10 elements): with_varint={} bytes, without_varint={} bytes",
encoded_with.len(),
encoded_without.len()
);
assert!(encoded_with.len() <= encoded_without.len());
}