use cyclone_runtime::{DecodeError, Limits, Reader, Writer};
macro_rules! vector {
($value:expr, $expected:expr, $write:ident, $read:ident) => {{
let value = $value;
let expected: &[u8] = $expected;
let mut writer = Writer::new();
writer.$write(value);
assert_eq!(writer.as_slice(), expected, "encode {value:?}");
let bytes = writer.into_bytes();
let mut reader = Reader::new(&bytes);
let decoded = reader.$read().expect("decode");
assert_eq!(decoded, value, "round-trip {value:?}");
assert!(reader.is_empty(), "cursor must land exactly at the end");
}};
}
#[test]
fn p_001_002_bool() {
vector!(false, &[0x00], write_bool, read_bool);
vector!(true, &[0x01], write_bool, read_bool);
}
#[test]
fn p_010_015_one_byte_integers() {
vector!(0u8, &[0x00], write_u8, read_u8);
vector!(255u8, &[0xFF], write_u8, read_u8);
vector!(0i8, &[0x00], write_i8, read_i8);
vector!(-1i8, &[0xFF], write_i8, read_i8);
vector!(-128i8, &[0x80], write_i8, read_i8);
vector!(127i8, &[0x7F], write_i8, read_i8);
}
#[test]
fn p_020_025_two_byte_integers() {
vector!(0u16, &[0x00, 0x00], write_u16, read_u16);
vector!(300u16, &[0x2C, 0x01], write_u16, read_u16);
vector!(65535u16, &[0xFF, 0xFF], write_u16, read_u16);
vector!(-1i16, &[0xFF, 0xFF], write_i16, read_i16);
vector!(-32768i16, &[0x00, 0x80], write_i16, read_i16);
vector!(32767i16, &[0xFF, 0x7F], write_i16, read_i16);
}
#[test]
fn p_030_036_four_byte_integers() {
vector!(0u32, &[0x00, 0x00, 0x00, 0x00], write_u32, read_u32);
vector!(100u32, &[0x64, 0x00, 0x00, 0x00], write_u32, read_u32);
vector!(0x12345678u32, &[0x78, 0x56, 0x34, 0x12], write_u32, read_u32);
vector!(4294967295u32, &[0xFF, 0xFF, 0xFF, 0xFF], write_u32, read_u32);
vector!(-1i32, &[0xFF, 0xFF, 0xFF, 0xFF], write_i32, read_i32);
vector!(i32::MIN, &[0x00, 0x00, 0x00, 0x80], write_i32, read_i32);
vector!(i32::MAX, &[0xFF, 0xFF, 0xFF, 0x7F], write_i32, read_i32);
}
#[test]
fn p_040_045_eight_byte_integers() {
vector!(0u64, &[0x00; 8], write_u64, read_u64);
vector!(1u64, &[0x01, 0, 0, 0, 0, 0, 0, 0], write_u64, read_u64);
vector!(u64::MAX, &[0xFF; 8], write_u64, read_u64);
vector!(-1i64, &[0xFF; 8], write_i64, read_i64);
vector!(i64::MIN, &[0, 0, 0, 0, 0, 0, 0, 0x80], write_i64, read_i64);
vector!(i64::MAX, &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F], write_i64, read_i64);
}
fn f32_vector(bits: u32, expected: &[u8]) {
let value = f32::from_bits(bits);
let mut writer = Writer::new();
writer.write_f32(value);
assert_eq!(writer.as_slice(), expected, "encode f32 0x{bits:08X}");
let bytes = writer.into_bytes();
let decoded = Reader::new(&bytes).read_f32().expect("decode f32");
assert_eq!(decoded.to_bits(), bits, "round-trip f32 0x{bits:08X}");
}
fn f64_vector(bits: u64, expected: &[u8]) {
let value = f64::from_bits(bits);
let mut writer = Writer::new();
writer.write_f64(value);
assert_eq!(writer.as_slice(), expected, "encode f64 0x{bits:016X}");
let bytes = writer.into_bytes();
let decoded = Reader::new(&bytes).read_f64().expect("decode f64");
assert_eq!(decoded.to_bits(), bits, "round-trip f64 0x{bits:016X}");
}
#[test]
fn f_001_007_f32() {
f32_vector(0x00000000, &[0x00, 0x00, 0x00, 0x00]);
f32_vector(0x80000000, &[0x00, 0x00, 0x00, 0x80]);
f32_vector(0x3F800000, &[0x00, 0x00, 0x80, 0x3F]);
f32_vector(0x3FC00000, &[0x00, 0x00, 0xC0, 0x3F]);
f32_vector(0x4048F5C3, &[0xC3, 0xF5, 0x48, 0x40]);
f32_vector(0x7F800000, &[0x00, 0x00, 0x80, 0x7F]);
f32_vector(0xFF800000, &[0x00, 0x00, 0x80, 0xFF]);
}
#[test]
fn f_020_023_f64() {
f64_vector(0x0000000000000000, &[0, 0, 0, 0, 0, 0, 0, 0]);
f64_vector(0x8000000000000000, &[0, 0, 0, 0, 0, 0, 0, 0x80]);
f64_vector(0x3FF0000000000000, &[0, 0, 0, 0, 0, 0, 0xF0, 0x3F]);
f64_vector(0x7FF0000000000000, &[0, 0, 0, 0, 0, 0, 0xF0, 0x7F]);
}
#[test]
fn f_030_032_nan_bit_patterns_preserved() {
f32_vector(0x7FC00000, &[0x00, 0x00, 0xC0, 0x7F]);
f32_vector(0x7FC00001, &[0x01, 0x00, 0xC0, 0x7F]);
f32_vector(0xFFC00000, &[0x00, 0x00, 0xC0, 0xFF]);
}
#[test]
fn f_040_041_negative_zero_is_not_zero() {
let mut positive = Writer::new();
positive.write_f32(0.0);
let mut negative = Writer::new();
negative.write_f32(-0.0);
assert_ne!(positive.as_slice(), negative.as_slice());
let bytes = negative.into_bytes();
let decoded = Reader::new(&bytes).read_f32().expect("decode");
assert_eq!(decoded.to_bits(), (-0.0f32).to_bits());
assert!(decoded.is_sign_negative());
}
#[test]
fn s_001_005_string() {
let cases: &[(&str, &[u8])] = &[
("", &[0x00, 0x00, 0x00, 0x00]),
("abc", &[0x03, 0x00, 0x00, 0x00, 0x61, 0x62, 0x63]),
("Alice", &[0x05, 0x00, 0x00, 0x00, 0x41, 0x6C, 0x69, 0x63, 0x65]),
("中", &[0x03, 0x00, 0x00, 0x00, 0xE4, 0xB8, 0xAD]),
(
"Xin chào",
&[0x09, 0x00, 0x00, 0x00, 0x58, 0x69, 0x6E, 0x20, 0x63, 0x68, 0xC3, 0xA0, 0x6F],
),
];
for (value, expected) in cases {
let mut writer = Writer::new();
writer.write_string(value);
assert_eq!(writer.as_slice(), *expected, "encode {value:?}");
let bytes = writer.into_bytes();
let mut reader = Reader::new(&bytes);
assert_eq!(reader.read_string().expect("decode"), *value);
assert!(reader.is_empty());
}
}
#[test]
fn r_005_empty_string_round_trips() {
let mut writer = Writer::new();
writer.write_string("");
let bytes = writer.into_bytes();
assert_eq!(Reader::new(&bytes).read_string().expect("decode"), "");
}
#[test]
fn b_001_003_bytes() {
let cases: &[(&[u8], &[u8])] = &[
(&[], &[0x00, 0x00, 0x00, 0x00]),
(&[0xFF, 0xFE], &[0x02, 0x00, 0x00, 0x00, 0xFF, 0xFE]),
(&[0x00, 0x00], &[0x02, 0x00, 0x00, 0x00, 0x00, 0x00]),
];
for (value, expected) in cases {
let mut writer = Writer::new();
writer.write_bytes(value);
assert_eq!(writer.as_slice(), *expected, "encode {value:02X?}");
let bytes = writer.into_bytes();
let mut reader = Reader::new(&bytes);
assert_eq!(reader.read_bytes().expect("decode"), *value);
assert!(reader.is_empty());
}
}
fn encode_u32_array(values: &[u32]) -> Vec<u8> {
let mut writer = Writer::new();
writer.write_array_count(values.len());
for &value in values {
writer.write_u32(value);
}
writer.into_bytes()
}
fn decode_u32_array(reader: &mut Reader<'_>) -> Result<Vec<u32>, DecodeError> {
let count = reader.read_array_count()?;
let mut values = Vec::new();
for _ in 0..count {
values.push(reader.read_u32()?);
}
Ok(values)
}
#[test]
fn a_001_002_array_of_u32() {
assert_eq!(encode_u32_array(&[]), [0x00, 0x00, 0x00, 0x00]);
assert_eq!(
encode_u32_array(&[1, 2, 3]),
[0x03, 0x00, 0x00, 0x00, 0x01, 0, 0, 0, 0x02, 0, 0, 0, 0x03, 0, 0, 0]
);
let bytes = encode_u32_array(&[1, 2, 3]);
let mut reader = Reader::new(&bytes);
assert_eq!(decode_u32_array(&mut reader).expect("decode"), vec![1, 2, 3]);
assert!(reader.is_empty());
}
#[test]
fn a_003_array_of_string() {
let values = ["a", "bc"];
let mut writer = Writer::new();
writer.write_array_count(values.len());
for value in values {
writer.write_string(value);
}
assert_eq!(
writer.as_slice(),
&[0x02, 0, 0, 0, 0x01, 0, 0, 0, 0x61, 0x02, 0, 0, 0, 0x62, 0x63]
);
}
#[test]
fn a_004_nested_array() {
let values: [&[u8]; 2] = [&[1, 2], &[]];
let mut writer = Writer::new();
writer.write_array_count(values.len());
for inner in values {
writer.write_array_count(inner.len());
for &byte in inner {
writer.write_u8(byte);
}
}
assert_eq!(writer.as_slice(), &[0x02, 0, 0, 0, 0x02, 0, 0, 0, 0x01, 0x02, 0x00, 0, 0, 0]);
}
#[test]
fn a_005_array_of_bool() {
let values = [true, false, true];
let mut writer = Writer::new();
writer.write_array_count(values.len());
for value in values {
writer.write_bool(value);
}
assert_eq!(writer.as_slice(), &[0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01]);
}
#[test]
fn r_004_empty_array_round_trips() {
let bytes = encode_u32_array(&[]);
let mut reader = Reader::new(&bytes);
assert_eq!(decode_u32_array(&mut reader).expect("decode"), Vec::<u32>::new());
assert!(reader.is_empty());
}
#[test]
fn t_001_basic_model() {
let mut writer = Writer::new();
writer.write_u32(42);
writer.write_string("Sword");
assert_eq!(
writer.as_slice(),
&[0x2A, 0, 0, 0, 0x05, 0, 0, 0, 0x53, 0x77, 0x6F, 0x72, 0x64]
);
}
#[test]
fn t_002_empty_model() {
let writer = Writer::new();
assert_eq!(writer.as_slice(), &[] as &[u8]);
assert!(writer.is_empty());
}
#[test]
fn t_003_004_nested_model() {
fn encode_vector3(writer: &mut Writer, xyz: [f32; 3]) {
for component in xyz {
writer.write_f32(component);
}
}
let mut writer = Writer::new();
encode_vector3(&mut writer, [1.5, 2.5, 3.5]);
assert_eq!(writer.as_slice(), &[0, 0, 0xC0, 0x3F, 0, 0, 0x20, 0x40, 0, 0, 0x60, 0x40]);
let mut writer = Writer::new();
encode_vector3(&mut writer, [1.5, 2.5, 3.5]);
writer.write_u32(100);
assert_eq!(
writer.as_slice(),
&[0, 0, 0xC0, 0x3F, 0, 0, 0x20, 0x40, 0, 0, 0x60, 0x40, 0x64, 0, 0, 0]
);
}
#[test]
fn t_010_no_padding_no_alignment() {
let mut writer = Writer::new();
writer.write_u8(1);
writer.write_u32(2);
writer.write_u8(3);
assert_eq!(writer.as_slice(), &[0x01, 0x02, 0x00, 0x00, 0x00, 0x03]);
assert_eq!(writer.len(), 6);
}
#[test]
fn t_011_declaration_order() {
let mut writer = Writer::new();
writer.write_u8(1); writer.write_u8(2); assert_eq!(writer.as_slice(), &[0x01, 0x02]);
}
#[test]
fn e_001_010_enum_is_always_u32() {
for (value, expected) in [
(0u32, [0x00, 0x00, 0x00, 0x00]),
(1, [0x01, 0x00, 0x00, 0x00]),
(3, [0x03, 0x00, 0x00, 0x00]),
] {
let mut writer = Writer::new();
writer.write_u32(value);
assert_eq!(writer.as_slice(), expected);
assert_eq!(writer.len(), 4);
}
}
#[test]
fn n_001_002_reject_invalid_bool() {
assert_eq!(Reader::new(&[0x02]).read_bool(), Err(DecodeError::InvalidBool(0x02)));
assert_eq!(Reader::new(&[0xFF]).read_bool(), Err(DecodeError::InvalidBool(0xFF)));
}
#[test]
fn n_010_011_reject_truncated_integers() {
assert_eq!(
Reader::new(&[0x00, 0x00, 0x00]).read_u32(),
Err(DecodeError::UnexpectedEof { needed: 4, remaining: 3 })
);
assert_eq!(
Reader::new(&[0x01, 0x02, 0x03, 0x04]).read_u64(),
Err(DecodeError::UnexpectedEof { needed: 8, remaining: 4 })
);
}
#[test]
fn n_020_021_reject_invalid_utf8() {
assert_eq!(
Reader::new(&[0x02, 0x00, 0x00, 0x00, 0xFF, 0xFE]).read_string(),
Err(DecodeError::InvalidUtf8)
);
assert_eq!(
Reader::new(&[0x03, 0x00, 0x00, 0x00, 0xED, 0xA0, 0x80]).read_string(),
Err(DecodeError::InvalidUtf8)
);
}
#[test]
fn n_022_023_reject_string_length_past_end() {
assert_eq!(
Reader::new(&[0x05, 0x00, 0x00, 0x00, 0x41, 0x6C, 0x69]).read_string(),
Err(DecodeError::UnexpectedEof { needed: 5, remaining: 3 })
);
assert_eq!(
Reader::new(&[0x64, 0x00, 0x00, 0x00, 0x41, 0x6C, 0x69, 0x63, 0x65]).read_string(),
Err(DecodeError::UnexpectedEof { needed: 100, remaining: 5 })
);
}
#[test]
fn n_030_reject_array_with_missing_elements() {
let bytes = [0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
let mut reader = Reader::new(&bytes);
assert_eq!(
decode_u32_array(&mut reader),
Err(DecodeError::UnexpectedEof { needed: 4, remaining: 0 })
);
}
#[test]
fn n_031_reject_huge_array_count_without_allocating() {
let bytes = [0xFF, 0xFF, 0xFF, 0xFF];
let mut reader = Reader::new(&bytes);
assert_eq!(
decode_u32_array(&mut reader),
Err(DecodeError::UnexpectedEof { needed: 4, remaining: 0 })
);
let limits = Limits { max_array_count: 100_000, ..Limits::UNLIMITED };
let mut reader = Reader::with_limits(&bytes, limits);
assert_eq!(
reader.read_array_count(),
Err(DecodeError::LengthOverflow { length: 4294967295, limit: 100_000 })
);
}
#[test]
fn configured_limits_reject_before_allocation() {
let mut bytes = vec![0x10, 0x00, 0x00, 0x00];
bytes.extend_from_slice(b"0123456789abcdef");
let limits = Limits { max_string_len: 8, ..Limits::UNLIMITED };
assert_eq!(
Reader::with_limits(&bytes, limits).read_string(),
Err(DecodeError::LengthOverflow { length: 16, limit: 8 })
);
let limits = Limits { max_bytes_len: 8, ..Limits::UNLIMITED };
assert_eq!(
Reader::with_limits(&bytes, limits).read_bytes(),
Err(DecodeError::LengthOverflow { length: 16, limit: 8 })
);
assert_eq!(Reader::new(&bytes).read_string().expect("decode"), "0123456789abcdef");
}
#[test]
fn remaining_bytes_bound_holds_under_any_configuration() {
let bytes = [0x64, 0x00, 0x00, 0x00, 0x41];
let limits = Limits { max_string_len: usize::MAX, ..Limits::UNLIMITED };
assert_eq!(
Reader::with_limits(&bytes, limits).read_string(),
Err(DecodeError::UnexpectedEof { needed: 100, remaining: 1 })
);
}
#[test]
fn failed_reads_do_not_move_the_cursor() {
let bytes = [0x02, 0x00, 0x00, 0x00, 0xFF, 0xFE];
let mut reader = Reader::new(&bytes);
assert!(reader.read_string().is_err());
assert_eq!(reader.position(), 0);
assert_eq!(reader.read_bytes().expect("decode"), vec![0xFF, 0xFE]);
let mut reader = Reader::new(&[0x02]);
assert!(reader.read_bool().is_err());
assert_eq!(reader.position(), 0);
let mut reader = Reader::new(&[0x00, 0x00, 0x00]);
assert!(reader.read_u32().is_err());
assert_eq!(reader.position(), 0);
}
#[test]
fn position_and_remaining_track_the_cursor() {
let bytes = [0x01, 0x02, 0x00, 0x00, 0x00, 0x03];
let mut reader = Reader::new(&bytes);
assert_eq!((reader.position(), reader.remaining()), (0, 6));
reader.read_u8().expect("decode");
assert_eq!((reader.position(), reader.remaining()), (1, 5));
reader.read_u32().expect("decode");
assert_eq!((reader.position(), reader.remaining()), (5, 1));
reader.read_u8().expect("decode");
assert!(reader.is_empty());
}
#[test]
fn writer_clear_keeps_capacity_and_drops_content() {
let mut writer = Writer::with_capacity(64);
writer.write_u32(100);
assert_eq!(writer.len(), 4);
writer.clear();
assert!(writer.is_empty());
assert_eq!(writer.as_slice(), &[] as &[u8]);
writer.write_u32(100);
assert_eq!(writer.as_slice(), &[0x64, 0x00, 0x00, 0x00]);
}
#[test]
fn encoding_is_deterministic() {
let encode = || {
let mut writer = Writer::new();
writer.write_u32(42);
writer.write_string("Knight");
writer.write_f32(10.5);
writer.write_bool(true);
writer.into_bytes()
};
assert_eq!(encode(), encode());
}
#[test]
fn rfc_0002_complete_example() {
let mut writer = Writer::new();
writer.write_u32(42);
writer.write_string("Knight");
writer.write_f32(10.5);
writer.write_f32(20.3);
writer.write_f32(-5.1);
writer.write_u32(100);
writer.write_bool(true);
assert_eq!(
writer.as_slice(),
&[
0x2A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x6E, 0x69, 0x67, 0x68, 0x74, 0x00, 0x00, 0x28, 0x41, 0x66, 0x66, 0xA2, 0x41, 0x33, 0x33, 0xA3, 0xC0, 0x64, 0x00, 0x00, 0x00, 0x01,
]
);
let bytes = writer.into_bytes();
let mut reader = Reader::new(&bytes);
assert_eq!(reader.read_u32().expect("decode"), 42);
assert_eq!(reader.read_string().expect("decode"), "Knight");
assert_eq!(reader.read_f32().expect("decode"), 10.5);
assert_eq!(reader.read_f32().expect("decode"), 20.3);
assert_eq!(reader.read_f32().expect("decode"), -5.1);
assert_eq!(reader.read_u32().expect("decode"), 100);
assert!(reader.read_bool().expect("decode"));
assert!(reader.is_empty());
}