diff --git a/src/basic.rs b/src/basic.rs index 0cadc0d..d8fa7da 100644 --- a/src/basic.rs +++ b/src/basic.rs @@ -1,80 +1,106 @@ -/* - * Functionality for parsing and writing basic data types - */ -use nom; -use nom::{IResult}; -use byteorder::BigEndian; -//use std::io::Write; +/// +/// 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)) + } +} -pub type OWResult = Result; /* * Parse functions */ -//pub fn parse_byte_as_bits(input: &[u8]) -> IResult<&[u8], (u8, u8, u8, u8, u8, u8, u8, u8)> { -// nom::bits::bits(nom::sequence::tuple(( -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// nom::bits::complete::take::<_, _, _, CustomError<_>>(1_usize), -// )))(input) -//} - - -pub fn parse_int2(input: &[u8]) -> IResult<&[u8], i16> { - nom::number::streaming::be_i16(input)? +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_int4(input: &[u8]) -> IResult<&[u8], i32> { - nom::number::streaming::be_i32(input)? +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 { - // Convert GDS REAL8 to IEEE float64 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 + mant *= -1.0 } - mant * 2_f64.powi(4 * (exp - 64) - 56) + let exp2 = 4 * (exp as i32 - 64) - 56; + mant * 2_f64.powi(exp2) } -pub fn parse_real8(input: &[u8]) -> IResult<&[u8], f64> { - let data = nom::number::streaming::be_u64(input)?; - IResult::Ok(decode_real8(data)) +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<&[u8], [u16; 6]> { - let mut buf = [0_u16; 6]; - let mut parts = nom::multi::fill(parse_int2, &mut buf)(input); - parts[0] += 1900; // Year is from 1900 - IResult::Ok(parts) +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<&[u8], [bool; 16]> { - let bits = [false; 16]; +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; } - bits + Ok((input, bits)) } -pub fn parse_ascii(input: &[u8], length: usize) -> IResult<&[u8], Vec> { - let last = input[length - 1]; - let true_length = if last == '\0' { length - 1 } else { length }; - let vec = input[..true_length].to_vec(); - IResult::Ok((input[length..], vec)) +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)) } @@ -82,7 +108,7 @@ pub fn parse_ascii(input: &[u8], length: usize) -> IResult<&[u8], Vec> { * Pack functions */ -pub fn pack_bitarray(bits: &[bool; 16]) -> u16 { +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); @@ -90,24 +116,28 @@ pub fn pack_bitarray(bits: &[bool; 16]) -> u16 { 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(&mut buf, int) + BigEndian::write_i16(buf, int) } pub fn pack_int4(buf: &mut [u8], int: i32) { - BigEndian::write_i32(&mut buf, int) + BigEndian::write_i32(buf, int) } pub fn pack_real8(buf: &mut [u8], fnum: f64) { - BigEndian::write_u64(&mut buf, encode_real8(fnum)) + 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'; + buf[len] = 0; len + 1 } else { len @@ -115,19 +145,18 @@ pub fn pack_ascii(buf: &mut [u8], data: &[u8]) -> usize { } -pub fn pack_datetime(buf: &mut [u8], date: &[u16; 6]) { +pub fn pack_datetime(buf: &mut [u8], date: &[i16; 6]) { assert!(buf.len() >= 6 * 2); let year = date[0] - 1900; - pack_int2(&mut buf, year); + 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 { - // Convert from float64 to GDS REAL8 representation. - // Split the ieee float bitfields let ieee = fnum.to_bits(); let sign = ieee & 0x8000_0000_0000_0000; @@ -191,8 +220,119 @@ pub fn encode_real8(fnum: f64) -> u64 { panic!("Number too big for real8 format"); //TODO error handling } - let gds_exp_bits = gds_exp << 56; + 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') + } +*/ +} diff --git a/src/elements.rs b/src/elements.rs index e972b77..8c2e35b 100644 --- a/src/elements.rs +++ b/src/elements.rs @@ -1,10 +1,8 @@ -/* - * Functionality for reading/writing elements (geometry, text labels, - * structure references) and associated properties. - */ +/// +/// Functionality for reading/writing elements (geometry, text labels, +/// structure references) and associated properties. +/// -//from .record import Record -// use records::{BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF, DATATYPE, PATHTYPE, BOXTYPE, NODETYPE, TEXTTYPE, LAYER, XY, WIDTH, COLROW, PRESENTATION, STRING, @@ -13,132 +11,147 @@ use records::{BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF, }; use records; -use record::RecordHeader; -use basic::{OWResult}; +use record::{RecordHeader, Record}; +use basic::{OResult, IResult, fail}; use std::collections::HashMap; use std::io::Write; -use nom::IResult; -pub fn read_properties(input: &[u8]) -> IResult<&[u8], HashMap::>> { - /* - * Read element properties. - * - * Assumes PROPATTR records have unique values. - * Stops reading after consuming ENDEL record. - * - * Args: - * stream: Stream to read from. - * - * Returns: - * propattr: -> propvalue mapping - */ - let properties = HashMap{}; +/// +/// Read element properties. +/// +/// Assumes PROPATTR records have unique values. +/// Stops reading after consuming ENDEL record. +/// +/// Args: +/// stream: Stream to read from. +/// +/// Returns: +/// propattr: -> propvalue mapping +/// +pub fn read_properties(input: &[u8]) -> IResult>> { + let mut properties = HashMap::new(); - let (input, header) = RecordHeader::parse(input)?; + let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != ENDEL::tag() { if header.tag == PROPATTR::tag() { - let (input, key) = PROPATTR::read_data(input, header.data_size)?; - let (input, value) = PROPVALUE::read(input)?; - assert!(!properties.contains_key(key), format!{"Duplicate property key: {}", key}); + let result = PROPATTR::read_data(input, header.data_size)?; + input = result.0; + let key = result.1; + let result = PROPVALUE::read(input)?; + input = result.0; + let value = result.1; + assert!(!properties.contains_key(&key), "Duplicate property key: {}", key); properties.insert(key, value); } - let (input, header) = RecordHeader::parse(input)?; + (input, header) = RecordHeader::read(input)?; } Ok((input, properties)) } -fn write_properties(ww: W, properties: &HashMap::>) -> OWResult { - /* - * Write element properties. - * - * This is does _not_ write the ENDEL record. - * - * Args: - * stream: Stream to write to. - */ +/// +/// Write element properties. +/// +/// This is does _not_ write the ENDEL record. +/// +/// Args: +/// stream: Stream to write to. +/// +pub fn write_properties(ww: &mut W, properties: &HashMap::>) -> OResult { let mut size = 0; - for (key, value) in &properties { + for (key, value) in properties { size += PROPATTR::write(ww, key)?; size += PROPVALUE::write(ww, value)?; } Ok(size) } -trait Element { - fn parse(input: &[u8]) -> Self; - /* - * Read from a stream to construct this object. - * Consumes up to (and including) the ENDEL record. - */ +pub trait Element { + /// + /// Read from a stream to construct this object. + /// Consumes up to (and including) the ENDEL record. + /// + fn read(input: &[u8]) -> IResult where Self: Sized; - fn write(&self, ww: W) -> OWResult; - /* - * Write this element to a stream. - * Finishes with an ENDEL record. - */ + /// + /// Write this element to a stream. + /// Finishes with an ENDEL record. + /// + fn write(&self, ww: &mut W) -> OResult; } -struct Reference { - /* - * Datastructure representing - * an instance of a structure (SREF / structure reference) or - * an array of instances (AREF / array reference). - * Type is determined by the presence of the `colrow` tuple. - * - * Transforms are applied to each individual instance (_not_ - * to the instance's origin location || array vectors). - */ - struct_name: Vec, // Name of the structure being referenced. - invert_y: bool, // Whether to mirror the pattern (negate y-values / flip across x-axis). Default false. - mag: f64, // Scaling factor (default 1) """ - angle_deg: f64, // Rotation (degrees counterclockwise) - xy: Vec, - /* - * (For SREF) Location in the parent structure corresponding to the instance's origin (0, 0). - * (For AREF) 3 locations: - * [`offset`, - * `offset + col_basis_vector * colrow[0]`, - * `offset + row_basis_vector * colrow[1]`] - * which define the first instance's offset and the array's basis vectors. - * Note that many GDS implementations only support manhattan basis vectors, and some - * assume a certain axis mapping (e.g. x->columns, y->rows) and "reinterpret" the - * basis vectors to match it. - */ +/// +/// Datastructure representing +/// an instance of a structure (SREF / structure reference) or +/// an array of instances (AREF / array reference). +/// Type is determined by the presence of the `colrow` tuple. +/// +/// Transforms are applied to each individual instance (_not_ +/// to the instance's origin location || array vectors). +/// +#[derive(Debug, Clone)] +pub struct Reference { + /// Name of the structure being referenced. + struct_name: Vec, + /// Whether to mirror the pattern (negate y-values / flip across x-axis). Default false. + invert_y: bool, + /// Scaling factor (default 1) + mag: f64, + /// Rotation (degrees counterclockwise) + angle_deg: f64, - colrow: Option<(i32, i32)>, // Number of columns and rows (AREF) || None (SREF) - properties: HashMap::>, // Properties associated with this reference. + /// (For SREF) Location in the parent structure corresponding to the instance's origin (0, 0). + /// (For AREF) 3 locations: + /// [`offset`, + /// `offset + col_basis_vector * colrow[0]`, + /// `offset + row_basis_vector * colrow[1]`] + /// which define the first instance's offset and the array's basis vectors. + /// Note that many GDS implementations only support manhattan basis vectors, and some + /// assume a certain axis mapping (e.g. x->columns, y->rows) and "reinterpret" the + /// basis vectors to match it. + xy: Vec, + + /// Number of columns and rows (AREF) || None (SREF) + colrow: Option<(i16, i16)>, + /// Properties associated with this reference. + properties: HashMap::>, } impl Element for Reference { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let mut invert_y = false; - let mut mag = 1; - let mut angle_deg = 0; + let mut mag = 1.0; + let mut angle_deg = 0.0; let mut colrow = None; - let (input, mut struct_name) = SNAME::skip_and_read(input)?; + let (input, struct_name) = SNAME::skip_and_read(input)?; - let (input, mut header) = RecordHeader::parse(input)?; + let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_XY { match header.tag { - records::RTAG_STRANS => - {let (input, invert_y) = STRANS::read_data(input, header.data_size)?[0];}, + records::RTAG_STRANS => { + let result = STRANS::read_data(input, header.data_size)?; + input = result.0; + invert_y = result.1[0]; + }, records::RTAG_MAG => - {let (input, mag) = MAG::read_data(input, header.data_size)?;}, + {(input, mag) = MAG::read_data(input, header.data_size)?;}, records::RTAG_ANGLE => - {let (input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, - records::RTAG_COLROW => - {let (input, colrow) = COLROW::read_data(input, header.data_size)?;}, + {(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, + records::RTAG_COLROW => { + let result = COLROW::read_data(input, header.data_size)?; + input = result.0; + colrow = Some((result.1[0], result.1[1])); + }, _ => - return Err(format!("Unexpected tag {:04x}", header.tag)), + return fail(input, format!("Unexpected tag {:04x}", header.tag)), }; - let (input, header) = RecordHeader::parse(input)?; + (input, header) = RecordHeader::read(input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, properties) = read_properties(input)?; - Reference{ + Ok((input, Reference{ struct_name: struct_name, xy: xy, properties: properties, @@ -146,34 +159,39 @@ impl Element for Reference { invert_y: invert_y, mag: mag, angle_deg: angle_deg - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; size += match self.colrow { - None => SREF::write(ww)?, - Some(_) => AREF::write(ww)?, + None => SREF::write(ww, &())?, + Some(_) => AREF::write(ww, &())?, }; - size += SNAME::write(ww, self.struct_name)?; - if self.angle_deg != 0 || self.mag != 1 || self.invert_y { - size += STRANS::write(ww, (self.invert_y as u16) << 15)?; - if self.mag != 1 { - size += MAG::write(ww, self.mag)?; + size += SNAME::write(ww, &self.struct_name)?; + if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y { + let strans = { + let mut arr = [false; 16]; + arr[0] = self.invert_y; + arr + }; + size += STRANS::write(ww, &strans)?; + if self.mag != 1.0 { + size += MAG::write(ww, &self.mag)?; } - if self.angle_deg !=0 { - size += ANGLE::write(ww, self.angle_deg)?; + if self.angle_deg != 0.0 { + size += ANGLE::write(ww, &self.angle_deg)?; } } - if self.colrow.is_some() { - size += COLROW::write(ww, self.colrow)?; + if let Some(cr) = self.colrow { + size += COLROW::write(ww, &vec!{cr.0, cr.1})?; } - size += XY::write(ww, self.xy)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww, None)?; + size += XY::write(ww, &self.xy)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } @@ -181,259 +199,289 @@ impl Element for Reference { impl Reference { pub fn check(&self) { if self.colrow.is_some() { - assert!(self.xy.len() != 6, format!("colrow is Some, so expected size-6 xy. Got {}", self.xy)); + assert!(self.xy.len() != 6, "colrow is Some, so expected size-6 xy. Got {:?}", self.xy); } else { - assert!(self.xy.len() != 2, format!("Expected size-2 xy. Got {}", self.xy)); + assert!(self.xy.len() != 2, "Expected size-2 xy. Got {:?}", self.xy); } } } -struct Boundary { - /* - * Datastructure representing a Boundary element. - */ - layer: (i16, i16), // (layer, data_type) tuple - xy: Vec, // Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,... - properties: HashMap::>, // Properties for the element. +/// +/// Datastructure representing a Boundary element. +/// +#[derive(Debug, Clone)] +pub struct Boundary { + /// (layer, data_type) tuple + layer: (i16, i16), + /// Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,... + xy: Vec, + /// Properties for the element. + properties: HashMap::>, } impl Element for Boundary { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = DATATYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; - Boundary{ + Ok((input, Boundary{ layer: (layer, dtype), xy: xy, properties: properties, - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += BOUNDARY::write(ww)?; - size += LAYER::write(ww, self.layer.0)?; - size += DATATYPE::write(ww, self.layer.1)?; - size += XY::write(ww, self.xy)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww)?; + size += BOUNDARY::write(ww, &())?; + size += LAYER::write(ww, &self.layer.0)?; + size += DATATYPE::write(ww, &self.layer.1)?; + size += XY::write(ww, &self.xy)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } -struct Path { - /* - * Datastructure representing a Path element. - - * If `path_type < 4`, `extension` values are not written. - * During read, `exension` defaults to (0, 0) even if unused. - */ - layer: (i16, i16), // (layer, data_type) tuple - path_type: i16, // End-cap type (0: flush, 1: circle, 2: square, 4: custom) - width: i16, // Path width - extension: (i32, i32), // Extension when using path_type=4. Ignored otherwise. - xy: Vec, // Path centerline coordinates. [x0, y0, x1, y1,...] - properties: HashMap::>, //Properties for the element. +/// +/// Datastructure representing a Path element. +/// +/// If `path_type < 4`, `extension` values are not written. +/// During read, `exension` defaults to (0, 0) even if unused. +/// +#[derive(Debug, Clone)] +pub struct Path { + /// (layer, data_type) tuple + layer: (i16, i16), + /// End-cap type (0: flush, 1: circle, 2: square, 4: custom) + path_type: i16, + /// Path width + width: i32, + /// Extension when using path_type=4. Ignored otherwise. + extension: (i32, i32), + /// Path centerline coordinates. [x0, y0, x1, y1,...] + xy: Vec, + /// Properties for the element. + properties: HashMap::>, } impl Element for Path { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let mut path_type = 0; let mut width = 0; let mut bgn_ext = 0; let mut end_ext = 0; - let (input, mut layer) = LAYER::skip_and_read(input)?; - let (input, mut dtype) = DATATYPE::read(input)?; + let (input, layer) = LAYER::skip_and_read(input)?; + let (input, dtype) = DATATYPE::read(input)?; - let (input, mut header) = RecordHeader::parse(&input)?; + let (mut input, mut header) = RecordHeader::read(&input)?; while header.tag != records::RTAG_XY { match header.tag { records::RTAG_PATHTYPE => - {let (input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, + {(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, records::RTAG_WIDTH => - {let (input, width) = WIDTH::read_data(input, header.data_size)?;}, + {(input, width) = WIDTH::read_data(input, header.data_size)?;}, records::RTAG_BGNEXTN => - {let (input, bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;}, + {(input, bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;}, records::RTAG_ENDEXTN => - {let (input, end_ext) = ENDEXTN::read_data(input, header.data_size)?;}, + {(input, end_ext) = ENDEXTN::read_data(input, header.data_size)?;}, _ => - return Err(format!("Unexpected tag {:04x}", header.tag)), + return fail(input, format!("Unexpected tag {:04x}", header.tag)), }; - let (input, header) = RecordHeader::parse(&input)?; + (input, header) = RecordHeader::read(&input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, properties) = read_properties(input)?; - Path{ + Ok((input, Path{ layer: (layer, dtype), xy: xy, properties: properties, extension: (bgn_ext, end_ext), path_type: path_type, width: width, - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += PATH::write(ww)?; - size += LAYER::write(ww, self.layer[0])?; - size += DATATYPE::write(ww, self.layer[1])?; + size += PATH::write(ww, &())?; + size += LAYER::write(ww, &self.layer.0)?; + size += DATATYPE::write(ww, &self.layer.1)?; if self.path_type != 0 { - size += PATHTYPE::write(ww, self.path_type)?; + size += PATHTYPE::write(ww, &self.path_type)?; } if self.width != 0 { - size += WIDTH::write(ww, self.width)?; + size += WIDTH::write(ww, &self.width)?; } if self.path_type < 4 { let (bgn_ext, end_ext) = self.extension; if bgn_ext != 0 { - size += BGNEXTN::write(ww, bgn_ext)?; + size += BGNEXTN::write(ww, &bgn_ext)?; } if end_ext != 0 { - size += ENDEXTN::write(ww, end_ext)?; + size += ENDEXTN::write(ww, &end_ext)?; } } - size += XY::write(ww, self.xy)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww)?; + size += XY::write(ww, &self.xy)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } -struct GDSBox { - /* - * Datastructure representing a Box element. Rarely used. - */ - layer: (i16, i16), // (layer, box_type) tuple - xy: Vec, // Box coordinates (5 pairs) - properties: HashMap::>, // Properties for the element. +/// +/// Datastructure representing a Box element. Rarely used. +/// +#[derive(Debug, Clone)] +pub struct GDSBox { + /// (layer, box_type) tuple + layer: (i16, i16), + /// Box coordinates (5 pairs) + xy: Vec, + /// Properties for the element. + properties: HashMap::>, } impl Element for GDSBox { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = BOXTYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; - GDSBox{ + Ok((input, GDSBox{ layer: (layer, dtype), xy: xy, properties: properties, - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += BOX::write(ww)?; - size += LAYER::write(ww, self.layer[0])?; - size += BOXTYPE::write(ww, self.layer[1])?; - size += XY::write(ww, self.xy)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww)?; + size += BOX::write(ww, &())?; + size += LAYER::write(ww, &self.layer.0)?; + size += BOXTYPE::write(ww, &self.layer.1)?; + size += XY::write(ww, &self.xy)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } -struct Node { - /* - * Datastructure representing a Node element. Rarely used. - */ - layer: (i16, i16), // (layer, box_type) tuple - xy: Vec, // 1-50 pairs of coordinates. - properties: HashMap::>, // Properties for the element. +/// +/// Datastructure representing a Node element. Rarely used. +/// +#[derive(Debug, Clone)] +pub struct Node { + /// (layer, box_type) tuple + layer: (i16, i16), + /// 1-50 pairs of coordinates. + xy: Vec, + /// Properties for the element. + properties: HashMap::>, } impl Element for Node { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = NODETYPE::read(input)?; let (input, xy) = XY::read(input)?; let (input, properties) = read_properties(input)?; - Node{ + Ok((input, Node{ layer: (layer, dtype), xy: xy, properties: properties, - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += NODE::write(ww)?; - size += LAYER::write(ww, self.layer[0])?; - size += NODETYPE::write(ww, self.layer[1])?; - size += XY::write(ww, self.xy)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww)?; + size += NODE::write(ww, &())?; + size += LAYER::write(ww, &self.layer.0)?; + size += NODETYPE::write(ww, &self.layer.1)?; + size += XY::write(ww, &self.xy)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } -struct Text { - /* - * Datastructure representing a text label. - */ - layer: (i16, i16), // (layer, node_type) tuple +/// +/// Datastructure representing a text label. +/// +#[derive(Debug, Clone)] +pub struct Text { + /// (layer, node_type) tuple + layer: (i16, i16), + + /// Bit array. Default all zeros. + /// bits 0-1: 00 left/01 center/10 right + /// bits 2-3: 00 top/01 middle/10 bottom + /// bits 4-5: font number presentation: [bool; 16], - /* - * Bit array. Default all zeros. - * bits 0-1: 00 left/01 center/10 right - * bits 2-3: 00 top/01 middle/10 bottom - * bits 4-5: font number - */ - path_type: i16, // Default 0 - width: i32, // Default 0 - invert_y: bool, // Vertical inversion. Default false. - mag: f64, // Scaling factor. Default 1. - angle_deg: f64, // Rotation (ccw). Default 0. - xy: Vec, // Position (1 pair only) - string: Vec, // Text content - properties: HashMap::> // Properties for the element. + + /// Default 0 + path_type: i16, + /// Default 0 + width: i32, + /// Vertical inversion. Default false. + invert_y: bool, + /// Scaling factor. Default 1. + mag: f64, + /// Rotation (ccw). Default 0. + angle_deg: f64, + /// Position (1 pair only) + xy: Vec, + /// Text content + string: Vec, + /// Properties for the element. + properties: HashMap::> } impl Element for Text { - fn parse(input: &[u8]) -> IResult<&[u8], Self> { + fn read(input: &[u8]) -> IResult { let mut path_type = 0; - let mut presentation = 0; + let mut presentation = [false; 16]; let mut invert_y = false; let mut width = 0; - let mut mag = 1; - let mut angle_deg = 0; + let mut mag = 1.0; + let mut angle_deg = 0.0; let (input, layer) = LAYER::skip_and_read(input)?; let (input, dtype) = TEXTTYPE::read(input)?; - let mut header = RecordHeader::parse(input)?; + let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_XY { match header.tag { records::RTAG_PRESENTATION => - {let (input, presentation) = PRESENTATION::read_data(input, header.data_size)?;}, + {(input, presentation) = PRESENTATION::read_data(input, header.data_size)?;}, records::RTAG_PATHTYPE => - {let (input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, + {(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;}, records::RTAG_WIDTH => - {let (input, width) = WIDTH::read_data(input, header.data_size)?;}, + {(input, width) = WIDTH::read_data(input, header.data_size)?;}, records::RTAG_STRANS => { - let (input, strans) = STRANS::read_data(input, header.data_size)?; - invert_y = strans[0]; + let result = STRANS::read_data(input, header.data_size)?; + input = result.0; + invert_y = result.1[0]; }, records::RTAG_MAG => - {let (input, mag) = MAG::read_data(input, header.data_size)?;}, + {(input, mag) = MAG::read_data(input, header.data_size)?;}, records::RTAG_ANGLE => - {let (input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, + {(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;}, _ => - return Err(format!("Unexpected tag {:04x}", header.tag)), + return fail(input, format!("Unexpected tag {:04x}", header.tag)), } - let (input, header) = RecordHeader::parse(input)?; + (input, header) = RecordHeader::read(input)?; } let (input, xy) = XY::read_data(input, header.data_size)?; let (input, string) = STRING::read(input)?; let (input, properties) = read_properties(input)?; - Text{ + Ok((input, Text{ layer: (layer, dtype), xy: xy, properties: properties, @@ -444,38 +492,41 @@ impl Element for Text { invert_y: invert_y, mag: mag, angle_deg: angle_deg, - } + })) } - fn write(&self, ww: W) -> OWResult { + fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += TEXT::write(ww)?; - size += LAYER::write(ww, self.layer[0])?; - size += TEXTTYPE::write(ww, self.layer[1])?; - if self.presentation != 0 { - size += PRESENTATION::write(ww, self.presentation)?; + size += TEXT::write(ww, &())?; + size += LAYER::write(ww, &self.layer.0)?; + size += TEXTTYPE::write(ww, &self.layer.1)?; + if self.presentation.iter().any(|&x| x) { + size += PRESENTATION::write(ww, &self.presentation)?; } if self.path_type != 0 { - size += PATHTYPE::write(ww, self.path_type)?; + size += PATHTYPE::write(ww, &self.path_type)?; } if self.width != 0 { - size += WIDTH::write(ww, self.width)? + size += WIDTH::write(ww, &self.width)? } - if self.angle_deg != 0 || self.mag != 1 || self.invert_y { - let strans = [false; 16]; - strans[0] = self.invert_y; - size += STRANS::write(ww, strans)?; - if self.mag != 1 { - size += MAG::write(ww, self.mag)?; + if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y { + let strans = { + let mut arr = [false; 16]; + arr[0] = self.invert_y; + arr + }; + size += STRANS::write(ww, &strans)?; + if self.mag != 1.0 { + size += MAG::write(ww, &self.mag)?; } - if self.angle_deg !=0 { - size += ANGLE::write(ww, self.angle_deg)?; + if self.angle_deg != 0.0 { + size += ANGLE::write(ww, &self.angle_deg)?; } } - size += XY::write(ww, self.xy)?; - size += STRING::write(ww, self.string)?; - size += write_properties(ww, self.properties)?; - size += ENDEL::write(ww)?; + size += XY::write(ww, &self.xy)?; + size += STRING::write(ww, &self.string)?; + size += write_properties(ww, &self.properties)?; + size += ENDEL::write(ww, &())?; Ok(size) } } diff --git a/src/lib.rs b/src/lib.rs index d1c5fd4..b17d455 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,10 @@ -extern crate nom; +//#![feature(generic_associated_types)] +#![feature(destructuring_assignment)] + extern crate byteorder; pub mod basic; pub mod record; pub mod records; pub mod elements; +pub mod library; diff --git a/src/library.rs b/src/library.rs index 7a8573a..06ff7ba 100644 --- a/src/library.rs +++ b/src/library.rs @@ -1,282 +1,392 @@ -/* - *File-level read/write functionality. - */ +/// +/// File-level read/write functionality. +/// -use nom; use std::io::Write; -use std::collections:HashMap; +use std::collections::HashMap; use record; +use record::{RecordHeader, Record}; use records; use elements; -//from .records import HEADER, BGNLIB, ENDLIB, UNITS, LIBNAME -//from .records import BGNSTR, STRNAME, ENDSTR, SNAME, COLROW, ENDEL -//from .records import BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF -//from .elements import Element, Reference, Text, Box, Boundary, Path, Node +use elements::{Element}; +use basic::{IResult, OResult, take_bytes, fail}; +const DEFAULT_DATE: [i16; 6] = [1900, 0, 0, 0, 0, 0]; + + +/// +/// Representation of the GDS file header. +/// +/// File header records: HEADER BGNLIB LIBNAME UNITS +/// Optional records are ignored if present and never written. +/// +/// Version is written as `600`. +/// +#[derive(Debug, Clone)] pub struct FileHeader { - /* - * Representation of the GDS file header. - * - * File header records: HEADER BGNLIB LIBNAME UNITS - * Optional records are ignored if present and never written. - * - * Version is written as `600`. - */ - name: Vec, // Library name - user_units_per_db_unit: f64, // Number of user units in one database unit - meters_per_db_unit: f64, // Number of meters in one database unit - mod_time: [u16; 6], // Last-modified time [y, m, d, h, m, s] - acc_time: [u16; 6], // Last-accessed time [y, m, d, h, m, s] + /// Number of user units in one database unit + user_units_per_db_unit: f64, + /// Number of meters in one database unit + meters_per_db_unit: f64, + /// Last-modified time [y, m, d, h, m, s] + mod_time: [i16; 6], + /// Last-accessed time [y, m, d, h, m, s] + acc_time: [i16; 6], + /// Library name + name: Vec, } impl FileHeader { pub fn new(name: &[u8], meters_per_db_unit: f64, user_units_per_db_unit: f64) -> Self { FileHeader{ - mod_time: [0, 1, 1, 0, 0, 0]; - acc_time: [0, 1, 1, 0, 0, 0]; + mod_time: [0, 1, 1, 0, 0, 0], + acc_time: [0, 1, 1, 0, 0, 0], name: name.to_owned(), user_units_per_db_unit: user_units_per_db_unit, meters_per_db_unit: meters_per_db_unit, } } - pub fn read(input: &[u8]) -> IResult<&[u8], Self> { - /* - * Read and construct a header from the provided stream. - * - * Args: - * stream: Seekable stream to read from - * - * Returns: - * FileHeader object - */ - let (input, version) = records::HEADER.read(input)?; - let (input, (mod_time, acc_time)) = records::BGNLIB.read(input)?; - let (input, name) = records::LIBNAME.skip_and_read(input)?; - let (input, (uu, dbu)) = records::UNITS.skip_and_read(input)?; + /// Read and construct a header from the provided input. + /// + /// Args: + /// input: Seekable input to read from + /// + /// Returns: + /// FileHeader object + /// + pub fn read(input: &[u8]) -> IResult { + let (input, _version) = records::HEADER::read(input)?; + let (input, [mod_time, acc_time]) = records::BGNLIB::read(input)?; + let (input, name) = records::LIBNAME::skip_and_read(input)?; + let (input, (uu, dbu)) = records::UNITS::skip_and_read(input)?; - FileHeader{ + Ok((input, FileHeader{ mod_time: mod_time, acc_time: acc_time, name: name, user_units_per_db_unit: uu, meters_per_db_unit: dbu, - } + })) } - pub fn write(&self, ww: W) -> OWResult { - /* - * Write the header to a stream - * - * Args: - * stream: Stream to write to - * - * Returns: - * number of bytes written - */ + /// Write the header to a input + /// + /// Args: + /// input: input to write to + /// + /// Returns: + /// number of bytes written + /// + pub fn write(&self, ww: &mut W) -> OResult { let mut size = 0; - size += records::HEADER.write(stream, 600) - size += records::BGNLIB.write(stream, [self.mod_time, self.acc_time]) - size += records::LIBNAME.write(stream, self.name) - size += records::UNITS.write(stream, (self.user_units_per_db_unit, self.meters_per_db_unit)) + size += records::HEADER::write(ww, &600)?; + size += records::BGNLIB::write(ww, &[self.mod_time, self.acc_time])?; + size += records::LIBNAME::write(ww, &self.name)?; + size += records::UNITS::write(ww, &(self.user_units_per_db_unit, self.meters_per_db_unit))?; Ok(size) } } -pub fn scan_structs(input: &[u8]) -> HashMap::, usize> { - /* - * Scan through a GDS file, building a table of - * {b'structure_name': byte_offset}. - * The intent of this function is to enable random access - * and/or partial (structure-by-structure) reads. - * - * Args: - * stream: Seekable stream to read from. Should be positioned - * before the first structure record, but possibly - * already past the file header. - */ +/// +/// Scan through a GDS file, building a table of +/// {b'structure_name': byte_offset}. +/// The intent of this function is to enable random access +/// and/or partial (structure-by-structure) reads. +/// +/// Args: +/// input: Seekable input to read from. Should be positioned +/// before the first structure record, but possibly +/// already past the file header. +/// +pub fn scan_structs(input: &[u8]) -> IResult, usize>> { let input_size = input.len(); - let positions = HashMap{}; + let mut positions = HashMap::new(); - let (input, header) = RecordHeader.parse(input)?; + let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_ENDLIB { - let (input, _) = nom::bytes::streaming::take(size)(input)?; - if tag == records::RTAG_BGNSTR { - let (input, name) = records::STRNAME.read(input)?; - if positions.conains_key(name) { - return Err(format!("Duplicate structure name: {}", name)); + (input, _) = take_bytes(input, header.data_size)?; + if header.tag == records::RTAG_BGNSTR { + let name; + (input, name) = records::STRNAME::read(input)?; + if positions.contains_key(&name) { + return fail(input, format!("Duplicate structure name: {:?}", name)); } let position = input_size - input.len(); positions.insert(name, position); } - let (input, header) = RecordHeader.parse(input)?; + (input, header) = RecordHeader::read(input)?; } - positions + Ok((input, positions)) } +#[derive(Debug, Clone)] pub struct Cell { + name: Vec, + boundaries: Vec, + paths: Vec, + nodes: Vec, + boxes: Vec, + texts: Vec, + refs: Vec, } impl Cell { + /// Build an empty cell + pub fn new(name: Vec) -> Self { + Cell{ + name: name, + boundaries: Vec::new(), + paths: Vec::new(), + nodes: Vec::new(), + boxes: Vec::new(), + texts: Vec::new(), + refs: Vec::new(), + } + } + + /// Skip to the next structure and attempt to read it. + /// + /// Args: + /// input: Seekable input to read from. + /// + /// Returns: + /// (name, elements) if a structure was found. + /// None if no structure was found before the end of the library. + /// + pub fn read(input: &[u8]) -> IResult> { + let (input, success) = records::BGNSTR::skip_past(input)?; + if !success { + return Ok((input, None)) + } + + let (input, name) = records::STRNAME::read(input)?; + let mut cell = Cell::new(name); + let (input, _) = cell.read_elements(input)?; + Ok((input, Some(cell))) + } + + /// Read elements from the input until an ENDSTR + /// record is encountered. The ENDSTR record is also + /// consumed. + /// + /// Args: + /// input: Seekable input to read from. + /// + /// Returns: + /// List of element objects. + /// + pub fn read_elements<'a>(&mut self, input: &'a [u8]) -> IResult<'a, ()> { + let (mut input, mut header) = RecordHeader::read(input)?; + while header.tag != records::RTAG_ENDSTR { + match header.tag { + records::RTAG_BOUNDARY => { + let boundary; + (input, _) = records::BOUNDARY::read(input)?; + (input, boundary) = elements::Boundary::read(input)?; + self.boundaries.push(boundary); + }, + records::RTAG_PATH => { + let path; + (input, _) = records::PATH::read(input)?; + (input, path) = elements::Path::read(input)?; + self.paths.push(path); + }, + records::RTAG_NODE => { + let node; + (input, _) = records::NODE::read(input)?; + (input, node) = elements::Node::read(input)?; + self.nodes.push(node); + }, + records::RTAG_BOX => { + let gds_box; + (input, _) = records::BOX::read(input)?; + (input, gds_box) = elements::GDSBox::read(input)?; + self.boxes.push(gds_box); + }, + records::RTAG_TEXT => { + let txt; + (input, _) = records::TEXT::read(input)?; + (input, txt) = elements::Text::read(input)?; + self.texts.push(txt); + }, + records::RTAG_SREF => { + let sref; + (input, _) = records::SREF::read(input)?; + (input, sref) = elements::Reference::read(input)?; + self.refs.push(sref); + }, + records::RTAG_AREF => { + let aref; + (input, _) = records::AREF::read(input)?; + (input, aref) = elements::Reference::read(input)?; + self.refs.push(aref); + }, + _ => { + // don't care, skip + (input, _) = take_bytes(input, header.data_size)?; + } + } + (input, header) = RecordHeader::read(input)?; + } + Ok((input, ())) + } + + /// + /// Write a structure to the provided input. + /// + /// Args: + /// name: Structure name (ascii-encoded). + /// elements: List of Elements containing the geometry and text in this struct. + /// cre_time: Creation time (optional). + /// mod_time: Modification time (optional). + /// + /// Return: + /// Number of bytes written + /// pub fn write( &self, - ww: Write, - name: &[u8], + ww: &mut W, cre_time: Option<[i16; 6]>, mod_time: Option<[i16; 6]>, - ) -> OWResult { - /* - * Write a structure to the provided stream. - * - * Args: - * name: Structure name (ascii-encoded). - * elements: List of Elements containing the geometry and text in this struct. - * cre_time: Creation time (optional). - * mod_time: Modification time (optional). - * - * Return: - * Number of bytes written - */ + ) -> OResult { let mut size = 0; - size += BGNSTR.write(ww, (cre_time, mod_time)) - size += STRNAME.write(ww, name) - size += cell.write(ww) - size += ENDSTR.write(ww) + size += records::BGNSTR::write(ww, &[cre_time.unwrap_or(DEFAULT_DATE), + mod_time.unwrap_or(DEFAULT_DATE)])?; + size += records::STRNAME::write(ww, &self.name)?; + size += self.write_elements(ww)?; + size += records::ENDSTR::write(ww, &())?; + Ok(size) + } + + pub fn write_elements(&self, ww: &mut W) -> OResult { + let mut size = 0; + for boundary in &self.boundaries { + size += boundary.write(ww)?; + } + for path in &self.paths { + size += path.write(ww)?; + } + for node in &self.nodes { + size += node.write(ww)?; + } + for gds_box in &self.boxes { + size += gds_box.write(ww)?; + } + for text in &self.texts { + size += text.write(ww)?; + } + for reference in &self.refs { + size += reference.write(ww)?; + } Ok(size) } } -pub fn try_read_struct(input: &[u8]) -> IResult<&[u8], Option<(Vec, Cell>)> { - /* - * Skip to the next structure and attempt to read it. - * - * Args: - * stream: Seekable stream to read from. - * - * Returns: - * (name, elements) if a structure was found. - * None if no structure was found before the end of the library. - */ - let (input, success) = records::BGNSTR.skip_past(input)?; - if !success { - return None - } - let (input, name) = records::STRNAME.read(input)?; - let (input, elements) = Cell::read_elements(input)?; - Some((name, elements)) -} +/// +/// Scan through a GDS file, building a table of instance counts +/// `{b'structure_name': {b'ref_name': count}}`. +/// +/// This is intended to provide a fast overview of the file's +/// contents without performing a full read of all elements. +/// +/// Args: +/// input: Seekable input to read from. Should be positioned +/// before the first structure record, but possibly +/// already past the file header. +/// +pub fn scan_hierarchy(input: &[u8]) -> IResult, HashMap::, u32>>> { + let mut structures = HashMap::new(); - -pub fn read_elements(stream: BinaryIO) -> List[Element] { - /* - * Read elements from the stream until an ENDSTR - * record is encountered. The ENDSTR record is also - * consumed. - * - * Args: - * stream: Seekable stream to read from. - * - * Returns: - * List of element objects. - */ - let (input, header) = RecordHeader.parse(input)?; - while header.tag != records::RTAG_ENDSTR { - match header.tag { - records::RTAG_BOUNDARY => { - let (input, boundary) = records::BOUNDARY.read(input)?; - cell.boundaries.insert(boundary); - }, - records::RTAG_PATH => { - let (input, path) = records::PATH.read(input)?; - cell.paths.insert(path); - }, - records::RTAG_NODE => { - let (input, node) = records::NODE.read(input)?; - cell.nodes.insert(node); - }, - records::RTAG_BOX => { - let (input, gds_box) = records::BOX.read(input)?; - cell.boxes.insert(gds_box); - }, - records::RTAG_TEXT => { - let (input, txt) = records::TEXT.read(input)?; - cell.texts.insert(txt); - }, - records::RTAG_SREF => { - let (input, sref) = records::SREF.read(input)?; - cell.refs.insert(sref); - }, - records::RTAG_AREF => { - let (input, aref) = records::AREF.read(input)?; - cell.refs.insert(aref); - }, - _ => { - // don't care, skip - let (input, _) = nom::bytes::streaming::take(size)(input)?; - } - } - let (input, header) = RecordHeader.parse(input)?; - } - Ok((input, data)) -} - - -pub fn scan_hierarchy(input: &[u8]) -> IResult<&[u8], HashMap::, HashMap::, u32>>> { - /* - * Scan through a GDS file, building a table of instance counts - * `{b'structure_name': {b'ref_name': count}}`. - * - * This is intended to provide a fast overview of the file's - * contents without performing a full read of all elements. - * - * Args: - * stream: Seekable stream to read from. Should be positioned - * before the first structure record, but possibly - * already past the file header. - */ - let structures = HashMap{}; - - let mut ref_name = None - let mut ref_count = None - let mut cur_structure = HashMap{}; - - let (input, header) = Record.read_header(stream) + let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_ENDLIB { match header.tag { records::RTAG_BGNSTR => { - let (input, _) = nom::bytes::streaming::take(size)(input)?; - let (input, name) = records::STRNAME.read(input)?; - if structures.contains_key(name) { - return Err(format!("Duplicate structure name: {}", name)); + (input, _) = take_bytes(input, header.data_size)?; + let result = records::STRNAME::read(input)?; + input = result.0; + let name = result.1; + if structures.contains_key(&name) { + return fail(input, format!("Duplicate structure name: {:?}", name)); } - cur_structure = HashMap{}; + let mut cur_structure = HashMap::new(); + let mut ref_name = None; + let mut ref_count = None; structures.insert(name, cur_structure); ref_name = None; ref_count = None; }, records::RTAG_SNAME => { - let (input, sname) = SNAME.read_data(input, header.data_size)?; - ref_name = Some(sname); + let result = records::SNAME::read_data(input, header.data_size)?; + input = result.0; + ref_name = Some(result.1); }, records::RTAG_COLROW => { - let (input, colrow) = COLROW.read_data(input, header.data_size); - ref_count = colrow[0] * colrow[1]; + let result = records::COLROW::read_data(input, header.data_size)?; + input = result.0; + let (col, row) = (result.1[0], result.1[1]); + ref_count = Some((col * row) as u32); }, records::RTAG_ENDEL => { *cur_structure.entry(ref_name.unwrap()).or_insert(0) += ref_count.unwrap_or(1); }, _ => { - let (input, _) = nom::bytes::streaming::take(size)(input)?; + (input, _) = take_bytes(input, header.data_size)?; }, } - let (input, header) = RecordHeader.parse(input)?; + (input, header) = RecordHeader::read(input)?; } - structures + Ok((input, structures)) +} + + + while header.tag != records::RTAG_ENDLIB { + +pub fn count_ref(input: &[u8]) -> IResult, u32))> { + let (input, found_struc) = records::BGNSTR.skip_past(input)?; + if not found_struc { + return Ok((input, None)) + } + let mut cur_structure = HashMap::new(); + let (input, name) = records::STRNAME::read(input)?; + if structures.contains_key(&name) { + return fail(input, format!("Duplicate structure name: {:?}", name)); + } + + let (mut input, mut header) = RecordHeader::read(input)?; + while header.tag != records::RTAG_ENDSTR { + let mut ref_name = None; + let mut ref_count = None; + while header.tag != records::RTAG_ENDEL { + match header.tag { + records::RTAG_SNAME => { + let result = records::SNAME::read_data(input1, header.data_size)?; + input1 = result.0; + ref_name = Some(result.1); + }, + records::RTAG_COLROW => { + let result = records::COLROW::read_data(input1, header.data_size)?; + input1 = result.0; + let (col, row) = (result.1[0], result.1[1]); + ref_count = Some((col * row) as u32); + }, + _ => { + (input1, _) = take_bytes(input1, header.data_size)?; + }, + } + (input1, header) = RecordHeader::read(input1)?; + } + // got ENDEL, update count for this reference + *cur_structure.entry(ref_name.unwrap()).or_insert(0) += ref_count.unwrap_or(1); + (input1, header) = RecordHeader::read(input1)?; + } + structures.insert(name, cur_structure); + (input, header) = RecordHeader::read(input1)?; } diff --git a/src/record.rs b/src/record.rs index 5f6775b..8d01c93 100644 --- a/src/record.rs +++ b/src/record.rs @@ -1,18 +1,15 @@ -/* - * Generic record-level read/write functionality. - */ -use nom::IResult; +/// +/// Generic record-level read/write functionality. +/// use std::io::Write; -use byteorder::BigEndian; +use std::convert::TryInto; +use byteorder::{ByteOrder, BigEndian}; use basic::{pack_datetime, pack_bitarray, pack_ascii, pack_int2, pack_int4, pack_real8}; #[warn(unused_imports)] use basic::{parse_datetime, parse_bitarray, parse_ascii, parse_int2, parse_int4, parse_real8}; #[warn(unused_imports)] -use basic::{OWResult}; +use basic::{OResult, IResult, fail, parse_u16, take_bytes}; use records; -//from .basic import parse_int2, parse_int4, parse_real8, parse_datetime, parse_bitarray -//from .basic import pack_int2, pack_int4, pack_real8, pack_datetime, pack_bitarray -//from .basic import parse_ascii, read //#[no_mangle] @@ -26,37 +23,45 @@ pub struct RecordHeader { } impl RecordHeader { - pub fn parse(input: &[u8]) -> IResult<&[u8], RecordHeader> { - let (_, size) = nom::number::streaming::be_u16(input[0..])?; - let (_, tag) = nom::number::streaming::be_u16(input[2..])?; - Ok((input[4..], RecordHeader{tag:tag, data_size:size - 4})) + pub fn read(input: &[u8]) -> IResult { + let (input, size) = parse_u16(input)?; + let (input, tag) = parse_u16(input)?; + Ok((input, RecordHeader{tag:tag, data_size:size - 4})) } - pub fn pack(self) -> [u8; 4] { - assert!(self.size < 0xffff - 4, "Record too big!"); - let vals = [self.size, self.tag]; + pub fn pack_into(&self) -> [u8; 4] { + assert!(self.data_size < 0xffff - 4, "Record too big!"); + let vals = [self.data_size, self.tag]; let mut buf = [0x77; 4]; BigEndian::write_u16_into(&vals, &mut buf); buf } - pub fn write(&self, ww: W) -> OWResult { - let bytes = self.pack(); - ww.write(bytes) + pub fn write(&self, ww: &mut W) -> OResult { + let bytes = self.pack_into(); + ww.write(&bytes) } } -pub trait Record { - fn tag() -> u32; - fn expected_size() -> Option; +pub trait RecordData { + type BareData; + type InData : ?Sized; + type ByteData : AsRef<[u8]>; - //fn parse_data(input: &[u8], size: usize) -> IResult<&[u8], Self>; + fn read(input: &[u8], size: u16) -> IResult; + fn pack_into(buf: &mut [u8], data: &Self::InData); + //fn size(data: &Self::BareData) -> u16; + fn pack(data: &Self::InData) -> Self::ByteData; } -impl Record { - pub fn check_size(&self, actual_size: usize) -> Result<(), &str> { - match self.expected_size() { + +pub trait Record { + fn tag() -> u16; + fn expected_size() -> Option; + + fn check_size(actual_size: u16) -> Result<(), String> { + match Self::expected_size() { Some(size) => if size == actual_size { Ok(()) } else { @@ -66,154 +71,323 @@ impl Record { } } - pub fn parse_header(input: &[u8]) -> IResult<&[u8], RecordHeader> { - RecordHeader::parse(input) + fn read_header(input: &[u8]) -> IResult { + RecordHeader::read(input) } - pub fn write_header(ww: W, data_size: usize) -> OWResult { - RecordHeader{tag: Self.tag(), size: data_size}.write(ww) + fn write_header(ww: &mut W, data_size: u16) -> OResult { + RecordHeader{tag: Self::tag(), data_size: data_size}.write(ww) } - pub fn skip_past(input: &[u8]) -> IResult<&[u8], bool> { - /* - * Skip to the end of the next occurence of this record. - * - * Return: - * True if the record was encountered and skipped. - * False if the end of the library was reached. - */ - let (input, header) = RecordHeader::parse(input)?; - while header.tag != Self.tag() { - let (input, _) = nom::bytes::streaming::take(header.size)?; + fn read_data(input: &[u8], size: u16) -> IResult { + RData::read(input, size) + } + + fn pack_data(buf: &mut [u8], data: &RData::InData) { + RData::pack_into(buf, data) + } + + /// + /// Skip to the end of the next occurence of this record. + /// + /// Return: + /// True if the record was encountered and skipped. + /// False if the end of the library was reached. + /// + fn skip_past(input: &[u8]) -> IResult { + let original_input = input; + let (mut input, mut header) = RecordHeader::read(input)?; + while header.tag != Self::tag() { + (input, _) = take_bytes(input, header.data_size)?; if header.tag == records::RTAG_ENDLIB { - return Ok((input, false)) + return Ok((original_input, false)) } - let (input, header) = RecordHeader::parse(input)?; + (input, header) = RecordHeader::read(input)?; } - let (input, _) = nom::bytes::streaming::take(header.size)?; + (input, _) = take_bytes(input, header.data_size)?; Ok((input, true)) } - /* - pub fn skip_and_read(input: &[u8]) -> IResult<&[u8], bool>{ - size, tag = Record.read_header(stream) - while tag != cls.tag{ - stream.seek(size, io.SEEK_CUR) - size, tag = Record.read_header(stream) + fn skip_and_read(input: &[u8]) -> IResult { + let (mut input, mut header) = RecordHeader::read(input)?; + while header.tag != Self::tag() { + (input, _) = take_bytes(input, header.data_size)?; + (input, header) = RecordHeader::read(input)?; } - data = cls.read_data(stream, size) - return data + let (input, data) = Self::read_data(input, header.data_size)?; + Ok((input, data)) } - def read(cls: Type[R], stream: BinaryIO){ - size = expect_record(stream, cls.tag) - data = cls.read_data(stream, size) - return data + fn expect_header(input: &[u8]) -> IResult { + let (input, header) = RecordHeader::read(input)?; + if header.tag != Self::tag() { + fail(input, format!("Unexpected record! Got tag 0x{:04x}, expected 0x{:04x}", header.tag, Self::tag())) + } else { + Ok((input, header.data_size)) + } } - def write(cls, stream: BinaryIO, data) -> int { - data_bytes = cls.pack_data(data) - b = cls.write_header(stream, len(data_bytes)) - b += stream.write(data_bytes) - return b + fn read(input: &[u8]) -> IResult { + let (input, size) = Self::expect_header(input)?; + Self::check_size(size).unwrap(); + let (input, data) = Self::read_data(input, size)?; + Ok((input, data)) + } + + fn write(ww: &mut W, data: &RData::InData) -> OResult { + let packed_data = RData::pack(data); + let data_bytes = packed_data.as_ref(); + let len: u16 = data_bytes.len().try_into().expect("Record longer than max size (u16)!"); + let mut size = 0; + size += Self::write_header(ww, len)?; + size += ww.write(data_bytes)?; + Ok(size) } - */ } +pub struct BitArray; +impl RecordData for BitArray { + type BareData = [bool; 16]; + type InData = [bool; 16]; + type ByteData = [u8; 2]; -pub trait BitArray { - fn parse_data(input: &[u8]) -> IResult<&[u8], [bool; 16]> { + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 2); parse_bitarray(input) } - fn pack_data(buf: &mut [u8], vals: &[bool; 16]) { - pack_bitarray(&mut buf, vals) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_bitarray(buf, data); + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 2]; + Self::pack_into(&mut buf, data); + buf } } -pub trait Int2 { - fn parse_data(input: &[u8]) -> IResult<&[u8], i16> { + +pub struct Int2; +impl RecordData for Int2 { + type BareData = i16; + type InData = i16; + type ByteData = [u8; 2]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 2); parse_int2(input) } - fn pack_data(buf: &mut [u8], val: i16) { - pack_int2(&mut buf, val) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_int2(buf, *data) + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 2]; + Self::pack_into(&mut buf, data); + buf } } -pub trait Int4 { - fn parse_data(input: &[u8]) -> IResult<&[u8], i32> { +pub struct Int4; +impl RecordData for Int4 { + type BareData = i32; + type InData = i32; + type ByteData = [u8; 4]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 4); parse_int4(input) } - fn pack_data(buf: &mut [u8], val: i32) { - pack_int4(&mut buf, val) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_int4(buf, *data) + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 4]; + Self::pack_into(&mut buf, data); + buf } } -pub trait Int2Array { - fn parse_data(input: &[u8], size: usize) -> IResult<&[u8], Vec> { + +pub struct Int2Array; +impl RecordData for Int2Array { + type BareData = Vec; + type InData = [i16]; + type ByteData = Vec; + + fn read(input: &[u8], size: u16) -> IResult { assert!(size % 2 == 0, "Record must contain an integer quantity of integers"); - nom::multi::count(parse_int2, size / 2)(input) + let mut buf = Vec::with_capacity(size as usize / 2); + let mut input = input; + for ii in 0..buf.len() { + (input, buf[ii]) = parse_int2(input)?; + } + Ok((input, buf)) } - fn pack_data(buf: &mut [u8], vals: &[i16]) { - BigEndian::write_i16_into(&vals, &mut buf) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + BigEndian::write_i16_into(&data, buf) + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = Vec::with_capacity(data.len() * 2); + Self::pack_into(&mut buf, data); + buf } } +pub struct Int4Array; +impl RecordData for Int4Array { + type BareData = Vec; + type InData = [i32]; + type ByteData = Vec; -pub trait Int4Array { - fn parse_data(input: &[u8], size: usize) -> IResult<&[u8], Vec> { + fn read(input: &[u8], size: u16) -> IResult { assert!(size % 4 == 0, "Record must contain an integer quantity of integers"); - nom::multi::count(parse_int4, size / 4)(input) + let mut buf = Vec::with_capacity(size as usize / 4); + let mut input = input; + for ii in 0..buf.len() { + (input, buf[ii]) = parse_int4(input)?; + } + Ok((input, buf)) } - fn pack_data(buf: &mut [u8], vals: &[i32]) { - BigEndian::write_i32_into(&vals, &mut buf) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + BigEndian::write_i32_into(&data, buf) + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = Vec::with_capacity(data.len() * 4); + Self::pack_into(&mut buf, data); + buf } } -pub trait Real8 { - fn parse_data(input: &[u8]) -> IResult<&[u8], f64> { +pub struct Real8; +impl RecordData for Real8 { + type BareData = f64; + type InData = f64; + type ByteData = [u8; 8]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 8); parse_real8(input) } - fn pack_data(buf: &mut [u8], val: f64) { - pack_real8(&mut buf, val) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_real8(buf, *data) + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 8]; + Self::pack_into(&mut buf, data); + buf } } -pub trait ASCII { - fn parse_data(input: &[u8]) -> IResult<&[u8], Vec> { - parse_ascii(input) +pub struct Real8Pair; +impl RecordData for Real8Pair { + type BareData = (f64, f64); + type InData = (f64, f64); + type ByteData = [u8; 2 * 8]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 2 * 8); + let (input, data0) = parse_real8(input)?; + let (input, data1) = parse_real8(input)?; + Ok((input, (data0, data1))) } - fn pack_data(buf: &mut [u8], data: &[u8]) { - pack_ascii(&mut buf, data) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_real8(&mut buf[8 * 0..], data.0); + pack_real8(&mut buf[8 * 1..], data.1); + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 2 * 8]; + Self::pack_into(&mut buf, data); + buf + } + + //fn write(ww: &mut W, data: &Self::BareData) -> OResult { + // let mut buf = [u8; 2 * 6 * 2]; + // Self::pack_into(&mut buf, data) + //} +} + +pub struct ASCII; +impl RecordData for ASCII { + type BareData = Vec; + type InData = [u8]; + type ByteData = Vec; + + fn read(input: &[u8], size: u16) -> IResult { + parse_ascii(input, size) + } + + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_ascii(buf, data); + } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = Vec::with_capacity(data.len() * 4); + Self::pack_into(&mut buf, data); + buf } } -pub trait DateTime { - fn parse_data(input: &[u8]) -> IResult<&[u8], [u16; 6]> { - parse_datetime(input) + +pub struct DateTimePair; +impl RecordData for DateTimePair { + type BareData = [[i16; 6]; 2]; + type InData = [[i16; 6]; 2]; + type ByteData = [u8; 2 * 6 * 2]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 2 * 6 * 2); + let (input, data0) = parse_datetime(input)?; + let (input, data1) = parse_datetime(input)?; + Ok((input, [data0, data1])) } - fn pack_data(buf: &mut [u8], data: [u16; 6]) { - pack_datetime(&mut buf, data) + fn pack_into(buf: &mut [u8], data: &Self::InData) { + pack_datetime(&mut buf[6 * 2 * 0..], &data[0]); + pack_datetime(&mut buf[6 * 2 * 1..], &data[1]); } + + fn pack(data: &Self::InData) -> Self::ByteData { + let mut buf = [0; 2 * 6 * 2]; + Self::pack_into(&mut buf, data); + buf + } + + //fn write(ww: &mut W, data: &Self::BareData) -> OResult { + // let mut buf = [u8; 2 * 6 * 2]; + // Self::pack_into(&mut buf, data) + //} } -impl DTR { - fn skip_and_read(input: &[u8]) -> IResult<&[u8], [DTR; 2]> { - let mut header = Self.read_header(input)?; - while header.tag != Self.tag() { - nom::bytes::streaming::take(header.data_size)?; - header = Self.read_header(input)?; - } - assert!(header.data_size == 6 * 2); - let data0 = Self.read_data(&input)?; - let data1 = Self.read_data(&input)?; - Ok([data0, data1]) +pub struct Empty; +impl RecordData for Empty { + type BareData = (); + type InData = (); + type ByteData = [u8; 0]; + + fn read(input: &[u8], size: u16) -> IResult { + assert!(size == 0); + Ok((input, ())) } + + fn pack_into(_buf: &mut [u8], _data: &Self::InData) { + } + + fn pack(_data: &Self::InData) -> Self::ByteData { + [] + } + //fn write(ww: &mut W, data: &Self::BareData) { + //} } diff --git a/src/records.rs b/src/records.rs index 92c5ad4..972cc78 100644 --- a/src/records.rs +++ b/src/records.rs @@ -1,9 +1,8 @@ -/* - * Record type and tag definitions - */ +/// +/// Record type and tag definitions +/// -use record::{Record, Int2, Int4, Int2Array, Int4Array, Real8, DateTime, BitArray, ASCII}; -//use basic::{OWResult}; +use record::{Record, Int2, Int4, Int2Array, Int4Array, Real8, Real8Pair, DateTimePair, BitArray, ASCII, Empty}; //use std::io::Write; @@ -91,7 +90,7 @@ pub const DATA_TYPE_STR: u16 = 0x06; pub const MAX_DATA_SIZE: usize = 8; /// Returns the size of the given data type in bytes. -pub fn data_size(t: u16) -> usize { +pub fn data_size(t: u16) -> Option { match t { x if x == DATA_TYPE_NONE => 0, x if x == DATA_TYPE_BIT => 2, @@ -103,215 +102,195 @@ pub fn data_size(t: u16) -> usize { } */ -pub struct HEADER {} -impl Record for HEADER { +pub struct HEADER; +impl Record for HEADER { fn tag() -> u16 { RTAG_HEADER } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for HEADER {} +//impl Record for HEADER; -pub struct BGNLIB {} -impl Record for BGNLIB { +pub struct BGNLIB; +impl Record for BGNLIB { fn tag() -> u16 { RTAG_BGNLIB } - fn expected_size() -> usize { Some(2 * 6) } + fn expected_size() -> Option { Some(2 * 6) } } -impl DateTime for BGNLIB {} -pub struct LIBNAME {} -impl Record for LIBNAME { +pub struct LIBNAME; +impl Record for LIBNAME { fn tag() -> u16 { RTAG_LIBNAME } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for LIBNAME {} -pub struct UNITS {} -impl Record for UNITS { +pub struct UNITS; +impl Record for UNITS { // (user_units_per_db_unit, db_units_per_meter) fn tag() -> u16 { RTAG_UNITS } - fn expected_size() -> usize { Some(2 * 8) } + fn expected_size() -> Option { Some(2 * 8) } } -impl Real8 for UNITS {} -pub struct ENDLIB {} -impl Record for ENDLIB { +pub struct ENDLIB; +impl Record for ENDLIB { fn tag() -> u16 { RTAG_ENDLIB } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct BGNSTR {} -impl Record for BGNSTR { +pub struct BGNSTR; +impl Record for BGNSTR { fn tag() -> u16 { RTAG_BGNSTR } - fn expected_size() -> usize { Some(2 * 6) } + fn expected_size() -> Option { Some(2 * 6) } } -impl DateTime for ENDLIB {} -pub struct STRNAME {} -impl Record for STRNAME { +pub struct STRNAME; +impl Record for STRNAME { fn tag() -> u16 { RTAG_STRNAME } - fn expected_size() -> usize { Some(2 * 6) } + fn expected_size() -> Option { Some(2 * 6) } } -impl ASCII for STRNAME {} -pub struct ENDSTR {} -impl Record for ENDSTR { +pub struct ENDSTR; +impl Record for ENDSTR { fn tag() -> u16 { RTAG_ENDSTR } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct BOUNDARY {} -impl Record for BOUNDARY { +pub struct BOUNDARY; +impl Record for BOUNDARY { fn tag() -> u16 { RTAG_BOUNDARY } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct PATH {} -impl Record for PATH { +pub struct PATH; +impl Record for PATH { fn tag() -> u16 { RTAG_PATH } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct SREF {} -impl Record for SREF { +pub struct SREF; +impl Record for SREF { fn tag() -> u16 { RTAG_SREF } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct AREF {} -impl Record for AREF { +pub struct AREF; +impl Record for AREF { fn tag() -> u16 { RTAG_AREF } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct TEXT {} -impl Record for TEXT { +pub struct TEXT; +impl Record for TEXT { fn tag() -> u16 { RTAG_TEXT } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct LAYER {} -impl Record for LAYER { +pub struct LAYER; +impl Record for LAYER { fn tag() -> u16 { RTAG_LAYER } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for LAYER {} -pub struct DATATYPE {} -impl Record for DATATYPE { +pub struct DATATYPE; +impl Record for DATATYPE { fn tag() -> u16 { RTAG_DATATYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for DATATYPE {} -pub struct WIDTH {} -impl Record for WIDTH { +pub struct WIDTH; +impl Record for WIDTH { fn tag() -> u16 { RTAG_WIDTH } - fn expected_size() -> usize { Some(4) } + fn expected_size() -> Option { Some(4) } } -impl Int4 for WIDTH {} -pub struct XY {} -impl Record for XY { +pub struct XY; +impl Record for XY { fn tag() -> u16 { RTAG_XY } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl Int4Array for XY {} -pub struct ENDEL {} -impl Record for ENDEL { +pub struct ENDEL; +impl Record for ENDEL { fn tag() -> u16 { RTAG_ENDEL } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct SNAME {} -impl Record for SNAME { +pub struct SNAME; +impl Record for SNAME { fn tag() -> u16 { RTAG_SNAME } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for SNAME {} -pub struct COLROW {} -impl Record for COLROW { +pub struct COLROW; +impl Record for COLROW { fn tag() -> u16 { RTAG_COLROW } - fn expected_size() -> usize { Some(4) } + fn expected_size() -> Option { Some(4) } } -impl Int2Array for COLROW {} -pub struct NODE {} -impl Record for NODE { +pub struct NODE; +impl Record for NODE { fn tag() -> u16 { RTAG_NODE } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct TEXTTYPE {} -impl Record for TEXTTYPE { +pub struct TEXTTYPE; +impl Record for TEXTTYPE { fn tag() -> u16 { RTAG_TEXTTYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for TEXTTYPE {} -pub struct PRESENTATION {} -impl Record for PRESENTATION { +pub struct PRESENTATION; +impl Record for PRESENTATION { fn tag() -> u16 { RTAG_PRESENTATION } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl BitArray for PRESENTATION {} -pub struct SPACING {} -impl Record for SPACING { +pub struct SPACING; +impl Record for SPACING { fn tag() -> u16 { RTAG_SPACING } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for SPACING {} -pub struct STRING {} -impl Record for STRING { +pub struct STRING; +impl Record for STRING { fn tag() -> u16 { RTAG_STRING } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for STRING {} -pub struct STRANS {} -impl Record for STRANS { +pub struct STRANS; +impl Record for STRANS { fn tag() -> u16 { RTAG_STRANS } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl BitArray for STRANS {} -pub struct MAG {} -impl Record for MAG { +pub struct MAG; +impl Record for MAG { fn tag() -> u16 { RTAG_MAG } - fn expected_size() -> usize { Some(8) } + fn expected_size() -> Option { Some(8) } } -impl Real8 for MAG {} -pub struct ANGLE {} -impl Record for ANGLE { +pub struct ANGLE; +impl Record for ANGLE { fn tag() -> u16 { RTAG_ANGLE } - fn expected_size() -> usize { Some(8) } + fn expected_size() -> Option { Some(8) } } -impl Real8 for ANGLE {} -pub struct UINTEGER {} -impl Record for UINTEGER { +pub struct UINTEGER; +impl Record for UINTEGER { fn tag() -> u16 { RTAG_UINTEGER } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for UINTEGER {} -pub struct USTRING {} -impl Record for USTRING { +pub struct USTRING; +impl Record for USTRING { fn tag() -> u16 { RTAG_USTRING } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for USTRING {} -pub struct REFLIBS {} -impl Record for REFLIBS { +pub struct REFLIBS; +impl Record for REFLIBS { fn tag() -> u16 { RTAG_REFLIBS } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } impl REFLIBS { - fn check_size(&self, actual_size: usize) -> Result<(), &str> { + fn check_size(actual_size: usize) -> Result<(), String> { if actual_size % 44 == 0 { Ok(()) } else { @@ -319,15 +298,14 @@ impl REFLIBS { } } } -impl ASCII for REFLIBS {} -pub struct FONTS {} -impl Record for FONTS { +pub struct FONTS; +impl Record for FONTS { fn tag() -> u16 { RTAG_FONTS } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } impl FONTS { - fn check_size(&self, actual_size: usize) -> Result<(), &str> { + fn check_size(actual_size: usize) -> Result<(), String> { if actual_size % 44 == 0 { Ok(()) } else { @@ -335,29 +313,26 @@ impl FONTS { } } } -impl ASCII for FONTS {} -pub struct PATHTYPE {} -impl Record for PATHTYPE { +pub struct PATHTYPE; +impl Record for PATHTYPE { fn tag() -> u16 { RTAG_PATHTYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for PATHTYPE {} -pub struct GENERATIONS {} -impl Record for GENERATIONS { +pub struct GENERATIONS; +impl Record for GENERATIONS { fn tag() -> u16 { RTAG_GENERATIONS } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for GENERATIONS {} -pub struct ATTRTABLE {} -impl Record for ATTRTABLE { +pub struct ATTRTABLE; +impl Record for ATTRTABLE { fn tag() -> u16 { RTAG_ATTRTABLE } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } impl ATTRTABLE { - fn check_size(&self, actual_size: usize) -> Result<(), &str> { + fn check_size(actual_size: usize) -> Result<(), String> { if actual_size % 44 == 0 { Ok(()) } else { @@ -365,231 +340,208 @@ impl ATTRTABLE { } } } -impl ASCII for ATTRTABLE {} -pub struct STYPTABLE {} -impl Record for STYPTABLE { +pub struct STYPTABLE; +impl Record for STYPTABLE { fn tag() -> u16 { RTAG_STYPTABLE } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for STYPTABLE {} -pub struct STRTYPE {} -impl Record for STRTYPE { +pub struct STRTYPE; +impl Record for STRTYPE { fn tag() -> u16 { RTAG_STRTYPE } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl Int2 for STRTYPE {} -pub struct ELFLAGS {} -impl Record for ELFLAGS { +pub struct ELFLAGS; +impl Record for ELFLAGS { fn tag() -> u16 { RTAG_ELFLAGS } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl BitArray for ELFLAGS {} -pub struct ELKEY {} -impl Record for ELKEY { +pub struct ELKEY; +impl Record for ELKEY { fn tag() -> u16 { RTAG_ELKEY } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for ELKEY {} -pub struct LINKTYPE {} -impl Record for LINKTYPE { +pub struct LINKTYPE; +impl Record for LINKTYPE { fn tag() -> u16 { RTAG_LINKTYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for LINKTYPE {} -pub struct LINKKEYS {} -impl Record for LINKKEYS { +pub struct LINKKEYS; +impl Record for LINKKEYS { fn tag() -> u16 { RTAG_LINKKEYS } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for LINKKEYS {} -pub struct NODETYPE {} -impl Record for NODETYPE { +pub struct NODETYPE; +impl Record for NODETYPE { fn tag() -> u16 { RTAG_NODETYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for NODETYPE {} -pub struct PROPATTR {} -impl Record for PROPATTR { +pub struct PROPATTR; +impl Record for PROPATTR { fn tag() -> u16 { RTAG_PROPATTR } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for PROPATTR {} -pub struct PROPVALUE {} -impl Record for PROPVALUE { +pub struct PROPVALUE; +impl Record for PROPVALUE { fn tag() -> u16 { RTAG_PROPVALUE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl ASCII for PROPVALUE {} -pub struct BOX {} -impl Record for BOX { +pub struct BOX; +impl Record for BOX { fn tag() -> u16 { RTAG_BOX } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct BOXTYPE {} -impl Record for BOXTYPE { +pub struct BOXTYPE; +impl Record for BOXTYPE { fn tag() -> u16 { RTAG_BOXTYPE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for BOXTYPE {} -pub struct PLEX {} -impl Record for PLEX { +pub struct PLEX; +impl Record for PLEX { fn tag() -> u16 { RTAG_PLEX } - fn expected_size() -> usize { Some(4) } + fn expected_size() -> Option { Some(4) } } -impl Int4 for PLEX {} -pub struct BGNEXTN {} -impl Record for BGNEXTN { +pub struct BGNEXTN; +impl Record for BGNEXTN { fn tag() -> u16 { RTAG_BGNEXTN } - fn expected_size() -> usize { Some(4) } + fn expected_size() -> Option { Some(4) } } -impl Int4 for BGNEXTN {} -pub struct ENDEXTN {} -impl Record for ENDEXTN { +pub struct ENDEXTN; +impl Record for ENDEXTN { fn tag() -> u16 { RTAG_ENDEXTN } - fn expected_size() -> usize { Some(4) } + fn expected_size() -> Option { Some(4) } } -impl Int4 for ENDEXTN {} -pub struct TAPENUM {} -impl Record for TAPENUM { +pub struct TAPENUM; +impl Record for TAPENUM { fn tag() -> u16 { RTAG_TAPENUM } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for TAPENUM {} -pub struct TAPECODE {} -impl Record for TAPECODE { +pub struct TAPECODE; +impl Record for TAPECODE { fn tag() -> u16 { RTAG_TAPECODE } - fn expected_size() -> usize { Some(2 * 6) } + fn expected_size() -> Option { Some(2 * 6) } } -impl Int2Array for TAPECODE {} -pub struct STRCLASS {} -impl Record for STRCLASS { +pub struct STRCLASS; +impl Record for STRCLASS { fn tag() -> u16 { RTAG_STRCLASS } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for STRCLASS {} -pub struct RESERVED {} -impl Record for RESERVED { +pub struct RESERVED; +impl Record for RESERVED { fn tag() -> u16 { RTAG_RESERVED } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2Array for RESERVED {} -pub struct FORMAT {} -impl Record for FORMAT { +pub struct FORMAT; +impl Record for FORMAT { fn tag() -> u16 { RTAG_FORMAT } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for FORMAT {} -pub struct MASK {} -impl Record for MASK { +pub struct MASK; +impl Record for MASK { fn tag() -> u16 { RTAG_MASK } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for MASK {} -pub struct ENDMASKS {} -impl Record for ENDMASKS { - // End of MASKS records +/// End of MASKS records +pub struct ENDMASKS; +impl Record for ENDMASKS { fn tag() -> u16 { RTAG_ENDMASKS } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct LIBDIRSIZE {} -impl Record for LIBDIRSIZE { +pub struct LIBDIRSIZE; +impl Record for LIBDIRSIZE { fn tag() -> u16 { RTAG_LIBDIRSIZE } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for LIBDIRSIZE {} -pub struct SRFNAME {} -impl Record for SRFNAME { +pub struct SRFNAME; +impl Record for SRFNAME { fn tag() -> u16 { RTAG_SRFNAME } - fn expected_size() -> usize { None } + fn expected_size() -> Option { None } } -impl ASCII for SRFNAME {} -pub struct LIBSECUR {} -impl Record for LIBSECUR { +pub struct LIBSECUR; +impl Record for LIBSECUR { fn tag() -> u16 { RTAG_LIBSECUR } - fn expected_size() -> usize { Some(2) } + fn expected_size() -> Option { Some(2) } } -impl Int2 for LIBSECUR {} -pub struct BORDER {} -impl Record for BORDER { +pub struct BORDER; +impl Record for BORDER { fn tag() -> u16 { RTAG_BORDER } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct SOFTFENCE {} -impl Record for SOFTFENCE { +pub struct SOFTFENCE; +impl Record for SOFTFENCE { fn tag() -> u16 { RTAG_SOFTFENCE } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct HARDFENCE {} -impl Record for HARDFENCE { +pub struct HARDFENCE; +impl Record for HARDFENCE { fn tag() -> u16 { RTAG_HARDFENCE } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct SOFTWIRE {} -impl Record for SOFTWIRE { +pub struct SOFTWIRE; +impl Record for SOFTWIRE { fn tag() -> u16 { RTAG_SOFTWIRE } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct HARDWIRE {} -impl Record for HARDWIRE { +pub struct HARDWIRE; +impl Record for HARDWIRE { fn tag() -> u16 { RTAG_HARDWIRE } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct PATHPORT {} -impl Record for PATHPORT { +pub struct PATHPORT; +impl Record for PATHPORT { fn tag() -> u16 { RTAG_PATHPORT } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct NODEPORT {} -impl Record for NODEPORT { +pub struct NODEPORT; +impl Record for NODEPORT { fn tag() -> u16 { RTAG_NODEPORT } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct USERCONSTRAINT {} -impl Record for USERCONSTRAINT { +pub struct USERCONSTRAINT; +impl Record for USERCONSTRAINT { fn tag() -> u16 { RTAG_USERCONSTRAINT } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct SPACERERROR {} -impl Record for SPACERERROR { +pub struct SPACERERROR; +impl Record for SPACERERROR { fn tag() -> u16 { RTAG_SPACERERROR } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } -pub struct CONTACT {} -impl Record for CONTACT { +pub struct CONTACT; +impl Record for CONTACT { fn tag() -> u16 { RTAG_CONTACT } - fn expected_size() -> usize { Some(0) } + fn expected_size() -> Option { Some(0) } } diff --git a/src/test_basic.rs b/src/test_basic.rs deleted file mode 100644 index 6bd8bee..0000000 --- a/src/test_basic.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* -import struct - -import pytest # type: ignore -import numpy # type: ignore -from numpy.testing import assert_array_equal # type: ignore - -from .basic import parse_bitarray, parse_int2, parse_int4, parse_real8, parse_ascii -from .basic import pack_bitarray, pack_int2, pack_int4, pack_real8, pack_ascii -from .basic import decode_real8, encode_real8 - -from .basic import KlamathError - - -def test_parse_bitarray(): - assert(parse_bitarray(b'59') == 13625) - assert(parse_bitarray(b'\0\0') == 0) - assert(parse_bitarray(b'\xff\xff') == 65535) - - # 4 bytes (too long) - with pytest.raises(KlamathError): - parse_bitarray(b'4321') - - # empty data - with pytest.raises(KlamathError): - parse_bitarray(b'') - - -def test_parse_int2(): - assert_array_equal(parse_int2(b'59\xff\xff\0\0'), (13625, -1, 0)) - - # odd length - with pytest.raises(KlamathError): - parse_int2(b'54321') - - # empty data - with pytest.raises(KlamathError): - parse_int2(b'') - - -def test_parse_int4(): - assert_array_equal(parse_int4(b'4321'), (875770417,)) - - # length % 4 != 0 - with pytest.raises(KlamathError): - parse_int4(b'654321') - - # empty data - with pytest.raises(KlamathError): - parse_int4(b'') - - -def test_decode_real8(): - # zeroes - assert(decode_real8(numpy.array([0x0])) == 0) - assert(decode_real8(numpy.array([1<<63])) == 0) # negative - assert(decode_real8(numpy.array([0xff << 56])) == 0) # denormalized - - assert(decode_real8(numpy.array([0x4110 << 48])) == 1.0) - assert(decode_real8(numpy.array([0xC120 << 48])) == -2.0) - - -def test_parse_real8(): - packed = struct.pack('>3Q', 0x0, 0x4110_0000_0000_0000, 0xC120_0000_0000_0000) - assert_array_equal(parse_real8(packed), (0.0, 1.0, -2.0)) - - # length % 8 != 0 - with pytest.raises(KlamathError): - parse_real8(b'0987654321') - - # empty data - with pytest.raises(KlamathError): - parse_real8(b'') - - -def test_parse_ascii(): - # empty data - with pytest.raises(KlamathError): - parse_ascii(b'') - - assert(parse_ascii(b'12345') == b'12345') - assert(parse_ascii(b'12345\0') == b'12345') # strips trailing null byte - - -def test_pack_bitarray(): - packed = pack_bitarray(321) - assert(len(packed) == 2) - assert(packed == struct.pack('>H', 321)) - - -def 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)) - - -def 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)) - - -def 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) - - -def 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) - - -def test_pack_ascii(): - assert(pack_ascii(b'4321') == b'4321') - assert(pack_ascii(b'321') == b'321\0') -*/