/// /// File-level read/write functionality. /// use std::io::Write; //use std::collections::HashMap; pub use crate::record; pub use crate::record::{RecordHeader, Record}; pub use crate::records; pub use crate::elements; pub use crate::elements::{read_elements, DListBuilder, FListBuilder}; pub use crate::basic::{IResult, OResult, take_bytes, fail}; use std::string::String; use std::collections::HashMap; use std::sync::Arc; use arrow::datatypes::{DataType, Field, Fields}; use arrow::array::{ StructBuilder, StringBuilder, UInt64Builder, UInt32Builder, Int16Builder, Float64Builder, StructArray, }; /// /// 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, 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, acc_time, 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) } } pub fn read_library(input: &[u8]) -> IResult { let input_size = input.len(); let property_t = DataType::Struct(Fields::from(vec![ Field::new("key", DataType::Int16, false), Field::new("value", DataType::Utf8, false), ])); let property_list_t = DataType::List(Arc::new( Field::new_list_field(property_t, false) )); let repetition_struct_t = DataType::Struct(Fields::from(vec![ Field::new("x0", DataType::Int32, false), Field::new("y0", DataType::Int32, false), Field::new("x1", DataType::Int32, false), Field::new("y1", DataType::Int32, false), Field::new("count0", DataType::Int16, false), Field::new("count1", DataType::Int16, false), ])); let ref_struct_t = DataType::Struct(Fields::from(vec![ Field::new("target", DataType::UInt32, false), Field::new("invert_y", DataType::Boolean, true), Field::new("mag", DataType::Float64, true), Field::new("angle_deg", DataType::Float64, true), Field::new("x", DataType::Int32, false), Field::new("y", DataType::Int32, false), Field::new("repetition", repetition_struct_t, true), Field::new("properties", property_list_t.clone(), true), ])); let text_struct_t = DataType::Struct(Fields::from(vec![ Field::new("layer", DataType::UInt32, false), Field::new("presentation_horiz", DataType::UInt8, true), Field::new("presentation_vert", DataType::UInt8, true), Field::new("presentation_font", DataType::UInt8, true), Field::new("path_type", DataType::Int16, true), Field::new("width", DataType::Int32, true), Field::new("invert_y", DataType::Boolean, true), Field::new("mag", DataType::Float64, true), Field::new("angle_deg", DataType::Float64, true), Field::new("x", DataType::Int32, false), Field::new("y", DataType::Int32, false), Field::new("string", DataType::Utf8, false), Field::new("properties", property_list_t.clone(), true), ])); let coords_t = DataType::List(Arc::new( Field::new_list_field(DataType::Int32, false) )); let boundary_struct_t = DataType::Struct(Fields::from(vec![ Field::new("layer", DataType::UInt32, false), Field::new("xy", coords_t.clone(), false), Field::new("properties", property_list_t.clone(), true), ])); let path_struct_t = DataType::Struct(Fields::from(vec![ Field::new("layer", DataType::UInt32, false), Field::new("path_type", DataType::Int16, true), Field::new("extension_start", DataType::Int32, true), Field::new("extension_end", DataType::Int32, true), Field::new("width", DataType::Int32, false), Field::new("xy", coords_t.clone(), false), Field::new("properties", property_list_t.clone(), true), ])); let boxnode_struct_t = DataType::Struct(Fields::from(vec![ Field::new("layer", DataType::UInt32, false), Field::new("xy", coords_t.clone(), false), Field::new("properties", property_list_t.clone(), true), ])); let ref_list_t = DataType::List(Arc::new( Field::new_list_field(ref_struct_t, false) )); let text_list_t = DataType::List(Arc::new( Field::new_list_field(text_struct_t, false) )); let boundary_list_t = DataType::List(Arc::new( Field::new_list_field(boundary_struct_t, false) )); let path_list_t = DataType::List(Arc::new( Field::new_list_field(path_struct_t, false) )); let boxnode_list_t = DataType::List(Arc::new( Field::new_list_field(boxnode_struct_t, false) )); let name_list_t = DataType::List(Arc::new( Field::new_list_field(DataType::Utf8, false) )); let layer_list_t = DataType::List(Arc::new( Field::new_list_field(DataType::UInt32, false) )); let time_t = DataType::FixedSizeList(Arc::new( Field::new_list_field(DataType::Int16, false), ), 6, ); let cell_struct_t = DataType::Struct(Fields::from(vec![ Field::new("id", DataType::UInt32, false), Field::new("file_offset", DataType::UInt64, false), Field::new("refs", ref_list_t, false), Field::new("boundaries", boundary_list_t, false), Field::new("paths", path_list_t, false), Field::new("nodes", boxnode_list_t.clone(), true), Field::new("boxes", boxnode_list_t.clone(), true), Field::new("texts", text_list_t, false), ])); let cells_list_t = DataType::List(Arc::new( Field::new_list_field(cell_struct_t, false) )); let mut lib_builder = StructBuilder::from_fields(vec![ Field::new("meters_per_db_unit", DataType::Float64, false), Field::new("user_units_per_db_unit", DataType::Float64, false), Field::new("lib_name", DataType::Utf8, false), Field::new("mod_time", time_t.clone(), false), Field::new("acc_time", time_t.clone(), false), Field::new("cells", cells_list_t, false), Field::new("cell_names", name_list_t, false), Field::new("layers", layer_list_t, false), ], 0, ); let (input, header) = FileHeader::read(input)?; let dbu_builder = lib_builder.field_builder::(0).unwrap(); dbu_builder.append_value(header.meters_per_db_unit); let uu_builder = lib_builder.field_builder::(1).unwrap(); uu_builder.append_value(header.user_units_per_db_unit); let libname_builder = lib_builder.field_builder::(2).unwrap(); libname_builder.append_value(String::from_utf8(header.name).unwrap()); let mtl_builder = lib_builder.field_builder::(3).unwrap(); let mt_builder = mtl_builder.values().as_any_mut().downcast_mut::().unwrap(); mt_builder.append_values(&header.mod_time, &[true; 6]); mtl_builder.append(true); let atl_builder = lib_builder.field_builder::(4).unwrap(); let at_builder = atl_builder.values().as_any_mut().downcast_mut::().unwrap(); at_builder.append_values(&header.acc_time, &[true; 6]); atl_builder.append(true); let mut names = HashMap::::new(); let mut layers = HashMap::::new(); let cells_builder = lib_builder.field_builder::(5).unwrap(); 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_bytes; (input, name_bytes) = records::STRNAME::read(input)?; let name = String::from_utf8(name_bytes).unwrap(); println!("{name}"); let next_id = names.len(); let id = names.entry(name).or_insert(next_id.try_into().unwrap()); let position = input_size - input.len(); let cell_builder = cells_builder.values().as_any_mut().downcast_mut::().unwrap(); let id_builder = cell_builder.field_builder::(0).unwrap(); id_builder.append_value(*id); let offset_builder = cell_builder.field_builder::(1).unwrap(); offset_builder.append_value(position.try_into().unwrap()); (input, _) = read_elements(input, cell_builder, &mut names, &mut layers)?; cell_builder.append(true); } (input, header) = RecordHeader::read(input)?; } cells_builder.append(true); let mut ids: HashMap = names.into_iter().map(|(kk, vv)| (vv, kk)).collect(); let names_builder = lib_builder.field_builder::(6).unwrap(); let name_builder = names_builder.values().as_any_mut().downcast_mut::().unwrap(); for id in 0..ids.len() { name_builder.append_value(ids.remove(&id.try_into().unwrap()).unwrap()); } names_builder.append(true); let mut layer_ids: HashMap = layers.into_iter().map(|(kk, vv)| (vv, kk)).collect(); let layers_builder = lib_builder.field_builder::(7).unwrap(); let layer_builder = layers_builder.values().as_any_mut().downcast_mut::().unwrap(); for layer_id in 0..layer_ids.len() { layer_builder.append_value(layer_ids.remove(&layer_id.try_into().unwrap()).unwrap()); } layers_builder.append(true); lib_builder.append(true); let lib = lib_builder.finish(); Ok((input, lib)) } /* /// /// 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)?; } */