/* *File-level read/write functionality. */ use nom; use std::io::Write; use std::collections:HashMap; use 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 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] } 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, } } 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)?; 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 */ 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)) 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. */ let input_size = input.len(); let positions = HashMap{}; let (input, header) = RecordHeader.parse(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)); } let position = input_size - input.len(); positions.insert(name, position); } let (input, header) = RecordHeader.parse(input)?; } positions } pub struct Cell { } impl Cell { pub fn write( &self, ww: Write, name: &[u8], 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 */ 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) 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)) } 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) 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)); } cur_structure = HashMap{}; 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); }, records::RTAG_COLROW => { let (input, colrow) = COLROW.read_data(input, header.data_size); ref_count = colrow[0] * colrow[1]; }, 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)?; }, } let (input, header) = RecordHeader.parse(input)?; } structures }