/// /// Functionality for parsing and writing basic data types /// use byteorder::{ByteOrder, BigEndian}; use std::io; pub type OResult = Result; pub type IResult<'a, O> = Result<(&'a [u8], O), (&'a [u8], ErrType)>; pub enum ErrType { Incomplete(Option), Failed(String), } pub fn fail(input: &[u8], msg: String) -> IResult { Err((input, ErrType::Failed(msg))) } pub fn incomplete(input: &[u8], size: Option) -> IResult { Err((input, ErrType::Incomplete(size))) } pub fn take_bytes>(input: &[u8], count: CC) -> IResult<&[u8]> { let cc = count.into(); if input.len() > cc { incomplete(input, Some(cc)) } else { let (taken, input) = input.split_at(cc); Ok((input, taken)) } } /* * Parse functions */ pub fn parse_u16(input: &[u8]) -> IResult { let (input, buf) = take_bytes(input, 2_usize)?; let val = BigEndian::read_u16(&buf); Ok((input, val)) } pub fn parse_int2(input: &[u8]) -> IResult { let (input, buf) = take_bytes(input, 2_usize)?; let val = BigEndian::read_i16(&buf); Ok((input, val)) } pub fn parse_int4(input: &[u8]) -> IResult { let (input, buf) = take_bytes(input, 4_usize)?; let val = BigEndian::read_i32(&buf); Ok((input, val)) } /// Convert GDS REAL8 to IEEE float64 pub fn decode_real8(int: u64) -> f64 { let neg = int & 0x8000_0000_0000_0000; let exp = (int >> 56) & 0x7f; let mut mant = (int & 0x00ff_ffff_ffff_ffff) as f64; if neg != 0 { mant *= -1.0 } let exp2 = 4 * (exp as i32 - 64) - 56; mant * 2_f64.powi(exp2) } pub fn parse_real8(input: &[u8]) -> IResult { let (input, buf) = take_bytes(input, 8_usize)?; let data = BigEndian::read_u64(&buf); Ok((input, decode_real8(data))) } pub fn parse_datetime(input: &[u8]) -> IResult<[i16; 6]> { let mut buf = [0_i16; 6]; let mut input = input; for ii in 0..6 { (input, buf[ii]) = parse_int2(input)?; } buf[0] += 1900; // Year is from 1900 Ok((input, buf)) } pub fn parse_bitarray(input: &[u8]) -> IResult<[bool; 16]> { let mut bits = [false; 16]; let (input, val) = parse_int2(input)?; for ii in 0..16 { bits[ii] = ((val >> (16 - 1 - ii)) & 0x01) == 1; } Ok((input, bits)) } pub fn parse_ascii(input: &[u8], length: u16) -> IResult> { let length = length as usize; let (input, data) = take_bytes(input, length)?; let last = data[length - 1]; let true_length = if last == 0 { length - 1 } else { length }; let vec = data[..true_length].to_vec(); Ok((input, vec)) } /* * Pack functions */ pub fn bitarray2int(bits: &[bool; 16]) -> u16 { let mut int: u16 = 0; for ii in 0..16 { int |= (bits[ii] as u16) << (16 - 1 - ii); } int } pub fn pack_bitarray(buf: &mut [u8], bits: &[bool; 16]) { BigEndian::write_u16(buf, bitarray2int(bits)) } pub fn pack_int2(buf: &mut [u8], int: i16) { BigEndian::write_i16(buf, int) } pub fn pack_int4(buf: &mut [u8], int: i32) { BigEndian::write_i32(buf, int) } pub fn pack_real8(buf: &mut [u8], fnum: f64) { BigEndian::write_u64(buf, encode_real8(fnum)) } pub fn pack_ascii(buf: &mut [u8], data: &[u8]) -> usize { let len = data.len(); buf[..len].copy_from_slice(data); if len % 2 == 1 { buf[len] = 0; len + 1 } else { len } } pub fn pack_datetime(buf: &mut [u8], date: &[i16; 6]) { assert!(buf.len() >= 6 * 2); let year = date[0] - 1900; pack_int2(buf, year); for ii in 1..6 { pack_int2(&mut buf[(2 * ii)..], date[ii]); } } /// Convert from float64 to GDS REAL8 representation. pub fn encode_real8(fnum: f64) -> u64 { // Split the ieee float bitfields let ieee = fnum.to_bits(); let sign = ieee & 0x8000_0000_0000_0000; let ieee_exp = (ieee >> 52) as i32 & 0x7ff; let ieee_mant = ieee & 0xf_ffff_ffff_ffff; let subnorm = (ieee_exp == 0) & (ieee_mant != 0); if (ieee_exp == 0) & (ieee_mant == 0) { return 0 } // IEEE normal double is (1 + ieee_mant / 2^52) * 2^(ieee_exp - 1023) // IEEE subnormal double is (ieee_mant / 2^52) * 2^(-1022) // GDS real8 is (gds_mant / 2^(7*8)) * 16^(gds_exp - 64) // = (gds_mant / 2^56) * 2^(4 * gds_exp - 256) // Convert exponent. let exp2 = if subnorm { -1022 } else {ieee_exp + 1 - 1023}; // +1 is due to mantissa (1.xxxx in IEEE vs 0.xxxxx in GDSII) let mut exp16 = exp2 / 4; let rest = exp2 % 4; // Compensate for exponent coarseness let comp = rest != 0; let mut shift; if comp { exp16 += 1; shift = 4 - rest; } else { shift = rest; } shift -= 3; // account for gds bit position // add leading one let mut gds_mant_unshifted = ieee_mant; if !subnorm { gds_mant_unshifted += 0x10_0000_0000_0000; } let mut gds_mant = if shift > 0 { gds_mant_unshifted >> shift } else { gds_mant_unshifted << -shift }; // add gds exponent bias let mut gds_exp = exp16 + 64; if gds_exp < -14 { // number is too small return 0 } let neg_biased = gds_exp < 0; if neg_biased { gds_mant >>= gds_exp * 4; gds_exp = 0; } let too_big = (gds_exp > 0x7f) & !subnorm; if too_big { panic!("Number too big for real8 format"); //TODO error handling } let gds_exp_bits = (gds_exp as u64) << 56; let real8 = sign | gds_exp_bits | gds_mant; real8 } #[cfg(test)] mod tests { #[test] fn test_parse_bitarray() { use basic::parse_bitarray; //assert!(parse_bitarray(b"59") == 13625); assert_eq!(parse_bitarray(b"\x00\x00").unwrap().1, [false; 16]); assert_eq!(parse_bitarray(b"\xff\xff").unwrap().1, [true; 16]); let arr_0001 = parse_bitarray(b"\x00\x01").unwrap().1; for (ii, &vv) in arr_0001.iter().enumerate() { assert_eq!(ii == 15, vv); } let arr_8000 = parse_bitarray(b"\x80\x00").unwrap().1; for (ii, &vv) in arr_8000.iter().enumerate() { assert_eq!(ii == 0, vv); } } #[test] fn test_parse_int2() { use basic::parse_int2; assert_eq!(parse_int2(b"59").unwrap().1, 13625); assert_eq!(parse_int2(b"\0\0").unwrap().1, 0); assert_eq!(parse_int2(b"\xff\xff").unwrap().1, -1); } #[test] fn test_parse_int4() { use basic::parse_int4; assert_eq!(parse_int4(b"4321").unwrap().1, 875770417); } #[test] fn test_decode_real8() { use basic::decode_real8; // zeroes assert_eq!(decode_real8(0x0), 0.0); assert_eq!(decode_real8(1<<63), 0.0); // negative assert_eq!(decode_real8(0xff << 56), 0.0); // denormalized assert_eq!(decode_real8(0x4110 << 48), 1.0); assert_eq!(decode_real8(0xC120 << 48), -2.0); //TODO panics on invalid? } #[test] fn test_parse_real8() { use basic:: parse_real8; assert_eq!(0.0, parse_real8(&[0; 8]).unwrap().1); assert_eq!(1.0, parse_real8(&[0x41, 0x10, 0, 0, 0, 0, 0, 0]).unwrap().1); assert_eq!(-2.0, parse_real8(&[0xC1, 0x20, 0, 0, 0, 0, 0, 0]).unwrap().1); } #[test] fn test_parse_ascii() { use basic::parse_ascii; assert_eq!(parse_ascii(b"12345", 5).unwrap().1, b"12345"); assert_eq!(parse_ascii(b"12345\0", 6).unwrap().1, b"12345"); // strips trailing null byte assert_eq!(parse_ascii(b"123456", 6).unwrap().1, b"123456"); } /* fn test_pack_bitarray() { packed = pack_bitarray(321) assert_eq!(len(packed), 2); assert_eq!(packed, struct.pack('>H', 321)); } fn test_pack_int2() { packed = pack_int2((3, 2, 1)) assert(len(packed) == 3*2) assert(packed == struct.pack('>3h', 3, 2, 1)) assert(pack_int2([-3, 2, -1]) == struct.pack('>3h', -3, 2, -1)) } fn test_pack_int4() { packed = pack_int4((3, 2, 1)) assert(len(packed) == 3*4) assert(packed == struct.pack('>3l', 3, 2, 1)) assert(pack_int4([-3, 2, -1]) == struct.pack('>3l', -3, 2, -1)) } fn test_encode_real8() { assert(encode_real8(numpy.array([0.0])) == 0) arr = numpy.array((1.0, -2.0, 1e-9, 1e-3, 1e-12)) assert_array_equal(decode_real8(encode_real8(arr)), arr) } fn test_pack_real8() { reals = (0, 1, -1, 0.5, 1e-9, 1e-3, 1e-12) packed = pack_real8(reals) assert(len(packed) == len(reals) * 8) assert_array_equal(parse_real8(packed), reals) } fn test_pack_ascii() { assert(pack_ascii(b'4321') == b'4321') assert(pack_ascii(b'321') == b'321\0') } */ }