/// /// File-level read/write functionality. /// use std::io::Write; use std::collections::HashMap; use crate::record; use crate::record::{RecordHeader, Record}; use crate::records; use crate::elements; use crate::elements::{Element}; use crate::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 { /// 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], name: name.to_owned(), user_units_per_db_unit: user_units_per_db_unit, meters_per_db_unit: meters_per_db_unit, } } /// 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)?; Ok((input, FileHeader{ mod_time: mod_time, acc_time: acc_time, name: name, user_units_per_db_unit: uu, meters_per_db_unit: dbu, })) } /// 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(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) } } /// /// 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 mut positions = HashMap::new(); let (mut input, mut header) = RecordHeader::read(input)?; while header.tag != records::RTAG_ENDLIB { (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); } (input, header) = RecordHeader::read(input)?; } 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: &mut W, cre_time: Option<[i16; 6]>, mod_time: Option<[i16; 6]>, ) -> OResult { let mut size = 0; 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) } } /* /// /// 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(); let mut ref_name = None; let mut ref_count = None; let (mut input, mut header) = RecordHeader::read(input)?; let mut cur_structure = HashMap::new(); while header.tag != records::RTAG_ENDLIB { match header.tag { records::RTAG_BGNSTR => { (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)); } let mut cur_structure = HashMap::new(); structures.insert(name, cur_structure); ref_name = None; ref_count = None; }, records::RTAG_SNAME => { let result = records::SNAME::read_data(input, header.data_size)?; input = result.0; ref_name = Some(result.1); }, records::RTAG_COLROW => { 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); }, _ => { (input, _) = take_bytes(input, header.data_size)?; }, } (input, header) = RecordHeader::read(input)?; } Ok((input, structures)) } */ /* pub fn count_ref(input: &[u8]) -> IResult, u32))> { let (input, found_struc) = records::BGNSTR.skip_past(input)?; if !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)?; } */