#![allow(deprecated)]
pub const VSF_VERSION: usize = 7;
pub const VSF_BACKWARD_COMPAT: usize = 7;
pub mod types;
pub mod encoding;
pub mod decoding;
pub mod builders;
#[cfg(feature = "text")]
pub mod text_encoding;
pub mod file_format;
pub mod vsf_builder;
pub mod schema;
pub mod crypto_algorithms;
pub mod verification;
pub mod handle;
#[cfg(feature = "crypto")]
pub mod decrypt;
pub mod colour;
pub mod themes;
#[cfg(feature = "inspect")]
pub mod inspect;
pub use types::{
datetime_to_eagle_time, eagle_time_nanos, eagle_time_oscillations, EagleTime, EtType,
LayoutOrder, StridedTensor, Tensor, VsfType, WorldCoord,
};
pub use colour::convert::{ColourFormat, RgbLinearF32, RgbaLinearF32};
pub use encoding::{EncodeNumber, EncodeNumberInclusive};
pub use decoding::parse;
pub use file_format::{validate_name, HeaderField, VsfField, VsfHeader, VsfSection};
pub use vsf_builder::{SectionMeta, VsfBuilder};
pub use builders::{
build_raw_image,
lumis_raw_capture,
parse_raw_image,
Aperture,
BlackLevel,
CalibrationHash,
CameraBuilder,
CameraSettings,
CfaPattern,
ExposureCompensation,
FlashFired,
FocalLength,
FocusDistance,
IsoSpeed,
LensBuilder,
LensInfo,
Magic9,
Manufacturer,
MeteringMode,
ModelName,
ParsedRawImage,
RawImageBuilder,
RawMetadata,
RawMetadataBuilder,
SerialNumber,
ShutterTime,
WhiteLevel,
};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tensor_creation() {
let tensor = Tensor::new(vec![3, 4], vec![0u8; 12]);
assert_eq!(tensor.len(), 12);
assert_eq!(tensor.ndim(), 2);
assert_eq!(tensor.shape, vec![3, 4]);
assert!(!tensor.is_empty());
}
#[test]
fn test_strided_tensor_contiguous() {
let row_major = StridedTensor::new(vec![3, 4], vec![4, 1], vec![0u8; 12]);
assert!(row_major.is_contiguous());
let col_major = StridedTensor::new(vec![3, 4], vec![1, 3], vec![0u8; 12]);
assert!(!col_major.is_contiguous());
}
#[test]
fn test_tensor_dimensions() {
let t1d = Tensor::new(vec![100], vec![0u32; 100]);
assert_eq!(t1d.ndim(), 1);
let t2d = Tensor::new(vec![10, 20], vec![0u16; 200]);
assert_eq!(t2d.ndim(), 2);
let t3d = Tensor::new(vec![5, 10, 20], vec![0u8; 1000]);
assert_eq!(t3d.ndim(), 3);
let t4d = Tensor::new(vec![2, 3, 4, 5], vec![0f32; 120]);
assert_eq!(t4d.ndim(), 4);
}
#[test]
#[should_panic(expected = "Data length 10 doesn't match shape")]
fn test_tensor_size_validation() {
Tensor::new(vec![3, 4], vec![0u8; 10]);
}
#[test]
fn test_roundtrip_unsigned() {
let val = VsfType::u0(true);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
assert_eq!(ptr, flat.len());
if let VsfType::u0(v) = parsed {
assert_eq!(v, true);
} else {
panic!("Expected u0");
}
let val = VsfType::u3(42);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
assert_eq!(ptr, flat.len());
if let VsfType::u3(v) = parsed {
assert_eq!(v, 42);
} else {
panic!("Expected u3");
}
let val = VsfType::u4(1000);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::u4(v) = parsed {
assert_eq!(v, 1000);
} else {
panic!("Expected u4");
}
let val = VsfType::u5(100000);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::u5(v) = parsed {
assert_eq!(v, 100000);
} else {
panic!("Expected u5");
}
}
#[test]
fn test_roundtrip_signed() {
let val = VsfType::i3(-42);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::i3(v) = parsed {
assert_eq!(v, -42);
} else {
panic!("Expected i3");
}
let val = VsfType::i5(-100000);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::i5(v) = parsed {
assert_eq!(v, -100000);
} else {
panic!("Expected i5");
}
}
#[test]
fn test_roundtrip_float() {
let val = VsfType::f5(3.14159);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::f5(v) = parsed {
assert!((v - 3.14159).abs() < 0.00001);
} else {
panic!("Expected f5");
}
let val = VsfType::f6(2.718281828459045);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::f6(v) = parsed {
assert!((v - 2.718281828459045).abs() < 1e-15);
} else {
panic!("Expected f6");
}
}
#[test]
fn test_roundtrip_complex() {
use num_complex::Complex;
let val = VsfType::j5(Complex::new(1.0f32, 2.0f32));
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::j5(v) = parsed {
assert!((v.re - 1.0).abs() < 0.00001);
assert!((v.im - 2.0).abs() < 0.00001);
} else {
panic!("Expected j5");
}
let val = VsfType::j6(Complex::new(3.14, -2.71));
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::j6(v) = parsed {
assert!((v.re - 3.14).abs() < 1e-15);
assert!((v.im + 2.71).abs() < 1e-15);
} else {
panic!("Expected j6");
}
}
#[test]
fn test_roundtrip_string() {
let val = VsfType::x("Hello, VSF!".to_string());
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::x(v) = parsed {
assert_eq!(v, "Hello, VSF!");
} else {
panic!("Expected x");
}
}
#[test]
fn test_roundtrip_metadata() {
let val = VsfType::l("test_label".to_string());
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::l(v) = parsed {
assert_eq!(v, "test_label");
} else {
panic!("Expected l");
}
let val = VsfType::o(1024);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::o(v) = parsed {
assert_eq!(v, 1024);
} else {
panic!("Expected o");
}
let val = VsfType::z(42);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::z(v) = parsed {
assert_eq!(v, 42);
} else {
panic!("Expected z");
}
}
#[test]
fn test_roundtrip_eagle_time() {
let val = VsfType::e(EtType::u(1000000));
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::e(EtType::u(v)) = parsed {
assert_eq!(v, 1000000);
} else {
panic!("Expected e(u)");
}
let val = VsfType::e(EtType::f6(123456.789));
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::e(EtType::f6(v)) = parsed {
assert!((v - 123456.789).abs() < 1e-10);
} else {
panic!("Expected e(f6)");
}
}
#[test]
fn test_roundtrip_tensor_small() {
let data = vec![1u16, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let tensor = Tensor::new(vec![3, 4], data.clone());
let val = VsfType::t_u4(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::t_u4(t) = parsed {
assert_eq!(t.shape, vec![3, 4]);
assert_eq!(t.data, data);
assert_eq!(ptr, flat.len()); } else {
panic!("Expected t_u4 tensor");
}
}
#[test]
fn test_roundtrip_tensor_1d() {
let data = vec![-100i32, 0, 100, 200, -50];
let tensor = Tensor::new(vec![5], data.clone());
let val = VsfType::t_i5(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::v_i5(v) = parsed {
assert_eq!(v.data, data);
} else {
panic!("Expected v_i5 vector, got {:?}", parsed);
}
}
#[test]
fn test_1d_vector_optimization_unsigned() {
let data_u8 = vec![1u8, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200];
let tensor_u8 = Tensor::new(vec![12], data_u8.clone());
let val_u8 = VsfType::t_u3(tensor_u8);
let flat_u8 = val_u8.flatten();
assert_eq!(flat_u8[0], b't');
assert_eq!(flat_u8[1], b'n');
let mut ptr = 0;
let parsed_u8 = parse(&flat_u8, &mut ptr).unwrap();
if let VsfType::t_u3(t) = parsed_u8 {
assert_eq!(t.shape, vec![12]);
assert_eq!(t.data, data_u8);
assert_eq!(ptr, flat_u8.len());
} else {
panic!("Expected t_u3 tensor, got {:?}", parsed_u8);
}
let data_u16 = vec![100u16, 200, 300, 400, 500, 1000, 2000, 3000];
let tensor_u16 = Tensor::new(vec![8], data_u16.clone());
let val_u16 = VsfType::t_u4(tensor_u16);
let flat_u16 = val_u16.flatten();
assert_eq!(flat_u16[0], b't');
assert_eq!(flat_u16[1], b'n');
let mut ptr = 0;
let parsed_u16 = parse(&flat_u16, &mut ptr).unwrap();
if let VsfType::v_u4(v) = parsed_u16 {
assert_eq!(v.data, data_u16);
} else {
panic!("Expected v_u4 vector, got {:?}", parsed_u16);
}
let data_u32 = vec![100000u32, 200000, 300000, 400000, 500000];
let tensor_u32 = Tensor::new(vec![5], data_u32.clone());
let val_u32 = VsfType::t_u5(tensor_u32);
let flat_u32 = val_u32.flatten();
assert_eq!(flat_u32[0], b't');
assert_eq!(flat_u32[1], b'n');
let mut ptr = 0;
let parsed_u32 = parse(&flat_u32, &mut ptr).unwrap();
if let VsfType::v_u5(v) = parsed_u32 {
assert_eq!(v.data, data_u32);
} else {
panic!("Expected v_u5 vector, got {:?}", parsed_u32);
}
let data_u64 = vec![1_000_000_000u64, 2_000_000_000, 3_000_000_000];
let tensor_u64 = Tensor::new(vec![3], data_u64.clone());
let val_u64 = VsfType::t_u6(tensor_u64);
let flat_u64 = val_u64.flatten();
assert_eq!(flat_u64[0], b't');
assert_eq!(flat_u64[1], b'n');
let mut ptr = 0;
let parsed_u64 = parse(&flat_u64, &mut ptr).unwrap();
if let VsfType::v_u6(v) = parsed_u64 {
assert_eq!(v.data, data_u64);
} else {
panic!("Expected v_u6 vector, got {:?}", parsed_u64);
}
}
#[test]
fn test_1d_vector_optimization_signed() {
let data_i8 = vec![-10i8, -5, 0, 5, 10, 20, -20, 30];
let tensor_i8 = Tensor::new(vec![8], data_i8.clone());
let val_i8 = VsfType::t_i3(tensor_i8);
let flat_i8 = val_i8.flatten();
assert_eq!(flat_i8[0], b't');
assert_eq!(flat_i8[1], b'n');
let mut ptr = 0;
let parsed_i8 = parse(&flat_i8, &mut ptr).unwrap();
if let VsfType::v_i3(v) = parsed_i8 {
assert_eq!(v.data, data_i8);
} else {
panic!("Expected v_i3 vector, got {:?}", parsed_i8);
}
let data_i16 = vec![-1000i16, -500, 0, 500, 1000];
let tensor_i16 = Tensor::new(vec![5], data_i16.clone());
let val_i16 = VsfType::t_i4(tensor_i16);
let flat_i16 = val_i16.flatten();
assert_eq!(flat_i16[0], b't');
assert_eq!(flat_i16[1], b'n');
let mut ptr = 0;
let parsed_i16 = parse(&flat_i16, &mut ptr).unwrap();
if let VsfType::v_i4(v) = parsed_i16 {
assert_eq!(v.data, data_i16);
} else {
panic!("Expected v_i4 vector, got {:?}", parsed_i16);
}
let data_i32 = vec![-100000i32, 0, 100000, 200000, -50000];
let tensor_i32 = Tensor::new(vec![5], data_i32.clone());
let val_i32 = VsfType::t_i5(tensor_i32);
let flat_i32 = val_i32.flatten();
assert_eq!(flat_i32[0], b't');
assert_eq!(flat_i32[1], b'n');
let mut ptr = 0;
let parsed_i32 = parse(&flat_i32, &mut ptr).unwrap();
if let VsfType::v_i5(v) = parsed_i32 {
assert_eq!(v.data, data_i32);
} else {
panic!("Expected v_i5 vector, got {:?}", parsed_i32);
}
}
#[test]
fn test_1d_vector_vs_multi_dim_encoding() {
let data_1d = vec![1u16, 2, 3, 4, 5];
let tensor_1d = Tensor::new(vec![5], data_1d.clone());
let val_1d = VsfType::t_u4(tensor_1d);
let flat_1d = val_1d.flatten();
let data_2d = vec![1u16, 2, 3, 4, 5, 6];
let tensor_2d = Tensor::new(vec![2, 3], data_2d.clone());
let val_2d = VsfType::t_u4(tensor_2d);
let flat_2d = val_2d.flatten();
assert_eq!(flat_1d[0], b't');
assert_eq!(flat_1d[1], b'n');
assert_eq!(flat_2d[0], b't');
assert_ne!(flat_2d[1], b'n');
assert!(flat_1d.len() <= flat_2d.len());
}
#[test]
fn test_1d_vector_large() {
let hash_data = vec![
0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB,
0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67,
0x89, 0xAB, 0xCD, 0xEF,
];
let tensor = Tensor::new(vec![32], hash_data.clone());
let val = VsfType::t_u3(tensor);
let flat = val.flatten();
assert_eq!(flat[0], b't');
assert_eq!(flat[1], b'n');
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::t_u3(t) = parsed {
assert_eq!(t.shape, vec![32]);
assert_eq!(t.data, hash_data);
assert_eq!(ptr, flat.len());
} else {
panic!("Expected t_u3 tensor");
}
}
#[test]
fn test_roundtrip_tensor_f64() {
let data = vec![1.1f64, 2.2, 3.3, 4.4, 5.5, 6.6];
let tensor = Tensor::new(vec![2, 3], data.clone());
let val = VsfType::t_f6(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::t_f6(t) = parsed {
assert_eq!(t.shape, vec![2, 3]);
for (a, b) in t.data.iter().zip(data.iter()) {
assert!((a - b).abs() < 1e-10);
}
} else {
panic!("Expected t_f6 tensor");
}
}
#[test]
#[cfg(feature = "spirix")]
fn test_roundtrip_spirix_f4e3() {
use spirix::{CircleF4E3, ScalarF4E3};
let scalar = ScalarF4E3 {
fraction: 12345,
exponent: -42,
};
let val = VsfType::s43(scalar);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::s43(s) = parsed {
assert_eq!(s.fraction, 12345);
assert_eq!(s.exponent, -42);
assert_eq!(ptr, flat.len()); } else {
panic!("Expected s43");
}
let circle = CircleF4E3 {
real: 100,
imaginary: -200,
exponent: 5,
};
let val = VsfType::c43(circle);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::c43(c) = parsed {
assert_eq!(c.real, 100);
assert_eq!(c.imaginary, -200);
assert_eq!(c.exponent, 5);
assert_eq!(ptr, flat.len()); } else {
panic!("Expected c43");
}
}
#[test]
fn test_roundtrip_bitpacked_12bit() {
use crate::types::BitPackedTensor;
let samples: Vec<u64> = (0..200).map(|i| (i * 17) % 4096).collect(); let tensor = BitPackedTensor::pack(12, vec![10, 20], &samples);
let val = VsfType::p(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::p(decoded) = parsed {
assert_eq!(decoded.bit_depth, 12);
assert_eq!(decoded.shape, vec![10, 20]);
assert_eq!(ptr, flat.len());
let unpacked = decoded.unpack().into_u64();
assert_eq!(unpacked.len(), 200);
for (i, &val) in unpacked.iter().enumerate() {
assert_eq!(val, samples[i], "Sample {} mismatch", i);
}
} else {
panic!("Expected bitpacked tensor");
}
}
#[test]
fn test_roundtrip_bitpacked_1bit() {
use crate::types::BitPackedTensor;
let samples: Vec<u64> = vec![1, 0, 1, 1, 0, 0, 1, 0];
let tensor = BitPackedTensor::pack(1, vec![8], &samples);
let val = VsfType::p(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::p(decoded) = parsed {
assert_eq!(decoded.bit_depth, 1);
assert_eq!(decoded.shape, vec![8]);
assert_eq!(decoded.data.len(), 1);
let unpacked = decoded.unpack().into_u64();
assert_eq!(unpacked, samples);
} else {
panic!("Expected bitpacked tensor");
}
}
#[test]
fn test_roundtrip_bitpacked_13bit() {
use crate::types::BitPackedTensor;
let samples: Vec<u64> = vec![0, 8191, 4096, 2048, 1024];
let tensor = BitPackedTensor::pack(13, vec![5], &samples);
let val = VsfType::p(tensor);
let flat = val.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::p(decoded) = parsed {
assert_eq!(decoded.bit_depth, 13);
assert_eq!(decoded.shape, vec![5]);
let unpacked = decoded.unpack().into_u64();
assert_eq!(unpacked, samples);
} else {
panic!("Expected bitpacked tensor");
}
}
#[test]
#[should_panic(expected = "Cannot pack")]
fn test_bitpacked_type_overflow() {
use crate::types::BitPackedTensor;
let samples: Vec<u8> = vec![255]; BitPackedTensor::pack(12, vec![1], &samples); }
#[test]
fn test_bitpacked_value_masking() {
use crate::types::BitPackedTensor;
let samples: Vec<u64> = vec![4096]; let tensor = BitPackedTensor::pack(12, vec![1], &samples);
let unpacked = tensor.unpack().into_u64();
assert_eq!(unpacked[0], 0); }
#[test]
fn test_world_coord_xyz_roundtrip() {
use crate::types::WorldCoord;
let coord = WorldCoord::from_xyz(0.5, 0.5, 0.7071); let (x, y, z) = coord.to_xyz();
assert!((x - 0.5).abs() < 0.01, "X error: {}", (x - 0.5).abs());
assert!((y - 0.5).abs() < 0.01, "Y error: {}", (y - 0.5).abs());
assert!((z - 0.7071).abs() < 0.01, "Z error: {}", (z - 0.7071).abs());
}
#[test]
fn test_roundtrip_world_coord() {
use crate::types::WorldCoord;
let coord = WorldCoord::from_lat_lon(0.0, 0.0);
let val = VsfType::w(coord);
let flat = val.flatten();
assert_eq!(flat[0], b'w');
assert_eq!(flat.len(), 9);
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
assert_eq!(ptr, flat.len());
if let VsfType::w(decoded) = parsed {
assert_eq!(decoded.raw(), coord.raw());
let (lat, lon) = decoded.to_lat_lon();
println!("Decoded (0,0): lat={}, lon={}", lat, lon);
assert!(lat.abs() < 1.0, "Lat error: {}", lat.abs());
assert!(lon.abs() < 1.0, "Lon error: {}", lon.abs());
} else {
panic!("Expected WorldCoord");
}
}
#[test]
fn test_world_coord_word_encoding() {
use crate::types::WorldCoord;
let coord = WorldCoord::from_lat_lon(51.5074, -0.1278); let words = coord.to_words();
assert_eq!(words.split_whitespace().count(), 7);
let decoded = WorldCoord::from_words(&words).expect("Should decode valid words");
assert_eq!(decoded.raw(), coord.raw());
}
#[test]
fn test_wrapped_type_roundtrip() {
let original_data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let wrapped = VsfType::v(b'z', original_data.clone());
let flat = wrapped.flatten();
assert_eq!(flat[0], b'v'); assert_eq!(flat[1], b'z');
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::v(alg, data) = parsed {
assert_eq!(alg, b'z');
assert_eq!(data, original_data);
assert_eq!(ptr, flat.len()); } else {
panic!("Expected wrapped type");
}
}
#[test]
fn test_wrapped_type_algorithms() {
let algorithms = vec![b'z', b'r', b'x', b'e'];
let test_data = vec![0xAB; 100];
for alg in algorithms {
let wrapped = VsfType::v(alg, test_data.clone());
let flat = wrapped.flatten();
let mut ptr = 0;
let parsed = parse(&flat, &mut ptr).unwrap();
if let VsfType::v(parsed_alg, parsed_data) = parsed {
assert_eq!(parsed_alg, alg);
assert_eq!(parsed_data, test_data);
} else {
panic!("Expected wrapped type for algorithm '{}'", alg as char);
}
}
}
}