klamath-rs/src/library.rs

283 lines
9.1 KiB
Rust
Raw Normal View History

2021-12-18 21:05:00 -08:00
/*
*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<u8>, // 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<W: 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::<Vec<u8>, 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<W: 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<u8>, 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::<Vec<u8>, HashMap::<Vec<u8>, 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
}