use std::collections::HashMap; use std::vec::Vec; use std::io::{Write,Read}; use std::num::FpCategory; use std::sync::LazyLock; pub enum Value { Integer(i64), ByteString(Vec), Boolean(bool), List(Vec), Dictionary(HashMap), Float(f64), Nothing, } static FLOAT_DIVISOR: LazyLock = LazyLock::new(|| (256_f64).ln()); fn write_packed_int( x: i64 ) -> Vec { let mut w = Vec::::with_capacity(10); let mut v = x; let mut bb = if x < 0 { v = -v; 0x40_u8 } else { 0x00_u8 } | (v&0x3F) as u8 | if v > (v & 0x3F) { 0x80_u8 } else { 0x00_u8 }; w.push(bb); v = v >> 6; while v > 0 { bb = (v & 0x7F) as u8 | if v > (v & 0x7F) { 0x80_u8 } else { 0x00_u8 }; w.push(bb); v = v >> 7; } w } fn read_packed_int( r: &mut R ) -> Result { let mut out : i64; let mut bb = [0u8]; r.read( &mut bb )?; let negative = (bb[0]&0x40_u8) == 0x40_u8; out = (bb[0]&0x3F_u8) as i64; let mut c = 0; while (bb[0]&0x80_u8) != 0 { r.read( &mut bb )?; out |= ((bb[0]&0x7F_u8) as i64) << (6+7*c); c += 1; } if negative { Ok(-out) } else { Ok(out) } } impl Value { pub fn write(&self, w: &mut W) -> Result<(), std::io::Error> { match self { Self::Integer(x) => { let bb = ['i' as u8]; w.write_all(&bb)?; w.write_all(&write_packed_int( *x ))?; }, Self::ByteString(s) => { let bb = ['s' as u8]; w.write_all(&bb)?; w.write_all(&write_packed_int( s.len() as i64 ))?; w.write_all( &s )?; }, Self::Boolean(b) => { let mut bb = [0u8]; bb[0] = if *b { '1' } else { '0' } as u8; w.write_all( &bb )?; }, Self::List(l) => { let mut bb = ['l' as u8]; w.write_all(&bb)?; for v in l { v.write( w )?; } bb[0] = 'e' as u8; w.write_all(&bb)?; }, Self::Dictionary(d) => { let mut bb = ['d' as u8]; w.write_all(&bb)?; for (k, v) in d { (Value::ByteString( k.clone().into_bytes() )).write( w )?; v.write( w )?; } bb[0] = 'e' as u8; w.write_all(&bb)?; }, Self::Float(f) => { match f.classify() { FpCategory::Nan => { let mut bb = ['F' as u8, 0u8]; bb[1] = if f.is_sign_negative() { 'N' } else { 'n' } as u8; w.write_all( &bb )?; }, FpCategory::Infinite => { let mut bb = ['F' as u8, 0u8]; bb[1] = if f.is_sign_negative() { 'I' } else { 'i' } as u8; w.write_all( &bb )?; }, FpCategory::Zero => { let mut bb = ['F' as u8, 0u8]; bb[1] = if f.is_sign_negative() { 'Z' } else { 'z' } as u8; w.write_all( &bb )?; }, FpCategory::Subnormal => { let mut bb = ['F' as u8, 0u8]; // The format doesn't account for these...uh...make them zero? bb[1] = if f.is_sign_negative() { 'Z' } else { 'z' } as u8; w.write_all( &bb )?; }, FpCategory::Normal => { let bb = ['f' as u8]; w.write_all( &bb )?; let mut bin = Vec::::with_capacity(10); let (negative, d) = if f.is_sign_negative() { (true, -*f) } else { (false, *f) }; let scale : i64 = (d.ln() / *FLOAT_DIVISOR) as i64; let scale = if scale < 0 { -1 } else { scale }; let mut d = d / 256.0_f64.powf( scale as f64 ); bin.push( d as u8 ); d = d.fract(); for _ in 0..15 { d = d*256.0; bin.push( d as u8 ); d = d.fract(); if d == 0.0 { break; } } if negative { w.write_all( &write_packed_int( -(bin.len() as i64) ) )?; } else { w.write_all( &write_packed_int( bin.len() as i64 ) )?; } w.write_all( &bin )?; w.write_all( &write_packed_int( scale ) )?; }, } }, Self::Nothing => { let bb = ['n' as u8]; w.write_all( &bb )?; }, } Ok(()) } pub fn write_packet( &self, w: &mut W ) -> Result<(), std::io::Error> { let mut body : Vec = vec![1u8,0u8,0u8,0u8,0u8]; self.write( &mut body )?; let len = body.len() as u32; body[1..5].copy_from_slice( &len.to_be_bytes() ); w.write_all( &body ) } } #[cfg(test)] mod tests { use super::*; use core::error::Error; use std::fs::File; use std::io::prelude::*; #[test] fn write_file1() -> Result<(),Box> { let mut f = File::create("test.gats")?; let v = Value::Dictionary(HashMap::from([ ("biggerint".to_string(), Value::Integer(98765)), ("negint".to_string(), Value::Integer(-98765)), ("integer".to_string(), Value::Integer(44)), ("boolean".to_string(), Value::Boolean(true)), ("list".to_string(), Value::List(vec![ Value::Integer(1), Value::Integer(1), Value::Integer(2), Value::Integer(3), Value::Integer(5), Value::Integer(8), ])), ("null".to_string(), Value::Nothing), ("float".to_string(), Value::Float(123.456)), ])); v.write_packet( &mut f ); Ok(()) } }