use std::convert::{TryFrom, TryInto};
use std::error::Error;
use std::fs::File;
#[allow(unused_imports)]
use std::io::prelude::*;
use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::{fmt, mem, str};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use chrono::prelude::*;
use chrono::{Datelike, NaiveDate, NaiveDateTime};
use derive_more::{self, Add, AddAssign, Sub, SubAssign};
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use serde::{Deserialize, Serialize};
#[macro_use]
extern crate derive_builder;
use layout21utils as utils;
#[doc(inline)]
pub use utils::{SerdeFile, SerializationFormat};
#[doc(hidden)]
mod read;
use read::{GdsParser, GdsScanner, GdsStructScan};
#[doc(hidden)]
mod write;
use write::GdsWriter;
#[cfg(test)]
mod tests;
#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
pub enum GdsRecordType {
Header = 0x00,
BgnLib,
LibName,
Units,
EndLib,
BgnStruct,
StructName, EndStruct,
Boundary,
Path,
StructRef,
ArrayRef,
Text,
Layer,
DataType,
Width,
Xy,
EndElement,
StructRefName, ColRow,
TextNode, Node,
TextType,
Presentation,
Spacing, String,
Strans,
Mag,
Angle,
Uinteger, Ustring, RefLibs,
Fonts,
PathType,
Generations,
AttrTable,
StypTable, StrType, ElemFlags,
ElemKey, LinkType, LinkKeys, Nodetype,
PropAttr,
PropValue,
Box,
BoxType,
Plex,
BeginExtn, EndExtn, TapeNum,
TapeCode,
StrClass, Reserved, Format,
Mask,
EndMasks,
LibDirSize,
SrfName,
LibSecur,
}
impl GdsRecordType {
pub fn valid(&self) -> bool {
match self {
Self::TextNode | Self::Spacing | Self::Uinteger | Self::Ustring | Self::StypTable | Self::StrType | Self::ElemKey | Self::LinkType | Self::LinkKeys | Self::StrClass | Self::Reserved => false,
_ => true,
}
}
}
#[derive(FromPrimitive, Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
pub enum GdsDataType {
NoData = 0,
BitArray = 1,
I16 = 2,
I32 = 3,
F32 = 4,
F64 = 5,
Str = 6,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
pub struct GdsRecordHeader {
rtype: GdsRecordType,
dtype: GdsDataType,
len: u16,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum GdsRecord {
Header { version: i16 },
BgnLib { dates: Vec<i16> }, LibName(String),
Units(f64, f64),
EndLib,
BgnStruct { dates: Vec<i16> }, StructName(String), StructRefName(String), EndStruct,
Boundary,
Path,
StructRef,
ArrayRef,
Text,
Layer(i16),
DataType(i16),
Width(i32),
Xy(Vec<i32>),
EndElement,
ColRow { cols: i16, rows: i16 },
Node,
TextType(i16),
Presentation(u8, u8),
String(String),
Strans(u8, u8),
Mag(f64),
Angle(f64),
RefLibs(String),
Fonts(String),
PathType(i16),
Generations(i16),
AttrTable(String),
ElemFlags(u8, u8),
Nodetype(i16),
PropAttr(i16),
PropValue(String),
Box,
BoxType(i16),
Plex(i32),
BeginExtn(i32),
EndExtn(i32),
TapeNum(i16),
TapeCode(Vec<i16>), Format(i16),
Mask(String),
EndMasks,
LibDirSize(i16),
SrfName(String),
LibSecur(i16),
}
pub struct GdsFloat64;
impl GdsFloat64 {
pub fn decode(val: u64) -> f64 {
let neg = (val & 0x8000_0000_0000_0000) != 0;
let exp: i32 = ((val & 0x7F00_0000_0000_0000) >> 8 * 7) as i32 - 64;
let mantissa: u64 = val & 0x00FF_FFFF_FFFF_FFFF;
let mantissa: f64 = mantissa as f64 / 2f64.powi(8 * 7);
if neg {
-1.0 * mantissa * 16f64.powi(exp)
} else {
mantissa * 16f64.powi(exp)
}
}
pub fn encode(mut val: f64) -> u64 {
if val == 0.0 {
return 0;
};
let mut top: u8 = 0;
if val < 0.0 {
top = 0x80;
val = -val;
}
let fexp: f64 = 0.25 * val.log2();
let mut exponent = fexp.ceil() as i32;
if fexp == fexp.ceil() {
exponent += 1;
}
let mantissa: u64 = (val * 16_f64.powi(14 - exponent)).round() as u64;
top += (64 + exponent) as u8;
let result: u64 = (top as u64).wrapping_shl(56) | (mantissa & 0x00FF_FFFF_FFFF_FFFF);
result
}
}
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct Unsupported;
#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct GdsStrans {
pub reflected: bool,
pub abs_mag: bool,
pub abs_angle: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mag: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub angle: Option<f64>,
}
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct GdsPresentation(u8, u8);
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct GdsElemFlags(u8, u8);
#[derive(Default, Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct GdsPlex(i32);
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct GdsUnits(f64, f64);
impl GdsUnits {
pub fn new(num1: f64, num2: f64) -> Self {
Self(num1, num2)
}
pub fn db_unit(&self) -> f64 {
self.1
}
pub fn user_unit(&self) -> f64 {
self.0 / self.1
}
}
impl Default for GdsUnits {
fn default() -> Self {
Self(1e-3, 1e-9)
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
pub struct GdsPoint {
pub x: i32,
pub y: i32,
}
impl GdsPoint {
pub fn new(x: i32, y: i32) -> Self {
GdsPoint { x, y }
}
pub fn vec(pts: &[(i32, i32)]) -> Vec<Self> {
pts.iter().map(|pt| Self::new(pt.0, pt.1)).collect()
}
fn parse(from: &Vec<i32>) -> GdsResult<Self> {
if from.len() != 2 {
return Err(GdsError::Str(
"GdsPoint coordinate vector: Invalid number of elements".into(),
));
}
Ok(GdsPoint {
x: from[0],
y: from[1],
})
}
fn parse_vec(from: &[i32]) -> GdsResult<Vec<GdsPoint>> {
if from.len() % 2 != 0 {
return Err(GdsError::Str(
"GdsPoint coordinate vector: Invalid number of elements".into(),
));
}
let mut rv = Vec::with_capacity(from.len() / 2);
for i in 0..from.len() / 2 {
rv.push(GdsPoint {
x: from[i * 2],
y: from[i * 2 + 1],
});
}
Ok(rv)
}
fn flatten(&self) -> Vec<i32> {
vec![self.x, self.y]
}
fn flatten_vec(src: &Vec<GdsPoint>) -> Vec<i32> {
let mut rv = Vec::with_capacity(src.len() * 2);
for pt in src.iter() {
rv.push(pt.x);
rv.push(pt.y);
}
rv
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub enum GdsFormatType {
Archive,
Filtered(Vec<Unsupported>),
}
#[derive(Default, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct GdsProperty {
pub attr: i16,
pub value: String,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsPath {
pub layer: i16,
pub datatype: i16,
pub xy: Vec<GdsPoint>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub width: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub path_type: Option<i16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub begin_extn: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub end_extn: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsBoundary {
pub layer: i16,
pub datatype: i16,
pub xy: Vec<GdsPoint>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsStructRef {
pub name: String,
pub xy: GdsPoint,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub strans: Option<GdsStrans>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsArrayRef {
pub name: String,
pub xy: [GdsPoint; 3],
pub cols: i16,
pub rows: i16,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub strans: Option<GdsStrans>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsTextElem {
pub string: String,
pub layer: i16,
pub texttype: i16,
pub xy: GdsPoint,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub presentation: Option<GdsPresentation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub path_type: Option<i16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub width: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default)]
pub strans: Option<GdsStrans>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsNode {
pub layer: i16,
pub nodetype: i16,
pub xy: Vec<GdsPoint>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsBox {
pub layer: i16,
pub boxtype: i16,
pub xy: [GdsPoint; 5],
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub elflags: Option<GdsElemFlags>,
#[serde(default, skip_serializing_if = "Option::is_none")]
#[builder(default, setter(strip_option))]
pub plex: Option<GdsPlex>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[builder(default, setter(strip_option))]
pub properties: Vec<GdsProperty>,
}
#[derive(derive_more::From, Debug, Clone, Deserialize, Serialize, PartialEq)]
pub enum GdsElement {
GdsBoundary(GdsBoundary),
GdsPath(GdsPath),
GdsStructRef(GdsStructRef),
GdsArrayRef(GdsArrayRef),
GdsTextElem(GdsTextElem),
GdsNode(GdsNode),
GdsBox(GdsBox),
}
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Add, AddAssign, Sub, SubAssign)]
pub struct GdsStats {
libraries: usize,
structs: usize,
boundaries: usize,
paths: usize,
struct_refs: usize,
array_refs: usize,
text_elems: usize,
nodes: usize,
boxes: usize,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct GdsDateTimes {
pub modified: NaiveDateTime,
pub accessed: NaiveDateTime,
}
impl Default for GdsDateTimes {
fn default() -> Self {
let now = Utc::now().naive_utc();
Self {
modified: now.clone(),
accessed: now.clone(),
}
}
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsStruct {
pub name: String,
pub dates: GdsDateTimes,
pub elems: Vec<GdsElement>,
}
impl GdsStruct {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
..Default::default()
}
}
fn stats(&self) -> GdsStats {
let mut stats = GdsStats::default();
stats.structs += 1;
for elem in &self.elems {
use GdsElement::*;
match elem {
GdsBoundary(_) => stats.boundaries += 1,
GdsPath(_) => stats.paths += 1,
GdsStructRef(_) => stats.struct_refs += 1,
GdsArrayRef(_) => stats.array_refs += 1,
GdsTextElem(_) => stats.text_elems += 1,
GdsNode(_) => stats.nodes += 1,
GdsBox(_) => stats.boxes += 1,
};
}
stats
}
}
#[derive(Default, Clone, Builder, Debug, Deserialize, Serialize, PartialEq)]
#[builder(pattern = "owned", setter(into), private)]
pub struct GdsLibrary {
pub name: String,
pub version: i16,
pub dates: GdsDateTimes,
pub units: GdsUnits,
pub structs: Vec<GdsStruct>,
#[serde(default, skip_serializing)]
#[builder(default)]
pub libdirsize: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub srfname: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub libsecur: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub reflibs: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub fonts: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub attrtable: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub generations: Unsupported,
#[serde(default, skip_serializing)]
#[builder(default)]
pub format_type: Unsupported,
}
impl GdsLibrary {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
version: 3,
..Default::default()
}
}
pub fn load(fname: impl AsRef<Path>) -> GdsResult<GdsLibrary> {
GdsParser::open(fname)?.parse_lib()
}
pub fn from_bytes(bytes: Vec<u8>) -> GdsResult<GdsLibrary> {
GdsParser::from_bytes(bytes)?.parse_lib()
}
#[allow(dead_code)] fn scan(fname: impl AsRef<Path>) -> GdsResult<Vec<GdsStructScan>> {
GdsScanner::scan(fname)
}
pub fn stats(&self) -> GdsStats {
let mut stats = GdsStats::default();
stats.libraries += 1;
for strukt in self.structs.iter() {
stats += strukt.stats();
}
stats
}
pub fn save(&self, fname: impl AsRef<Path>) -> GdsResult<()> {
let mut wr = GdsWriter::open(fname)?;
wr.write_lib(self)
}
pub fn write(&self, file: impl Write) -> GdsResult<()> {
let mut wr = GdsWriter::new(file);
wr.write_lib(self)
}
}
impl SerdeFile for GdsLibrary {}
impl SerdeFile for GdsStruct {}
pub struct GdsLayerSpec {
pub layer: i16,
pub xtype: i16,
}
pub trait HasLayer {
fn layerspec(&self) -> GdsLayerSpec;
}
impl GdsLayerSpec {
pub fn new(layer: i16, xtype: i16) -> GdsLayerSpec {
GdsLayerSpec { layer, xtype }
}
}
impl HasLayer for GdsBoundary {
fn layerspec(&self) -> GdsLayerSpec {
GdsLayerSpec::new(self.layer, self.datatype)
}
}
impl HasLayer for GdsTextElem {
fn layerspec(&self) -> GdsLayerSpec {
GdsLayerSpec::new(self.layer, self.texttype)
}
}
impl HasLayer for GdsNode {
fn layerspec(&self) -> GdsLayerSpec {
GdsLayerSpec::new(self.layer, self.nodetype)
}
}
impl HasLayer for GdsBox {
fn layerspec(&self) -> GdsLayerSpec {
GdsLayerSpec::new(self.layer, self.boxtype)
}
}
impl HasLayer for GdsPath {
fn layerspec(&self) -> GdsLayerSpec {
GdsLayerSpec::new(self.layer, self.datatype)
}
}
#[derive(Debug, Clone)]
pub enum GdsContext {
Library,
Struct,
StructRef,
ArrayRef,
Boundary,
Box,
Path,
Text,
Node,
Property,
}
pub type GdsResult<T> = Result<T, GdsError>;
#[derive(Debug)]
pub enum GdsError {
RecordDecode(GdsRecordType, GdsDataType, u16),
RecordLen(usize),
InvalidDataType(u8),
InvalidRecordType(u8),
Unsupported(Option<GdsRecord>, Option<GdsContext>),
Parse {
msg: String,
record: GdsRecord,
recordnum: usize,
bytepos: u64,
ctx: Vec<GdsContext>,
},
Boxed(Box<dyn Error>),
Str(String),
}
impl std::fmt::Display for GdsError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl std::error::Error for GdsError {}
impl From<std::io::Error> for GdsError {
fn from(e: std::io::Error) -> Self {
Self::Boxed(Box::new(e))
}
}
impl From<std::str::Utf8Error> for GdsError {
fn from(e: std::str::Utf8Error) -> Self {
Self::Boxed(Box::new(e))
}
}
impl From<String> for GdsError {
fn from(e: String) -> Self {
GdsError::Str(e)
}
}
impl From<&str> for GdsError {
fn from(e: &str) -> Self {
GdsError::Str(e.to_string())
}
}
#[cfg(any(test, feature = "selftest"))]
pub fn roundtrip(lib: &GdsLibrary) -> GdsResult<()> {
use tempfile::tempfile;
let mut file = tempfile()?;
lib.write(&mut file)?;
file.seek(SeekFrom::Start(0))?;
let mut bytes = Vec::new();
file.read_to_end(&mut bytes)?;
let lib2 = GdsLibrary::from_bytes(bytes)?;
assert_eq!(*lib, lib2);
Ok(())
}