2022-03-20 16:28:34 -07:00
|
|
|
///
|
|
|
|
/// File-level read/write functionality.
|
|
|
|
///
|
2021-12-18 21:05:00 -08:00
|
|
|
|
|
|
|
use std::io::Write;
|
2022-03-20 16:28:34 -07:00
|
|
|
use std::collections::HashMap;
|
2021-12-18 21:05:00 -08:00
|
|
|
|
|
|
|
use record;
|
2022-03-20 16:28:34 -07:00
|
|
|
use record::{RecordHeader, Record};
|
2021-12-18 21:05:00 -08:00
|
|
|
use records;
|
|
|
|
use elements;
|
2022-03-20 16:28:34 -07:00
|
|
|
use elements::{Element};
|
|
|
|
use basic::{IResult, OResult, take_bytes, fail};
|
2021-12-18 21:05:00 -08:00
|
|
|
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
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)]
|
2021-12-18 21:05:00 -08:00
|
|
|
pub struct FileHeader {
|
2022-03-20 16:28:34 -07:00
|
|
|
/// 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<u8>,
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FileHeader {
|
|
|
|
pub fn new(name: &[u8], meters_per_db_unit: f64, user_units_per_db_unit: f64) -> Self {
|
|
|
|
FileHeader{
|
2022-03-20 16:28:34 -07:00
|
|
|
mod_time: [0, 1, 1, 0, 0, 0],
|
|
|
|
acc_time: [0, 1, 1, 0, 0, 0],
|
2021-12-18 21:05:00 -08:00
|
|
|
name: name.to_owned(),
|
|
|
|
user_units_per_db_unit: user_units_per_db_unit,
|
|
|
|
meters_per_db_unit: meters_per_db_unit,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
/// 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<Self> {
|
|
|
|
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)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
Ok((input, FileHeader{
|
2021-12-18 21:05:00 -08:00
|
|
|
mod_time: mod_time,
|
|
|
|
acc_time: acc_time,
|
|
|
|
name: name,
|
|
|
|
user_units_per_db_unit: uu,
|
|
|
|
meters_per_db_unit: dbu,
|
2022-03-20 16:28:34 -07:00
|
|
|
}))
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
/// Write the header to a input
|
|
|
|
///
|
|
|
|
/// Args:
|
|
|
|
/// input: input to write to
|
|
|
|
///
|
|
|
|
/// Returns:
|
|
|
|
/// number of bytes written
|
|
|
|
///
|
|
|
|
pub fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
2021-12-18 21:05:00 -08:00
|
|
|
let mut size = 0;
|
2022-03-20 16:28:34 -07:00
|
|
|
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))?;
|
2021-12-18 21:05:00 -08:00
|
|
|
Ok(size)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
///
|
|
|
|
/// 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<HashMap::<Vec<u8>, usize>> {
|
2021-12-18 21:05:00 -08:00
|
|
|
let input_size = input.len();
|
2022-03-20 16:28:34 -07:00
|
|
|
let mut positions = HashMap::new();
|
2021-12-18 21:05:00 -08:00
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
let (mut input, mut header) = RecordHeader::read(input)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
while header.tag != records::RTAG_ENDLIB {
|
2022-03-20 16:28:34 -07:00
|
|
|
(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));
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
let position = input_size - input.len();
|
|
|
|
positions.insert(name, position);
|
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
(input, header) = RecordHeader::read(input)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
Ok((input, positions))
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
#[derive(Debug, Clone)]
|
2021-12-18 21:05:00 -08:00
|
|
|
pub struct Cell {
|
2022-03-20 16:28:34 -07:00
|
|
|
name: Vec<u8>,
|
2021-12-18 21:05:00 -08:00
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
boundaries: Vec<elements::Boundary>,
|
|
|
|
paths: Vec<elements::Path>,
|
|
|
|
nodes: Vec<elements::Node>,
|
|
|
|
boxes: Vec<elements::GDSBox>,
|
|
|
|
texts: Vec<elements::Text>,
|
|
|
|
refs: Vec<elements::Reference>,
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Cell {
|
2022-03-20 16:28:34 -07:00
|
|
|
/// Build an empty cell
|
|
|
|
pub fn new(name: Vec<u8>) -> 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<Option<Cell>> {
|
|
|
|
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
|
|
|
|
///
|
2021-12-18 21:05:00 -08:00
|
|
|
pub fn write<W: Write>(
|
|
|
|
&self,
|
2022-03-20 16:28:34 -07:00
|
|
|
ww: &mut W,
|
2021-12-18 21:05:00 -08:00
|
|
|
cre_time: Option<[i16; 6]>,
|
|
|
|
mod_time: Option<[i16; 6]>,
|
2022-03-20 16:28:34 -07:00
|
|
|
) -> OResult {
|
2021-12-18 21:05:00 -08:00
|
|
|
let mut size = 0;
|
2022-03-20 16:28:34 -07:00
|
|
|
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, &())?;
|
2021-12-18 21:05:00 -08:00
|
|
|
Ok(size)
|
|
|
|
}
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
pub fn write_elements<W: Write>(&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)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
Ok(size)
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
///
|
|
|
|
/// 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::<Vec<u8>, HashMap::<Vec<u8>, u32>>> {
|
|
|
|
let mut structures = HashMap::new();
|
2021-12-18 21:05:00 -08:00
|
|
|
|
|
|
|
|
2022-03-20 16:28:34 -07:00
|
|
|
let (mut input, mut header) = RecordHeader::read(input)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
while header.tag != records::RTAG_ENDLIB {
|
|
|
|
match header.tag {
|
|
|
|
records::RTAG_BGNSTR => {
|
2022-03-20 16:28:34 -07:00
|
|
|
(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));
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
let mut cur_structure = HashMap::new();
|
|
|
|
let mut ref_name = None;
|
|
|
|
let mut ref_count = None;
|
2021-12-18 21:05:00 -08:00
|
|
|
structures.insert(name, cur_structure);
|
|
|
|
ref_name = None;
|
|
|
|
ref_count = None;
|
|
|
|
},
|
|
|
|
records::RTAG_SNAME => {
|
2022-03-20 16:28:34 -07:00
|
|
|
let result = records::SNAME::read_data(input, header.data_size)?;
|
|
|
|
input = result.0;
|
|
|
|
ref_name = Some(result.1);
|
2021-12-18 21:05:00 -08:00
|
|
|
},
|
|
|
|
records::RTAG_COLROW => {
|
2022-03-20 16:28:34 -07:00
|
|
|
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);
|
2021-12-18 21:05:00 -08:00
|
|
|
},
|
|
|
|
records::RTAG_ENDEL => {
|
|
|
|
*cur_structure.entry(ref_name.unwrap()).or_insert(0) += ref_count.unwrap_or(1);
|
|
|
|
},
|
|
|
|
_ => {
|
2022-03-20 16:28:34 -07:00
|
|
|
(input, _) = take_bytes(input, header.data_size)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
},
|
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
(input, header) = RecordHeader::read(input)?;
|
|
|
|
}
|
|
|
|
Ok((input, structures))
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
while header.tag != records::RTAG_ENDLIB {
|
|
|
|
|
|
|
|
pub fn count_ref(input: &[u8]) -> IResult<Option((Vec<u8>, 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)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|
2022-03-20 16:28:34 -07:00
|
|
|
structures.insert(name, cur_structure);
|
|
|
|
(input, header) = RecordHeader::read(input1)?;
|
2021-12-18 21:05:00 -08:00
|
|
|
}
|