Compare commits
No commits in common. "master" and "arrow" have entirely different histories.
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
||||
[package]
|
||||
name = "klamath_rs_ext"
|
||||
version = "0.2.0"
|
||||
authors = ["jan <jan@mpxd.net>"]
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[lib]
|
||||
name = "klamath_rs_ext"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
byteorder = "^1"
|
||||
arrow = {version = "^54", features = ["ffi"]}
|
||||
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
6
klamath_rs_ext/__init__.py
Normal file
6
klamath_rs_ext/__init__.py
Normal file
@ -0,0 +1,6 @@
|
||||
from .basic import pack_int2 as pack_int2
|
||||
from .basic import pack_int4 as pack_int4
|
||||
|
||||
|
||||
__version__ = 0.2
|
||||
|
92
klamath_rs_ext/basic.py
Normal file
92
klamath_rs_ext/basic.py
Normal file
@ -0,0 +1,92 @@
|
||||
from collections.abc import Sequence
|
||||
import ctypes
|
||||
from pathlib import Path
|
||||
from itertools import chain
|
||||
|
||||
import numpy
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from .klamath_rs_ext import lib, ffi
|
||||
|
||||
|
||||
CONV_TABLE_i16 = {
|
||||
numpy.float64: lib.f64_to_i16,
|
||||
numpy.float32: lib.f32_to_i16,
|
||||
numpy.int64: lib.i64_to_i16,
|
||||
numpy.int32: lib.i32_to_i16,
|
||||
numpy.int16: lib.i16_to_i16,
|
||||
numpy.uint64: lib.u64_to_i16,
|
||||
numpy.uint32: lib.u32_to_i16,
|
||||
numpy.uint16: lib.u16_to_i16,
|
||||
}
|
||||
|
||||
CONV_TABLE_i32 = {
|
||||
numpy.float64: lib.f64_to_i32,
|
||||
numpy.float32: lib.f32_to_i32,
|
||||
numpy.int64: lib.i64_to_i32,
|
||||
numpy.int32: lib.i32_to_i32,
|
||||
numpy.uint64: lib.u64_to_i32,
|
||||
numpy.uint32: lib.u32_to_i32,
|
||||
}
|
||||
|
||||
|
||||
|
||||
def pack_int2(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
|
||||
arr = numpy.atleast_1d(data)
|
||||
|
||||
for dtype in CONV_TABLE_i16.keys():
|
||||
if arr.dtype != dtype:
|
||||
continue
|
||||
|
||||
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
||||
if arr is data:
|
||||
arr = numpy.array(arr, copy=True)
|
||||
|
||||
fn = CONV_TABLE_i16[dtype]
|
||||
buf = ffi.from_buffer(ffi.typeof(fn).args[0], arr, require_writable=True)
|
||||
result = fn(buf, arr.size)
|
||||
|
||||
if result != 0:
|
||||
raise ValueError(f'Invalid value for conversion to Int2: {result}')
|
||||
|
||||
i2arr = arr.view('>i2')[::arr.itemsize // 2]
|
||||
return i2arr.tobytes()
|
||||
|
||||
if arr.dtype == numpy.dtype('>i2'):
|
||||
return arr.tobytes()
|
||||
|
||||
if (arr > 32767).any() or (arr < -32768).any():
|
||||
raise Exception(f'int2 data out of range: {arr}')
|
||||
|
||||
return arr.astype('>i2').tobytes()
|
||||
|
||||
|
||||
def pack_int4(data: NDArray[numpy.integer] | Sequence[int] | int) -> bytes:
|
||||
arr = numpy.atleast_1d(data)
|
||||
|
||||
for dtype in CONV_TABLE_i32.keys():
|
||||
if arr.dtype != dtype:
|
||||
continue
|
||||
|
||||
arr = numpy.require(arr, requirements=('C_CONTIGUOUS', 'ALIGNED', 'WRITEABLE', 'OWNDATA'))
|
||||
if arr is data:
|
||||
arr = numpy.array(arr, copy=True)
|
||||
|
||||
fn = CONV_TABLE_i32[dtype]
|
||||
buf = ffi.from_buffer(ffi.typeof(fn).args[0], arr, require_writable=True)
|
||||
result = fn(buf, arr.size)
|
||||
|
||||
if result != 0:
|
||||
raise ValueError(f'Invalid value for conversion to Int4: {result}')
|
||||
|
||||
i4arr = arr.view('>i4')[::arr.itemsize // 4]
|
||||
return i4arr.tobytes()
|
||||
|
||||
if arr.dtype == numpy.dtype('>i4'):
|
||||
return arr.tobytes()
|
||||
|
||||
if (arr > 2147483647).any() or (arr < -2147483648).any():
|
||||
raise Exception(f'int4 data out of range: {arr}')
|
||||
|
||||
return arr.astype('>i4').tobytes()
|
||||
|
0
klamath_rs_ext/py.typed
Normal file
0
klamath_rs_ext/py.typed
Normal file
37
pyproject.toml
Normal file
37
pyproject.toml
Normal file
@ -0,0 +1,37 @@
|
||||
[build-system]
|
||||
requires = ["maturin>1.0,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "klamath_rs_ext"
|
||||
description = "Compiled extensions for klamath GDS library"
|
||||
#readme = "README.md"
|
||||
#license = { file = "LICENSE.md" }
|
||||
authors = [
|
||||
{ name="Jan Petykiewicz", email="jan@mpxd.net" },
|
||||
]
|
||||
homepage = "https://mpxd.net/code/jan/klamath-rs"
|
||||
repository = "https://mpxd.net/code/jan/klamath-rs"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Development Status :: 4 - Beta",
|
||||
# "Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Information Technology",
|
||||
"Intended Audience :: Manufacturing",
|
||||
"Intended Audience :: Science/Research",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)",
|
||||
]
|
||||
requires-python = ">=3.11"
|
||||
#include = [
|
||||
# "LICENSE.md"
|
||||
# ]
|
||||
dynamic = ["version"]
|
||||
dependencies = [
|
||||
"cffi",
|
||||
]
|
||||
|
||||
[tool.maturin]
|
||||
bindings = "cffi"
|
||||
|
393
src/basic.rs
Normal file
393
src/basic.rs
Normal file
@ -0,0 +1,393 @@
|
||||
///
|
||||
/// Functionality for parsing and writing basic data types
|
||||
///
|
||||
use byteorder::{ByteOrder, BigEndian};
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
|
||||
pub type OResult = Result<usize, io::Error>;
|
||||
|
||||
pub type IResult<'a, O> = Result<(&'a [u8], O), (&'a [u8], ErrType)>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ErrType {
|
||||
Incomplete(Option<usize>),
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
pub fn fail<O>(input: &[u8], msg: String) -> IResult<O> {
|
||||
Err((input, ErrType::Failed(msg)))
|
||||
}
|
||||
|
||||
pub fn incomplete<O>(input: &[u8], size: Option<usize>) -> IResult<O> {
|
||||
Err((input, ErrType::Incomplete(size)))
|
||||
}
|
||||
|
||||
pub fn take_bytes<CC: Into<usize>>(input: &[u8], count: CC) -> IResult<&[u8]> {
|
||||
let cc = count.into();
|
||||
if input.len() < cc {
|
||||
incomplete(input, Some(cc))
|
||||
} else {
|
||||
let (taken, input) = input.split_at(cc);
|
||||
Ok((input, taken))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse functions
|
||||
*/
|
||||
pub fn parse_u16(input: &[u8]) -> IResult<u16> {
|
||||
let (input, buf) = take_bytes(input, 2_usize)?;
|
||||
let val = BigEndian::read_u16(buf);
|
||||
Ok((input, val))
|
||||
}
|
||||
|
||||
pub fn parse_int2(input: &[u8]) -> IResult<i16> {
|
||||
let (input, buf) = take_bytes(input, 2_usize)?;
|
||||
let val = BigEndian::read_i16(buf);
|
||||
Ok((input, val))
|
||||
}
|
||||
|
||||
pub fn parse_int4(input: &[u8]) -> IResult<i32> {
|
||||
let (input, buf) = take_bytes(input, 4_usize)?;
|
||||
let val = BigEndian::read_i32(buf);
|
||||
Ok((input, val))
|
||||
}
|
||||
|
||||
/// Convert GDS REAL8 to IEEE float64
|
||||
pub fn decode_real8(int: u64) -> f64 {
|
||||
let neg = int & 0x8000_0000_0000_0000;
|
||||
let exp = (int >> 56) & 0x7f;
|
||||
let mut mant = (int & 0x00ff_ffff_ffff_ffff) as f64;
|
||||
if neg != 0 {
|
||||
mant *= -1.0
|
||||
}
|
||||
let exp2 = 4 * (exp as i32 - 64) - 56;
|
||||
mant * 2_f64.powi(exp2)
|
||||
}
|
||||
|
||||
pub fn parse_real8(input: &[u8]) -> IResult<f64> {
|
||||
let (input, buf) = take_bytes(input, 8_usize)?;
|
||||
let data = BigEndian::read_u64(buf);
|
||||
Ok((input, decode_real8(data)))
|
||||
}
|
||||
|
||||
|
||||
pub fn parse_datetime(input: &[u8]) -> IResult<[i16; 6]> {
|
||||
let mut buf = [0_i16; 6];
|
||||
let mut input = input;
|
||||
for bb in &mut buf {
|
||||
(input, *bb) = parse_int2(input)?;
|
||||
}
|
||||
buf[0] += 1900; // Year is from 1900
|
||||
Ok((input, buf))
|
||||
}
|
||||
|
||||
|
||||
pub fn parse_bitarray(input: &[u8]) -> IResult<[bool; 16]> {
|
||||
let mut bits = [false; 16];
|
||||
let (input, val) = parse_int2(input)?;
|
||||
for (ii, bit) in bits.iter_mut().enumerate() {
|
||||
*bit = ((val >> (16 - 1 - ii)) & 0x01) == 1;
|
||||
}
|
||||
Ok((input, bits))
|
||||
}
|
||||
|
||||
|
||||
pub fn parse_ascii(input: &[u8], length: u16) -> IResult<Vec<u8>> {
|
||||
let length = length as usize;
|
||||
let (input, data) = take_bytes(input, length)?;
|
||||
let last = data[length - 1];
|
||||
let true_length = if last == 0 { length - 1 } else { length };
|
||||
let vec = data[..true_length].to_vec();
|
||||
Ok((input, vec))
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Pack functions
|
||||
*/
|
||||
|
||||
pub fn bitarray2int(bits: &[bool; 16]) -> u16 {
|
||||
let mut int: u16 = 0;
|
||||
for (ii, bit) in bits.iter().enumerate() {
|
||||
int |= (*bit as u16) << (16 - 1 - ii);
|
||||
}
|
||||
int
|
||||
}
|
||||
|
||||
pub fn pack_bitarray(buf: &mut [u8], bits: &[bool; 16]) {
|
||||
BigEndian::write_u16(buf, bitarray2int(bits));
|
||||
}
|
||||
|
||||
|
||||
pub fn pack_int2(buf: &mut [u8], int: i16) {
|
||||
BigEndian::write_i16(buf, int);
|
||||
}
|
||||
|
||||
pub fn pack_int4(buf: &mut [u8], int: i32) {
|
||||
BigEndian::write_i32(buf, int);
|
||||
}
|
||||
|
||||
pub fn pack_real8(buf: &mut [u8], fnum: f64) -> Result<(), FloatTooBigError> {
|
||||
BigEndian::write_u64(buf, encode_real8(fnum)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pack_ascii(buf: &mut [u8], data: &[u8]) -> usize {
|
||||
let len = data.len();
|
||||
buf[..len].copy_from_slice(data);
|
||||
if len % 2 == 1 {
|
||||
buf[len] = 0;
|
||||
len + 1
|
||||
} else {
|
||||
len
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn pack_datetime(buf: &mut [u8], date: &[i16; 6]) {
|
||||
assert!(buf.len() >= 6 * 2);
|
||||
let year = date[0] - 1900;
|
||||
pack_int2(buf, year);
|
||||
for ii in 1..6 {
|
||||
pack_int2(&mut buf[(2 * ii)..], date[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FloatTooBigError {
|
||||
float_value: f64,
|
||||
}
|
||||
impl fmt::Display for FloatTooBigError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Float {0} is too large for Real8", self.float_value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from float64 to GDS REAL8 representation.
|
||||
pub fn encode_real8(fnum: f64) -> Result<u64, FloatTooBigError> {
|
||||
// Split the ieee float bitfields
|
||||
let ieee = fnum.to_bits();
|
||||
let sign = ieee & 0x8000_0000_0000_0000;
|
||||
let ieee_exp = (ieee >> 52) as i32 & 0x7ff;
|
||||
let ieee_mant = ieee & 0x000f_ffff_ffff_ffff;
|
||||
|
||||
let subnorm = (ieee_exp == 0) & (ieee_mant != 0);
|
||||
if (ieee_exp == 0) & (ieee_mant == 0) {
|
||||
return Ok(0)
|
||||
}
|
||||
|
||||
// IEEE normal double is (1 + ieee_mant / 2^52) * 2^(ieee_exp - 1023)
|
||||
// IEEE subnormal double is (ieee_mant / 2^52) * 2^(-1022)
|
||||
// GDS real8 is (gds_mant / 2^(7*8)) * 16^(gds_exp - 64)
|
||||
// = (gds_mant / 2^56) * 2^(4 * gds_exp - 256)
|
||||
|
||||
// Convert exponent.
|
||||
let exp2 = if subnorm { -1022 } else {ieee_exp + 1 - 1023}; // +1 is due to mantissa (1.xxxx in IEEE vs 0.xxxxx in GDSII)
|
||||
let mut exp16 = exp2 / 4;
|
||||
let rest = exp2 % 4;
|
||||
|
||||
// Compensate for exponent coarseness
|
||||
let comp = rest != 0;
|
||||
let mut shift;
|
||||
if comp {
|
||||
exp16 += 1;
|
||||
shift = 4 - rest;
|
||||
} else {
|
||||
shift = rest;
|
||||
}
|
||||
shift -= 3; // account for gds bit position
|
||||
|
||||
// add leading one
|
||||
let mut gds_mant_unshifted = ieee_mant;
|
||||
if !subnorm {
|
||||
gds_mant_unshifted += 0x10_0000_0000_0000;
|
||||
}
|
||||
|
||||
let mut gds_mant = if shift > 0 {
|
||||
gds_mant_unshifted >> shift
|
||||
} else {
|
||||
gds_mant_unshifted << -shift
|
||||
};
|
||||
|
||||
// add gds exponent bias
|
||||
let mut gds_exp = exp16 + 64;
|
||||
|
||||
if gds_exp < -14 {
|
||||
// number is too small
|
||||
return Ok(0)
|
||||
}
|
||||
|
||||
let neg_biased = gds_exp < 0;
|
||||
if neg_biased {
|
||||
gds_mant >>= gds_exp * 4;
|
||||
gds_exp = 0;
|
||||
}
|
||||
|
||||
let too_big = (gds_exp > 0x7f) & !subnorm;
|
||||
if too_big {
|
||||
return Err(FloatTooBigError{float_value: fnum});
|
||||
}
|
||||
|
||||
let gds_exp_bits = (gds_exp as u64) << 56;
|
||||
|
||||
let real8 = sign | gds_exp_bits | gds_mant;
|
||||
Ok(real8)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn test_parse_bitarray() {
|
||||
use crate::basic::parse_bitarray;
|
||||
|
||||
//assert!(parse_bitarray(b"59") == 13625);
|
||||
assert_eq!(parse_bitarray(b"\x00\x00").unwrap().1, [false; 16]);
|
||||
assert_eq!(parse_bitarray(b"\xff\xff").unwrap().1, [true; 16]);
|
||||
|
||||
let arr_0001 = parse_bitarray(b"\x00\x01").unwrap().1;
|
||||
for (ii, &vv) in arr_0001.iter().enumerate() {
|
||||
assert_eq!(ii == 15, vv);
|
||||
}
|
||||
|
||||
let arr_8000 = parse_bitarray(b"\x80\x00").unwrap().1;
|
||||
for (ii, &vv) in arr_8000.iter().enumerate() {
|
||||
assert_eq!(ii == 0, vv);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_int2() {
|
||||
use crate::basic::parse_int2;
|
||||
assert_eq!(parse_int2(b"59").unwrap().1, 13625);
|
||||
assert_eq!(parse_int2(b"\0\0").unwrap().1, 0);
|
||||
assert_eq!(parse_int2(b"\xff\xff").unwrap().1, -1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_int4() {
|
||||
use crate::basic::parse_int4;
|
||||
assert_eq!(parse_int4(b"4321").unwrap().1, 875770417);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_real8() {
|
||||
use crate::basic::decode_real8;
|
||||
|
||||
// zeroes
|
||||
assert_eq!(decode_real8(0x0), 0.0);
|
||||
assert_eq!(decode_real8(1<<63), 0.0); // negative
|
||||
assert_eq!(decode_real8(0xff << 56), 0.0); // denormalized
|
||||
|
||||
assert_eq!(decode_real8(0x4110 << 48), 1.0);
|
||||
assert_eq!(decode_real8(0xC120 << 48), -2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_encode_real8_panic() {
|
||||
use crate::basic::encode_real8;
|
||||
encode_real8(1e80).unwrap();
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_real8() {
|
||||
use crate::basic:: parse_real8;
|
||||
|
||||
assert_eq!(0.0, parse_real8(&[0; 8]).unwrap().1);
|
||||
assert_eq!(1.0, parse_real8(&[0x41, 0x10, 0, 0, 0, 0, 0, 0]).unwrap().1);
|
||||
assert_eq!(-2.0, parse_real8(&[0xC1, 0x20, 0, 0, 0, 0, 0, 0]).unwrap().1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ascii() {
|
||||
use crate::basic::parse_ascii;
|
||||
|
||||
assert_eq!(parse_ascii(b"12345", 5).unwrap().1, b"12345");
|
||||
assert_eq!(parse_ascii(b"12345\0", 6).unwrap().1, b"12345"); // strips trailing null byte
|
||||
assert_eq!(parse_ascii(b"123456", 6).unwrap().1, b"123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_bitarray() {
|
||||
use crate::basic::pack_bitarray;
|
||||
let mut buf = [10; 3];
|
||||
let mut bools = [false; 16];
|
||||
bools[1] = true;
|
||||
bools[2] = true;
|
||||
bools[11] = true;
|
||||
|
||||
pack_bitarray(&mut buf, &bools);
|
||||
assert_eq!(buf[0], 0b0110_0000);
|
||||
assert_eq!(buf[1], 0b0001_0000);
|
||||
assert_eq!(buf[2], 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_int2() {
|
||||
use crate::basic::pack_int2;
|
||||
let mut buf = [10; 3 * 2];
|
||||
pack_int2(&mut buf, -3);
|
||||
pack_int2(&mut buf[2..], 2);
|
||||
pack_int2(&mut buf[4..], -1);
|
||||
assert_eq!(buf[0..2], [0xFF, 0xFD]);
|
||||
assert_eq!(buf[2..4], [0x00, 0x02]);
|
||||
assert_eq!(buf[4..6], [0xFF, 0xFF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_int4() {
|
||||
use crate::basic::pack_int4;
|
||||
let mut buf = [10; 3 * 4];
|
||||
pack_int4(&mut buf, -3);
|
||||
pack_int4(&mut buf[4..], 2);
|
||||
pack_int4(&mut buf[8..], -1);
|
||||
assert_eq!(buf[0..4], [0xFF, 0xFF, 0xFF, 0xFD]);
|
||||
assert_eq!(buf[4..8], [0x00, 0x00, 0x00, 0x02]);
|
||||
assert_eq!(buf[8..12], [0xFF, 0xFF, 0xFF, 0xFF]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_real8() {
|
||||
use crate::basic::{encode_real8, decode_real8};
|
||||
const REALS: [f64; 5] = [1.0, -2.0, 1e-9, 1e-3, 1e-12];
|
||||
for vv in REALS {
|
||||
print!("{vv}\n");
|
||||
assert!((decode_real8(encode_real8(vv).unwrap()) - vv).abs() < f64::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_real8() {
|
||||
use crate::basic::{pack_real8, parse_real8};
|
||||
const COUNT: usize = 7;
|
||||
const REALS: [f64; COUNT] = [0.0, 1.0, -1.0, 0.5, 1e-9, 1e-3, 1e-12];
|
||||
let mut buf = [10; 8 * COUNT];
|
||||
for (ii, &vv) in REALS.iter().enumerate() {
|
||||
pack_real8(&mut buf[ii * 8..], vv).unwrap();
|
||||
}
|
||||
for (ii, &vv) in REALS.iter().enumerate() {
|
||||
print!("{vv}\n");
|
||||
let parsed_val = parse_real8(&buf[ii * 8..]).unwrap().1;
|
||||
assert!((parsed_val - vv).abs() < f64::EPSILON);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_ascii() {
|
||||
use crate::basic::pack_ascii;
|
||||
let mut buf = [10; 12];
|
||||
pack_ascii(&mut buf[0..], "4321".as_bytes());
|
||||
pack_ascii(&mut buf[6..], "321".as_bytes());
|
||||
assert_eq!(&buf[0..4], "4321".as_bytes());
|
||||
assert_eq!(&buf[4..6], [10, 10]);
|
||||
assert_eq!(&buf[6..9], "321".as_bytes());
|
||||
assert_eq!(&buf[9..], [0, 10, 10]);
|
||||
}
|
||||
}
|
974
src/elements.rs
Normal file
974
src/elements.rs
Normal file
@ -0,0 +1,974 @@
|
||||
///
|
||||
/// Functionality for reading/writing elements (geometry, text labels,
|
||||
/// structure references) and associated properties.
|
||||
///
|
||||
|
||||
use crate::records::{
|
||||
//BOX, BOUNDARY, NODE, PATH, TEXT, SREF, AREF,
|
||||
DATATYPE, PATHTYPE, BOXTYPE, NODETYPE, TEXTTYPE,
|
||||
LAYER, XY, WIDTH, COLROW, PRESENTATION, STRING,
|
||||
STRANS, MAG, ANGLE, PROPATTR, PROPVALUE,
|
||||
ENDEL, BGNEXTN, ENDEXTN, SNAME,
|
||||
};
|
||||
|
||||
use crate::records;
|
||||
use crate::record::{RecordHeader, Record};
|
||||
use crate::basic::{IResult, fail, take_bytes}; //OResult
|
||||
|
||||
use std::string::String;
|
||||
use std::collections::HashMap;
|
||||
//use std::io::Write;
|
||||
|
||||
use arrow::array::{
|
||||
StructBuilder, FixedSizeListBuilder, ListBuilder, StringBuilder, ArrayBuilder, BooleanBuilder,
|
||||
Int32Builder, Int16Builder, UInt64Builder, UInt32Builder, UInt8Builder, Float64Builder,
|
||||
};
|
||||
|
||||
|
||||
pub type DListBuilder = ListBuilder<Box<dyn ArrayBuilder>>;
|
||||
pub type FListBuilder = FixedSizeListBuilder<Box<dyn ArrayBuilder>>;
|
||||
|
||||
|
||||
fn insert_layer(
|
||||
layer: i16,
|
||||
dtype: i16,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
struct_builder: &mut StructBuilder,
|
||||
field_index: usize,
|
||||
) {
|
||||
let layer32 = ((layer as u16 as u32) << 16) | (dtype as u16 as u32);
|
||||
let next_id = layers.len();
|
||||
let id = layers.entry(layer32).or_insert(next_id.try_into().unwrap());
|
||||
let layer_builder = struct_builder.field_builder::<UInt32Builder>(field_index).unwrap();
|
||||
layer_builder.append_value(*id);
|
||||
}
|
||||
|
||||
|
||||
pub fn read_elements<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
names: &mut HashMap<String, u32>,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let mut input = input;
|
||||
let (_, mut header) = RecordHeader::read(input)?; // don't consume tag
|
||||
while header.tag != records::RTAG_ENDSTR {
|
||||
(input, _) = match header.tag {
|
||||
records::RTAG_SREF => read_ref(input, cell_builder, header.tag, names)?,
|
||||
records::RTAG_AREF => read_ref(input, cell_builder, header.tag, names)?,
|
||||
records::RTAG_BOUNDARY => read_boundary(input, cell_builder, layers)?,
|
||||
records::RTAG_PATH => read_path(input, cell_builder, layers)?,
|
||||
records::RTAG_NODE => read_boxnode(input, cell_builder, header.tag, layers)?,
|
||||
records::RTAG_BOX => read_boxnode(input, cell_builder, header.tag, layers)?,
|
||||
records::RTAG_TEXT => read_text(input, cell_builder, layers)?,
|
||||
_ => {
|
||||
// don't care, skip
|
||||
let result = take_bytes(input, header.data_size + 4)?;
|
||||
(result.0, ())
|
||||
}
|
||||
};
|
||||
(_, header) = RecordHeader::read(input)?; // don't consume tag
|
||||
}
|
||||
(input, _) = take_bytes(input, 4_usize)?; // consume endstr tag
|
||||
|
||||
let refs_builder = cell_builder.field_builder::<DListBuilder>(2).unwrap();
|
||||
refs_builder.append(true);
|
||||
let boundaries_builder = cell_builder.field_builder::<DListBuilder>(3).unwrap();
|
||||
boundaries_builder.append(true);
|
||||
let paths_builder = cell_builder.field_builder::<DListBuilder>(4).unwrap();
|
||||
paths_builder.append(true);
|
||||
let nodes_builder = cell_builder.field_builder::<DListBuilder>(5).unwrap();
|
||||
nodes_builder.append(true);
|
||||
let boxes_builder = cell_builder.field_builder::<DListBuilder>(6).unwrap();
|
||||
boxes_builder.append(true);
|
||||
let texts_builder = cell_builder.field_builder::<DListBuilder>(7).unwrap();
|
||||
texts_builder.append(true);
|
||||
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
|
||||
pub fn read_boundary<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let boundaries_builder = cell_builder.field_builder::<DListBuilder>(3).unwrap();
|
||||
let boundary_builder = boundaries_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
|
||||
let (input, _) = records::BOUNDARY::read(input)?;
|
||||
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = DATATYPE::read(input)?;
|
||||
insert_layer(layer, dtype, layers, boundary_builder, 0);
|
||||
|
||||
let xys_builder = boundary_builder.field_builder::<DListBuilder>(1).unwrap();
|
||||
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
|
||||
let (input, xy_iter) = XY::read(input)?;
|
||||
for xy in xy_iter {
|
||||
xy_builder.append_value(xy);
|
||||
}
|
||||
xys_builder.append(true);
|
||||
|
||||
let props_builder = boundary_builder.field_builder::<DListBuilder>(2).unwrap();
|
||||
let (input, ()) = read_properties(input, props_builder)?;
|
||||
|
||||
boundary_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
|
||||
pub fn read_path<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let paths_builder = cell_builder.field_builder::<DListBuilder>(4).unwrap();
|
||||
let path_builder = paths_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
|
||||
let (input, _) = records::PATH::read(input)?;
|
||||
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = DATATYPE::read(input)?;
|
||||
insert_layer(layer, dtype, layers, path_builder, 0);
|
||||
|
||||
let mut path_type = None;
|
||||
let mut width = None;
|
||||
let mut bgn_ext = None;
|
||||
let mut end_ext = None;
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
records::RTAG_PATHTYPE => {
|
||||
let _path_type;
|
||||
(input, _path_type) = PATHTYPE::read_data(input, header.data_size)?;
|
||||
path_type = Some(_path_type);
|
||||
},
|
||||
records::RTAG_WIDTH => {
|
||||
let _width;
|
||||
(input, _width) = WIDTH::read_data(input, header.data_size)?;
|
||||
width = Some(_width);
|
||||
},
|
||||
records::RTAG_BGNEXTN => {
|
||||
let _bgn_ext;
|
||||
(input, _bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;
|
||||
bgn_ext = Some(_bgn_ext);
|
||||
},
|
||||
records::RTAG_ENDEXTN => {
|
||||
let _end_ext;
|
||||
(input, _end_ext) = ENDEXTN::read_data(input, header.data_size)?;
|
||||
end_ext = Some(_end_ext);
|
||||
},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
};
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let path_type_builder = path_builder.field_builder::<Int16Builder>(1).unwrap();
|
||||
path_type_builder.append_option(path_type);
|
||||
let ext0_builder = path_builder.field_builder::<Int32Builder>(2).unwrap();
|
||||
ext0_builder.append_option(bgn_ext);
|
||||
let ext1_builder = path_builder.field_builder::<Int32Builder>(3).unwrap();
|
||||
ext1_builder.append_option(end_ext);
|
||||
let width_builder = path_builder.field_builder::<Int32Builder>(4).unwrap();
|
||||
width_builder.append_option(width);
|
||||
|
||||
let xys_builder = path_builder.field_builder::<DListBuilder>(5).unwrap();
|
||||
let (input, xy_iter) = XY::read_data(input, header.data_size)?;
|
||||
for xy in xy_iter {
|
||||
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
|
||||
xy_builder.append_value(xy);
|
||||
}
|
||||
xys_builder.append(true);
|
||||
|
||||
let props_builder = path_builder.field_builder::<DListBuilder>(6).unwrap();
|
||||
let (input, ()) = read_properties(input, props_builder)?;
|
||||
|
||||
path_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
pub fn read_boxnode<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
tag: u16,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let field_num = match tag {
|
||||
records::RTAG_NODE => 5,
|
||||
records::RTAG_BOX => 6,
|
||||
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
|
||||
};
|
||||
|
||||
let boxnodes_builder = cell_builder.field_builder::<DListBuilder>(field_num).unwrap();
|
||||
let boxnode_builder = boxnodes_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
|
||||
let (input, _) = match tag {
|
||||
records::RTAG_NODE => records::NODE::read(input)?,
|
||||
records::RTAG_BOX => records::BOX::read(input)?,
|
||||
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
|
||||
};
|
||||
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = match tag {
|
||||
records::RTAG_NODE => NODETYPE::read(input)?,
|
||||
records::RTAG_BOX => BOXTYPE::read(input)?,
|
||||
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
|
||||
};
|
||||
insert_layer(layer, dtype, layers, boxnode_builder, 0);
|
||||
|
||||
let xys_builder = boxnode_builder.field_builder::<DListBuilder>(1).unwrap();
|
||||
let xy_builder = xys_builder.values().as_any_mut().downcast_mut::<Int32Builder>().unwrap();
|
||||
let (input, xy_iter) = XY::read(input)?;
|
||||
for xy in xy_iter {
|
||||
xy_builder.append_value(xy);
|
||||
}
|
||||
xys_builder.append(true);
|
||||
|
||||
let props_builder = boxnode_builder.field_builder::<DListBuilder>(2).unwrap();
|
||||
let (input, ()) = read_properties(input, props_builder)?;
|
||||
|
||||
boxnode_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
pub fn read_text<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
layers: &mut HashMap<u32, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let texts_builder = cell_builder.field_builder::<DListBuilder>(7).unwrap();
|
||||
let text_builder = texts_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
|
||||
let mut path_type = None;
|
||||
let mut pres_hori = None;
|
||||
let mut pres_vert = None;
|
||||
let mut pres_font = None;
|
||||
let mut invert_y = None;
|
||||
let mut width = None;
|
||||
let mut mag = None;
|
||||
let mut angle_deg = None;
|
||||
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = TEXTTYPE::read(input)?;
|
||||
insert_layer(layer, dtype, layers, text_builder, 0);
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
// TODO warn if repeat tags?
|
||||
records::RTAG_PRESENTATION => {
|
||||
let _presentation;
|
||||
(input, _presentation) = PRESENTATION::read_data(input, header.data_size)?;
|
||||
pres_hori = Some(_presentation[14] as u8 * 2 + _presentation[15] as u8);
|
||||
pres_vert = Some(_presentation[12] as u8 * 2 + _presentation[13] as u8);
|
||||
pres_font = Some(_presentation[10] as u8 * 2 + _presentation[11] as u8);
|
||||
},
|
||||
records::RTAG_PATHTYPE => {
|
||||
let _path_type;
|
||||
(input, _path_type) = PATHTYPE::read_data(input, header.data_size)?;
|
||||
path_type = Some(_path_type);
|
||||
},
|
||||
records::RTAG_WIDTH => {
|
||||
let _width;
|
||||
(input, _width) = WIDTH::read_data(input, header.data_size)?;
|
||||
width = Some(_width);
|
||||
},
|
||||
records::RTAG_STRANS => {
|
||||
let strans;
|
||||
(input, strans) = STRANS::read_data(input, header.data_size)?;
|
||||
invert_y = Some(strans[0]);
|
||||
},
|
||||
records::RTAG_MAG => {
|
||||
let _mag;
|
||||
(input, _mag) = MAG::read_data(input, header.data_size)?;
|
||||
mag = Some(_mag);
|
||||
},
|
||||
records::RTAG_ANGLE => {
|
||||
let _angle_deg;
|
||||
(input, _angle_deg) = ANGLE::read_data(input, header.data_size)?;
|
||||
angle_deg = Some(_angle_deg);
|
||||
},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
}
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
|
||||
let pres_hori_builder = text_builder.field_builder::<UInt8Builder>(1).unwrap();
|
||||
pres_hori_builder.append_option(pres_hori);
|
||||
let pres_vert_builder = text_builder.field_builder::<UInt8Builder>(2).unwrap();
|
||||
pres_vert_builder.append_option(pres_vert);
|
||||
let pres_font_builder = text_builder.field_builder::<UInt8Builder>(3).unwrap();
|
||||
pres_font_builder.append_option(pres_font);
|
||||
let path_type_builder = text_builder.field_builder::<Int16Builder>(4).unwrap();
|
||||
path_type_builder.append_option(path_type);
|
||||
let width_builder = text_builder.field_builder::<Int32Builder>(5).unwrap();
|
||||
width_builder.append_option(width);
|
||||
let inv_builder = text_builder.field_builder::<BooleanBuilder>(6).unwrap();
|
||||
inv_builder.append_option(invert_y);
|
||||
let mag_builder = text_builder.field_builder::<Float64Builder>(7).unwrap();
|
||||
mag_builder.append_option(mag);
|
||||
let angle_builder = text_builder.field_builder::<Float64Builder>(8).unwrap();
|
||||
angle_builder.append_option(angle_deg);
|
||||
|
||||
let (input, mut xy_iter) = XY::read_data(input, header.data_size)?;
|
||||
let xx = xy_iter.next().unwrap();
|
||||
let yy = xy_iter.next().unwrap();
|
||||
let xy = ((xx as u32 as u64) << 32) | (yy as u32 as u64);
|
||||
let xy_builder = text_builder.field_builder::<UInt64Builder>(9).unwrap();
|
||||
xy_builder.append_value(xy);
|
||||
|
||||
let (input, string_bytes) = STRING::read(input)?;
|
||||
let string = String::from_utf8(string_bytes).unwrap();
|
||||
let string_builder = text_builder.field_builder::<StringBuilder>(10).unwrap();
|
||||
string_builder.append_value(string);
|
||||
|
||||
let props_builder = text_builder.field_builder::<DListBuilder>(11).unwrap();
|
||||
let (input, ()) = read_properties(input, props_builder)?;
|
||||
|
||||
text_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn read_ref<'a>(
|
||||
input: &'a [u8],
|
||||
cell_builder: &mut StructBuilder,
|
||||
tag: u16,
|
||||
names: &mut HashMap<String, u32>,
|
||||
) -> IResult<'a, ()> {
|
||||
let (input, _) = match tag {
|
||||
records::RTAG_SREF => records::SREF::read(input)?,
|
||||
records::RTAG_AREF => records::AREF::read(input)?,
|
||||
_ => return fail(input, format!("Unexpected tag {:04x}", tag)),
|
||||
};
|
||||
|
||||
let is_aref = tag == records::RTAG_AREF;
|
||||
let refs_builder = cell_builder.field_builder::<DListBuilder>(2).unwrap();
|
||||
let ref_builder = refs_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
|
||||
let mut invert_y = None;
|
||||
let mut mag = None;
|
||||
let mut angle_deg = None;
|
||||
let mut colrow = None;
|
||||
|
||||
let (input, struct_name_bytes) = SNAME::skip_and_read(input)?;
|
||||
let struct_name = String::from_utf8(struct_name_bytes).unwrap();
|
||||
let next_id = names.len();
|
||||
let id = names.entry(struct_name).or_insert(next_id.try_into().unwrap());
|
||||
let target_builder = ref_builder.field_builder::<UInt32Builder>(0).unwrap();
|
||||
target_builder.append_value(*id);
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
records::RTAG_STRANS => {
|
||||
let strans;
|
||||
(input, strans) = STRANS::read_data(input, header.data_size)?;
|
||||
invert_y = Some(strans[0]);
|
||||
},
|
||||
records::RTAG_MAG => {
|
||||
let _mag;
|
||||
(input, _mag) = MAG::read_data(input, header.data_size)?;
|
||||
mag = Some(_mag);
|
||||
},
|
||||
records::RTAG_ANGLE => {
|
||||
let _angle_deg;
|
||||
(input, _angle_deg) = ANGLE::read_data(input, header.data_size)?;
|
||||
angle_deg = Some(_angle_deg);
|
||||
},
|
||||
records::RTAG_COLROW => {
|
||||
let mut _colrow;
|
||||
(input, _colrow) = COLROW::read_data(input, header.data_size)?;
|
||||
colrow = Some((_colrow.next().unwrap(), _colrow.next().unwrap()));
|
||||
if !is_aref {
|
||||
return fail(input, "Got a COLROW record inside an SREF".to_string());
|
||||
}
|
||||
},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
};
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let inv_builder = ref_builder.field_builder::<BooleanBuilder>(1).unwrap();
|
||||
inv_builder.append_option(invert_y);
|
||||
let mag_builder = ref_builder.field_builder::<Float64Builder>(2).unwrap();
|
||||
mag_builder.append_option(mag);
|
||||
let angle_builder = ref_builder.field_builder::<Float64Builder>(3).unwrap();
|
||||
angle_builder.append_option(angle_deg);
|
||||
|
||||
|
||||
let (input, mut xy_iter) = XY::read_data(input, header.data_size)?;
|
||||
let xx = xy_iter.next().unwrap();
|
||||
let yy = xy_iter.next().unwrap();
|
||||
let xy = ((xx as u32 as u64) << 32) | (yy as u32 as u64);
|
||||
let xy_builder = ref_builder.field_builder::<UInt64Builder>(4).unwrap();
|
||||
xy_builder.append_value(xy);
|
||||
|
||||
let rep_builder = ref_builder.field_builder::<StructBuilder>(5).unwrap();
|
||||
// println!("ref, {is_aref:?}");
|
||||
if is_aref {
|
||||
if colrow.is_none() {
|
||||
return fail(input, "AREF without COLROW before XY".to_string())
|
||||
}
|
||||
let (count0, count1) = colrow.unwrap();
|
||||
|
||||
let x0a = (xy_iter.next().unwrap() - xx) / (count0 as i32);
|
||||
let y0a = (xy_iter.next().unwrap() - yy) / (count0 as i32);
|
||||
let x1a = (xy_iter.next().unwrap() - xx) / (count1 as i32);
|
||||
let y1a = (xy_iter.next().unwrap() - yy) / (count1 as i32);
|
||||
|
||||
let xy0a = ((x0a as u32 as u64) << 32) | (y0a as u32 as u64);
|
||||
let xy1a = ((x1a as u32 as u64) << 32) | (y1a as u32 as u64);
|
||||
let xy0_builder = rep_builder.field_builder::<UInt64Builder>(0).unwrap();
|
||||
xy0_builder.append_value(xy0a);
|
||||
let xy1_builder = rep_builder.field_builder::<UInt64Builder>(1).unwrap();
|
||||
xy1_builder.append_value(xy1a);
|
||||
|
||||
let counts = ((count0 as u16 as u32) << 16) | (count1 as u16 as u32);
|
||||
let counts_builder = rep_builder.field_builder::<UInt32Builder>(2).unwrap();
|
||||
counts_builder.append_value(counts);
|
||||
|
||||
} else {
|
||||
let xy0_builder = rep_builder.field_builder::<UInt64Builder>(0).unwrap();
|
||||
xy0_builder.append_null();
|
||||
let xy1_builder = rep_builder.field_builder::<UInt64Builder>(1).unwrap();
|
||||
xy1_builder.append_null();
|
||||
let counts_builder = rep_builder.field_builder::<UInt32Builder>(2).unwrap();
|
||||
counts_builder.append_null();
|
||||
}
|
||||
rep_builder.append(is_aref);
|
||||
|
||||
let props_builder = ref_builder.field_builder::<DListBuilder>(6).unwrap();
|
||||
let (input, ()) = read_properties(input, props_builder)?;
|
||||
|
||||
ref_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Read element properties.
|
||||
///
|
||||
/// Assumes PROPATTR records have unique values.
|
||||
/// Stops reading after consuming ENDEL record.
|
||||
///
|
||||
/// Args:
|
||||
/// stream: Stream to read from.
|
||||
///
|
||||
/// Returns:
|
||||
/// propattr: -> propvalue mapping
|
||||
///
|
||||
pub fn read_properties<'a>(input: &'a [u8], props_builder: &mut DListBuilder) -> IResult<'a, ()> {
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != ENDEL::tag() {
|
||||
if header.tag == PROPATTR::tag() {
|
||||
let key;
|
||||
let value_bytes;
|
||||
(input, key) = PROPATTR::read_data(input, header.data_size)?;
|
||||
(input, value_bytes) = PROPVALUE::read(input)?;
|
||||
|
||||
let value = String::from_utf8(value_bytes).unwrap();
|
||||
//assert!(!properties.contains_key(&key), "Duplicate property key: {}", key);
|
||||
|
||||
let prop_builder = props_builder.values().as_any_mut().downcast_mut::<StructBuilder>().unwrap();
|
||||
let key_builder = prop_builder.field_builder::<Int16Builder>(0).unwrap();
|
||||
key_builder.append_value(key);
|
||||
|
||||
let val_builder = prop_builder.field_builder::<StringBuilder>(1).unwrap();
|
||||
val_builder.append_value(value);
|
||||
|
||||
prop_builder.append(true);
|
||||
}
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
props_builder.append(true);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
///
|
||||
/// Write element properties.
|
||||
///
|
||||
/// This is does _not_ write the ENDEL record.
|
||||
///
|
||||
/// Args:
|
||||
/// stream: Stream to write to.
|
||||
///
|
||||
pub fn write_properties<W: Write>(ww: &mut W, properties: &HashMap::<i16, Vec<u8>>) -> OResult {
|
||||
let mut size = 0;
|
||||
for (key, value) in properties {
|
||||
size += PROPATTR::write(ww, key)?;
|
||||
size += PROPVALUE::write(ww, value)?;
|
||||
}
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
pub trait Element {
|
||||
///
|
||||
/// Read from a stream to construct this object.
|
||||
/// Consumes up to (and including) the ENDEL record.
|
||||
///
|
||||
fn read(input: &[u8]) -> IResult<Self> where Self: Sized;
|
||||
|
||||
///
|
||||
/// Write this element to a stream.
|
||||
/// Finishes with an ENDEL record.
|
||||
///
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult;
|
||||
}
|
||||
|
||||
///
|
||||
/// Datastructure representing
|
||||
/// an instance of a structure (SREF / structure reference) or
|
||||
/// an array of instances (AREF / array reference).
|
||||
/// Type is determined by the presence of the `colrow` tuple.
|
||||
///
|
||||
/// Transforms are applied to each individual instance (_not_
|
||||
/// to the instance's origin location || array vectors).
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Reference {
|
||||
/// Name of the structure being referenced.
|
||||
struct_name: Vec<u8>,
|
||||
/// Whether to mirror the pattern (negate y-values / flip across x-axis). Default false.
|
||||
invert_y: bool,
|
||||
/// Scaling factor (default 1)
|
||||
mag: f64,
|
||||
/// Rotation (degrees counterclockwise)
|
||||
angle_deg: f64,
|
||||
|
||||
/// (For SREF) Location in the parent structure corresponding to the instance's origin (0, 0).
|
||||
/// (For AREF) 3 locations:
|
||||
/// [`offset`,
|
||||
/// `offset + col_basis_vector * colrow[0]`,
|
||||
/// `offset + row_basis_vector * colrow[1]`]
|
||||
/// which define the first instance's offset and the array's basis vectors.
|
||||
/// Note that many GDS implementations only support manhattan basis vectors, and some
|
||||
/// assume a certain axis mapping (e.g. x->columns, y->rows) and "reinterpret" the
|
||||
/// basis vectors to match it.
|
||||
xy: Vec<i32>,
|
||||
|
||||
/// Number of columns and rows (AREF) || None (SREF)
|
||||
colrow: Option<(i16, i16)>,
|
||||
/// Properties associated with this reference.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Element for Reference {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let mut invert_y = false;
|
||||
let mut mag = 1.0;
|
||||
let mut angle_deg = 0.0;
|
||||
let mut colrow = None;
|
||||
let (input, struct_name) = SNAME::skip_and_read(input)?;
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
records::RTAG_STRANS => {
|
||||
let result = STRANS::read_data(input, header.data_size)?;
|
||||
input = result.0;
|
||||
invert_y = result.1[0];
|
||||
},
|
||||
records::RTAG_MAG =>
|
||||
{(input, mag) = MAG::read_data(input, header.data_size)?;},
|
||||
records::RTAG_ANGLE =>
|
||||
{(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;},
|
||||
records::RTAG_COLROW => {
|
||||
let result = COLROW::read_data(input, header.data_size)?;
|
||||
input = result.0;
|
||||
colrow = Some((result.1[0], result.1[1]));
|
||||
},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
};
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, Reference{
|
||||
struct_name: struct_name,
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
colrow: colrow,
|
||||
invert_y: invert_y,
|
||||
mag: mag,
|
||||
angle_deg: angle_deg
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += match self.colrow {
|
||||
None => SREF::write(ww, &())?,
|
||||
Some(_) => AREF::write(ww, &())?,
|
||||
};
|
||||
|
||||
size += SNAME::write(ww, &self.struct_name)?;
|
||||
if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y {
|
||||
let strans = {
|
||||
let mut arr = [false; 16];
|
||||
arr[0] = self.invert_y;
|
||||
arr
|
||||
};
|
||||
size += STRANS::write(ww, &strans)?;
|
||||
if self.mag != 1.0 {
|
||||
size += MAG::write(ww, &self.mag)?;
|
||||
}
|
||||
if self.angle_deg != 0.0 {
|
||||
size += ANGLE::write(ww, &self.angle_deg)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(cr) = self.colrow {
|
||||
size += COLROW::write(ww, &vec!{cr.0, cr.1})?;
|
||||
}
|
||||
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
impl Reference {
|
||||
pub fn check(&self) {
|
||||
if self.colrow.is_some() {
|
||||
assert!(self.xy.len() != 6, "colrow is Some, so expected size-6 xy. Got {:?}", self.xy);
|
||||
} else {
|
||||
assert!(self.xy.len() != 2, "Expected size-2 xy. Got {:?}", self.xy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Datastructure representing a Boundary element.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Boundary {
|
||||
/// (layer, data_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,...
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Element for Boundary {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = DATATYPE::read(input)?;
|
||||
let (input, xy) = XY::read(input)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, Boundary{
|
||||
layer: (layer, dtype),
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += BOUNDARY::write(ww, &())?;
|
||||
size += LAYER::write(ww, &self.layer.0)?;
|
||||
size += DATATYPE::write(ww, &self.layer.1)?;
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Datastructure representing a Path element.
|
||||
///
|
||||
/// If `path_type < 4`, `extension` values are not written.
|
||||
/// During read, `exension` defaults to (0, 0) even if unused.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Path {
|
||||
/// (layer, data_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// End-cap type (0: flush, 1: circle, 2: square, 4: custom)
|
||||
path_type: i16,
|
||||
/// Path width
|
||||
width: i32,
|
||||
/// Extension when using path_type=4. Ignored otherwise.
|
||||
extension: (i32, i32),
|
||||
/// Path centerline coordinates. [x0, y0, x1, y1,...]
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Element for Path {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let mut path_type = 0;
|
||||
let mut width = 0;
|
||||
let mut bgn_ext = 0;
|
||||
let mut end_ext = 0;
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = DATATYPE::read(input)?;
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
records::RTAG_PATHTYPE =>
|
||||
{(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;},
|
||||
records::RTAG_WIDTH =>
|
||||
{(input, width) = WIDTH::read_data(input, header.data_size)?;},
|
||||
records::RTAG_BGNEXTN =>
|
||||
{(input, bgn_ext) = BGNEXTN::read_data(input, header.data_size)?;},
|
||||
records::RTAG_ENDEXTN =>
|
||||
{(input, end_ext) = ENDEXTN::read_data(input, header.data_size)?;},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
};
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, Path{
|
||||
layer: (layer, dtype),
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
extension: (bgn_ext, end_ext),
|
||||
path_type: path_type,
|
||||
width: width,
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += PATH::write(ww, &())?;
|
||||
size += LAYER::write(ww, &self.layer.0)?;
|
||||
size += DATATYPE::write(ww, &self.layer.1)?;
|
||||
if self.path_type != 0 {
|
||||
size += PATHTYPE::write(ww, &self.path_type)?;
|
||||
}
|
||||
if self.width != 0 {
|
||||
size += WIDTH::write(ww, &self.width)?;
|
||||
}
|
||||
|
||||
if self.path_type < 4 {
|
||||
let (bgn_ext, end_ext) = self.extension;
|
||||
if bgn_ext != 0 {
|
||||
size += BGNEXTN::write(ww, &bgn_ext)?;
|
||||
}
|
||||
if end_ext != 0 {
|
||||
size += ENDEXTN::write(ww, &end_ext)?;
|
||||
}
|
||||
}
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Datastructure representing a Box element. Rarely used.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GDSBox {
|
||||
/// (layer, box_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// Box coordinates (5 pairs)
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Element for GDSBox {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = BOXTYPE::read(input)?;
|
||||
let (input, xy) = XY::read(input)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, GDSBox{
|
||||
layer: (layer, dtype),
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += BOX::write(ww, &())?;
|
||||
size += LAYER::write(ww, &self.layer.0)?;
|
||||
size += BOXTYPE::write(ww, &self.layer.1)?;
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// Datastructure representing a Node element. Rarely used.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Node {
|
||||
/// (layer, box_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// 1-50 pairs of coordinates.
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Element for Node {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = NODETYPE::read(input)?;
|
||||
let (input, xy) = XY::read(input)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, Node{
|
||||
layer: (layer, dtype),
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += NODE::write(ww, &())?;
|
||||
size += LAYER::write(ww, &self.layer.0)?;
|
||||
size += NODETYPE::write(ww, &self.layer.1)?;
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Datastructure representing a text label.
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Text {
|
||||
/// (layer, node_type) tuple
|
||||
layer: (i16, i16),
|
||||
|
||||
/// Bit array. Default all zeros.
|
||||
/// bits 0-1: 00 left/01 center/10 right
|
||||
/// bits 2-3: 00 top/01 middle/10 bottom
|
||||
/// bits 4-5: font number
|
||||
presentation: [bool; 16],
|
||||
|
||||
/// Default 0
|
||||
path_type: i16,
|
||||
/// Default 0
|
||||
width: i32,
|
||||
/// Vertical inversion. Default false.
|
||||
invert_y: bool,
|
||||
/// Scaling factor. Default 1.
|
||||
mag: f64,
|
||||
/// Rotation (ccw). Default 0.
|
||||
angle_deg: f64,
|
||||
/// Position (1 pair only)
|
||||
xy: Vec<i32>,
|
||||
/// Text content
|
||||
string: Vec<u8>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>
|
||||
}
|
||||
|
||||
impl Element for Text {
|
||||
fn read(input: &[u8]) -> IResult<Self> {
|
||||
let mut path_type = 0;
|
||||
let mut presentation = [false; 16];
|
||||
let mut invert_y = false;
|
||||
let mut width = 0;
|
||||
let mut mag = 1.0;
|
||||
let mut angle_deg = 0.0;
|
||||
let (input, layer) = LAYER::skip_and_read(input)?;
|
||||
let (input, dtype) = TEXTTYPE::read(input)?;
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_XY {
|
||||
match header.tag {
|
||||
records::RTAG_PRESENTATION =>
|
||||
{(input, presentation) = PRESENTATION::read_data(input, header.data_size)?;},
|
||||
records::RTAG_PATHTYPE =>
|
||||
{(input, path_type) = PATHTYPE::read_data(input, header.data_size)?;},
|
||||
records::RTAG_WIDTH =>
|
||||
{(input, width) = WIDTH::read_data(input, header.data_size)?;},
|
||||
records::RTAG_STRANS => {
|
||||
let result = STRANS::read_data(input, header.data_size)?;
|
||||
input = result.0;
|
||||
invert_y = result.1[0];
|
||||
},
|
||||
records::RTAG_MAG =>
|
||||
{(input, mag) = MAG::read_data(input, header.data_size)?;},
|
||||
records::RTAG_ANGLE =>
|
||||
{(input, angle_deg) = ANGLE::read_data(input, header.data_size)?;},
|
||||
_ =>
|
||||
return fail(input, format!("Unexpected tag {:04x}", header.tag)),
|
||||
}
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let (input, xy) = XY::read_data(input, header.data_size)?;
|
||||
|
||||
let (input, string) = STRING::read(input)?;
|
||||
let (input, properties) = read_properties(input)?;
|
||||
Ok((input, Text{
|
||||
layer: (layer, dtype),
|
||||
xy: xy,
|
||||
properties: properties,
|
||||
string: string,
|
||||
presentation: presentation,
|
||||
path_type: path_type,
|
||||
width: width,
|
||||
invert_y: invert_y,
|
||||
mag: mag,
|
||||
angle_deg: angle_deg,
|
||||
}))
|
||||
}
|
||||
|
||||
fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let mut size = 0;
|
||||
size += TEXT::write(ww, &())?;
|
||||
size += LAYER::write(ww, &self.layer.0)?;
|
||||
size += TEXTTYPE::write(ww, &self.layer.1)?;
|
||||
if self.presentation.iter().any(|&x| x) {
|
||||
size += PRESENTATION::write(ww, &self.presentation)?;
|
||||
}
|
||||
if self.path_type != 0 {
|
||||
size += PATHTYPE::write(ww, &self.path_type)?;
|
||||
}
|
||||
if self.width != 0 {
|
||||
size += WIDTH::write(ww, &self.width)?
|
||||
}
|
||||
if self.angle_deg != 0.0 || self.mag != 1.0 || self.invert_y {
|
||||
let strans = {
|
||||
let mut arr = [false; 16];
|
||||
arr[0] = self.invert_y;
|
||||
arr
|
||||
};
|
||||
size += STRANS::write(ww, &strans)?;
|
||||
if self.mag != 1.0 {
|
||||
size += MAG::write(ww, &self.mag)?;
|
||||
}
|
||||
if self.angle_deg != 0.0 {
|
||||
size += ANGLE::write(ww, &self.angle_deg)?;
|
||||
}
|
||||
}
|
||||
size += XY::write(ww, &self.xy)?;
|
||||
size += STRING::write(ww, &self.string)?;
|
||||
size += write_properties(ww, &self.properties)?;
|
||||
size += ENDEL::write(ww, &())?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
*/
|
425
src/iface.rs
Normal file
425
src/iface.rs
Normal file
@ -0,0 +1,425 @@
|
||||
/*
|
||||
* Shapes:
|
||||
* layer, dtype (2x i16)
|
||||
* bounds (4x i32)
|
||||
* offset/ptr (usize)
|
||||
* cell_id (usize)
|
||||
*
|
||||
* Cell:
|
||||
* name (??)
|
||||
* offset (usize)
|
||||
* len (usize)
|
||||
*
|
||||
* Refs:
|
||||
* name_ind (usize)
|
||||
* invert (bool)
|
||||
* mag (f64)
|
||||
* angle (f64)
|
||||
* offset/ptr (usize)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
use arrow;
|
||||
use arrow::array::{LargeStringArray, DictionaryArray, UInt64Array, Int32Array, UInt16Array}
|
||||
use arrow::builder::{LargeStringDictionaryBuilder, UInt64Builder, Int32Builder, Int64Builder, BooleanBuilder};
|
||||
use library::FileHeader;
|
||||
|
||||
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::{Element};
|
||||
pub use crate::basic::{IResult, OResult, take_bytes, fail};
|
||||
|
||||
|
||||
|
||||
const DEFAULT_DATE: [i16; 6] = [1900, 0, 0, 0, 0, 0];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LibraryBuilder {
|
||||
cell_name: LargeStringDictionaryBuilder,
|
||||
cell_offset: UInt64Builder,
|
||||
|
||||
ref_parent: UInt64Builder,
|
||||
ref_name: LargeStringDictionaryBuilder,
|
||||
ref_mirror: BooleanBuilder,
|
||||
ref_mag: Float64Builder,
|
||||
ref_angle: Float64Builder,
|
||||
ref_offset: UInt64Builder,
|
||||
|
||||
shape_parent: UInt64Builder,
|
||||
shape_offset: UInt64Builder,
|
||||
shape_type: UInt8Builder,
|
||||
shape_layer: UInt16Builder,
|
||||
shape_dtype: UInt16Builder,
|
||||
shape_xmin: Int32Builder,
|
||||
shape_ymin: Int32Builder,
|
||||
shape_xmax: Int32Builder,
|
||||
shape_ymax: Int32Builder,
|
||||
shape_data: LargeListArrayBuilder<Int32Builder>,
|
||||
|
||||
text_parent: UInt64Builder,
|
||||
text_string: LargeStringDictionaryBuilder,
|
||||
text_x: Int32Builder,
|
||||
text_y: Int32Builder,
|
||||
|
||||
|
||||
|
||||
boundaries: Vec<elements::Boundary>,
|
||||
paths: Vec<elements::Path>,
|
||||
nodes: Vec<elements::Node>,
|
||||
boxes: Vec<elements::GDSBox>,
|
||||
texts: Vec<elements::Text>,
|
||||
refs: Vec<elements::Reference>,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
/// 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
|
||||
///
|
||||
pub fn write<W: 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<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)?;
|
||||
}
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn make_table(input: &[u8]) -> IResult<HashMap::<Vec<u8>, usize>> {
|
||||
let input_size = input.len();
|
||||
let mut names = LargeStringDictionaryBuilder::new();
|
||||
let mut positions = UInt64Builder::new();
|
||||
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != records::RTAG_ENDLIB {
|
||||
(input, let record_bytes) = take_bytes(input, header.data_size)?;
|
||||
if header.tag == records::RTAG_BGNSTR {
|
||||
let position = input_size - input.len();
|
||||
|
||||
let name;
|
||||
(input, name) = records::STRNAME::read(record_bytes)?;
|
||||
|
||||
names.append(name);
|
||||
positions.append(position);
|
||||
|
||||
|
||||
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, ()))
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
Ok((input, positions))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub struct References {
|
||||
invert_y: Vec<bool>,
|
||||
mag_and_angle_deg: Vec<f64>,
|
||||
xy: Vec<i32>,
|
||||
|
||||
names: Vec<u8>,
|
||||
name_inds: Vec<usize>,
|
||||
|
||||
has_prop: Vec<bool>,
|
||||
has_rep: Vec<bool>,
|
||||
}
|
||||
|
||||
pub struct Repetitions {
|
||||
colrow_vecs: Vec<i32>, // 4 per
|
||||
colrow_counts: Vec<i16>, // 2 per
|
||||
}
|
||||
|
||||
pub struct Shapes {
|
||||
layers_and_dtypes: Vec<i16>, // 2 per
|
||||
xy: Vec<i32>, // variable per
|
||||
has_prop: Vec<bool>,
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub struct References {
|
||||
invert_y: bool,
|
||||
mag: f64,
|
||||
angle_deg: f64,
|
||||
|
||||
xy: (i32, i32),
|
||||
|
||||
// Use id to look up these... maybe include has_props and has_rep?
|
||||
struct_name: Vec<u8>,
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
|
||||
col_vec: (i32 i32),
|
||||
row_vec: (i32, i32),
|
||||
colrow: (i16, i16),
|
||||
}
|
||||
|
||||
pub struct Boundary {
|
||||
/// (layer, data_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// Ordered vertices of the shape. First and last points should be identical. Order x0, y0, x1,...
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
pub struct Path {
|
||||
/// (layer, data_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// End-cap type (0: flush, 1: circle, 2: square, 4: custom)
|
||||
path_type: i16,
|
||||
/// Path width
|
||||
width: i32,
|
||||
/// Extension when using path_type=4. Ignored otherwise.
|
||||
extension: (i32, i32),
|
||||
/// Path centerline coordinates. [x0, y0, x1, y1,...]
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
|
||||
pub struct GDSBox {
|
||||
/// (layer, box_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// Box coordinates (5 pairs)
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
pub struct Node {
|
||||
/// (layer, box_type) tuple
|
||||
layer: (i16, i16),
|
||||
/// 1-50 pairs of coordinates.
|
||||
xy: Vec<i32>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>,
|
||||
}
|
||||
pub struct Text {
|
||||
/// (layer, node_type) tuple
|
||||
layer: (i16, i16),
|
||||
|
||||
/// Bit array. Default all zeros.
|
||||
/// bits 0-1: 00 left/01 center/10 right
|
||||
/// bits 2-3: 00 top/01 middle/10 bottom
|
||||
/// bits 4-5: font number
|
||||
presentation: [bool; 16],
|
||||
|
||||
/// Default 0
|
||||
path_type: i16,
|
||||
/// Default 0
|
||||
width: i32,
|
||||
/// Vertical inversion. Default false.
|
||||
invert_y: bool,
|
||||
/// Scaling factor. Default 1.
|
||||
mag: f64,
|
||||
/// Rotation (ccw). Default 0.
|
||||
angle_deg: f64,
|
||||
/// Position (1 pair only)
|
||||
xy: Vec<i32>,
|
||||
/// Text content
|
||||
string: Vec<u8>,
|
||||
/// Properties for the element.
|
||||
properties: HashMap::<i16, Vec<u8>>
|
||||
}
|
119
src/lib.rs
Normal file
119
src/lib.rs
Normal file
@ -0,0 +1,119 @@
|
||||
extern crate byteorder;
|
||||
|
||||
pub mod basic;
|
||||
pub mod record;
|
||||
pub mod records;
|
||||
pub mod elements;
|
||||
pub mod library;
|
||||
|
||||
use crate::library::read_library;
|
||||
|
||||
use byteorder::{ByteOrder, BigEndian};
|
||||
use std::mem::size_of;
|
||||
use std::ffi::{CStr, OsStr, c_char};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::str;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use arrow::ffi::{to_ffi, FFI_ArrowArray, FFI_ArrowSchema};
|
||||
use arrow::array::Array;
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn read_path(
|
||||
cpath: *const c_char,
|
||||
arr: *mut FFI_ArrowArray,
|
||||
schema: *mut FFI_ArrowSchema,
|
||||
) {
|
||||
let cstr = unsafe { CStr::from_ptr(cpath) };
|
||||
let path: &Path;
|
||||
if cfg!(unix) {
|
||||
let osstr = OsStr::from_bytes(cstr.to_bytes());
|
||||
path = osstr.as_ref();
|
||||
} else if cfg!(windows) {
|
||||
let ustr = str::from_utf8(cstr.to_bytes()).expect("Non-UTF8 paths are not supported");
|
||||
path = ustr.as_ref();
|
||||
} else {
|
||||
panic!("Unsupported OS");
|
||||
}
|
||||
|
||||
let input = fs::read(path).expect("File read failed");
|
||||
let (_input, struct_arr) = read_library(&input).expect("Read failed");
|
||||
(*arr, *schema) = to_ffi(&struct_arr.to_data()).unwrap();
|
||||
}
|
||||
|
||||
|
||||
macro_rules! impl_i16be {
|
||||
( $tt:ty, $arr:ident, $size:ident ) => {
|
||||
{
|
||||
let sl = unsafe { std::slice::from_raw_parts_mut($arr, $size) };
|
||||
for xx in sl.iter_mut() {
|
||||
if *xx < i16::MIN as $tt { return *xx }
|
||||
if *xx > i16::MAX as $tt { return *xx }
|
||||
|
||||
let mut buf = [0; size_of::<$tt>()];
|
||||
BigEndian::write_i16(&mut buf, *xx as i16);
|
||||
*xx = <$tt>::from_le_bytes(buf);
|
||||
}
|
||||
0 as $tt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
macro_rules! impl_i32be {
|
||||
( $tt:ty, $arr:ident, $size:ident ) => {
|
||||
{
|
||||
let sl = unsafe { std::slice::from_raw_parts_mut($arr, $size) };
|
||||
for xx in sl.iter_mut() {
|
||||
if *xx < i32::MIN as $tt { return *xx }
|
||||
if *xx > i32::MAX as $tt { return *xx }
|
||||
|
||||
let mut buf = [0; size_of::<$tt>()];
|
||||
BigEndian::write_i32(&mut buf, *xx as i32);
|
||||
*xx = <$tt>::from_le_bytes(buf);
|
||||
}
|
||||
0 as $tt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn f64_to_i16(arr: *mut f64, size: usize) -> f64 { impl_i16be!(f64, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn f64_to_i32(arr: *mut f64, size: usize) -> f64 { impl_i32be!(f64, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn f32_to_i16(arr: *mut f32, size: usize) -> f32 { impl_i16be!(f32, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn f32_to_i32(arr: *mut f32, size: usize) -> f32 { impl_i32be!(f32, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn u64_to_i16(arr: *mut u64, size: usize) -> u64 { impl_i16be!(u64, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn u64_to_i32(arr: *mut u64, size: usize) -> u64 { impl_i32be!(u64, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn i64_to_i16(arr: *mut i64, size: usize) -> i64 { impl_i16be!(i64, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn i64_to_i32(arr: *mut i64, size: usize) -> i64 { impl_i32be!(i64, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn u32_to_i16(arr: *mut u32, size: usize) -> u32 { impl_i16be!(u32, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn u32_to_i32(arr: *mut u32, size: usize) -> u32 { impl_i32be!(u32, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn i32_to_i16(arr: *mut i32, size: usize) -> i32 { impl_i16be!(i32, arr, size) }
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn i32_to_i32(arr: *mut i32, size: usize) -> i32 { impl_i32be!(i32, arr, size) }
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn u16_to_i16(arr: *mut u16, size: usize) -> u16 { impl_i16be!(u16, arr, size) }
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn i16_to_i16(arr: *mut i16, size: usize) -> i16 { impl_i16be!(i16, arr, size) }
|
||||
|
605
src/library.rs
Normal file
605
src/library.rs
Normal file
@ -0,0 +1,605 @@
|
||||
///
|
||||
/// 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<u8>,
|
||||
}
|
||||
|
||||
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<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)?;
|
||||
|
||||
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<W: 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<StructArray> {
|
||||
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("xy0", DataType::UInt64, false),
|
||||
Field::new("xy1", DataType::UInt64, false),
|
||||
Field::new("counts", DataType::UInt32, 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("xy", DataType::UInt64, 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("xy", DataType::UInt64, 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::<Float64Builder>(0).unwrap();
|
||||
dbu_builder.append_value(header.meters_per_db_unit);
|
||||
let uu_builder = lib_builder.field_builder::<Float64Builder>(1).unwrap();
|
||||
uu_builder.append_value(header.user_units_per_db_unit);
|
||||
let libname_builder = lib_builder.field_builder::<StringBuilder>(2).unwrap();
|
||||
libname_builder.append_value(String::from_utf8(header.name).unwrap());
|
||||
let mtl_builder = lib_builder.field_builder::<FListBuilder>(3).unwrap();
|
||||
let mt_builder = mtl_builder.values().as_any_mut().downcast_mut::<Int16Builder>().unwrap();
|
||||
mt_builder.append_values(&header.mod_time, &[true; 6]);
|
||||
mtl_builder.append(true);
|
||||
let atl_builder = lib_builder.field_builder::<FListBuilder>(4).unwrap();
|
||||
let at_builder = atl_builder.values().as_any_mut().downcast_mut::<Int16Builder>().unwrap();
|
||||
at_builder.append_values(&header.acc_time, &[true; 6]);
|
||||
atl_builder.append(true);
|
||||
|
||||
let mut names = HashMap::<String, u32>::new();
|
||||
let mut layers = HashMap::<u32, u32>::new();
|
||||
let cells_builder = lib_builder.field_builder::<DListBuilder>(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::<StructBuilder>().unwrap();
|
||||
let id_builder = cell_builder.field_builder::<UInt32Builder>(0).unwrap();
|
||||
id_builder.append_value(*id);
|
||||
|
||||
let offset_builder = cell_builder.field_builder::<UInt64Builder>(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<u32, String> = names.into_iter().map(|(kk, vv)| (vv, kk)).collect();
|
||||
let names_builder = lib_builder.field_builder::<DListBuilder>(6).unwrap();
|
||||
let name_builder = names_builder.values().as_any_mut().downcast_mut::<StringBuilder>().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<u32, u32> = layers.into_iter().map(|(kk, vv)| (vv, kk)).collect();
|
||||
let layers_builder = lib_builder.field_builder::<DListBuilder>(7).unwrap();
|
||||
let layer_builder = layers_builder.values().as_any_mut().downcast_mut::<UInt32Builder>().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<HashMap::<Vec<u8>, 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<u8>,
|
||||
|
||||
boundaries: Vec<elements::Boundary>,
|
||||
paths: Vec<elements::Path>,
|
||||
nodes: Vec<elements::Node>,
|
||||
boxes: Vec<elements::GDSBox>,
|
||||
texts: Vec<elements::Text>,
|
||||
refs: Vec<elements::Reference>,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
/// 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
|
||||
///
|
||||
pub fn write<W: 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<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)?;
|
||||
}
|
||||
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::<Vec<u8>, HashMap::<Vec<u8>, 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<Option((Vec<u8>, 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)?;
|
||||
}
|
||||
*/
|
39
src/misc.py
Normal file
39
src/misc.py
Normal file
@ -0,0 +1,39 @@
|
||||
'''
|
||||
https://github.com/apache/arrow/blob/main/python/pyarrow/tests/test_cffi.py
|
||||
'''
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ctypes
|
||||
import pyarrow as pa
|
||||
from pyarrow.cffi import ffi
|
||||
|
||||
c_schema = ffi.new('struct ArrowSchema*')
|
||||
ptr_schema = int(ffi.cast('uintptr_t', c_schema))
|
||||
c_array = ffi.new('struct ArrowArray*')
|
||||
ptr_array = int(ffi.cast('uintptr_t', c_array))
|
||||
|
||||
# pyarrow.Array._import_from_c(ptr_array, pa.list_(pa.int32()))
|
||||
|
||||
# import gc
|
||||
# gc.collect() # Make sure no Arrow data dangles in a ref cycle
|
||||
# pyarrow.Array._export_from_c(arr, ptr_array, ptr_schema)
|
||||
|
||||
arr_new = pyarrow.Array._import_from_c(ptr_array, ptr_schema)
|
||||
|
||||
|
422
src/record.rs
Normal file
422
src/record.rs
Normal file
@ -0,0 +1,422 @@
|
||||
///
|
||||
/// Generic record-level read/write functionality.
|
||||
///
|
||||
use std::io::Write;
|
||||
use std::convert::TryInto;
|
||||
use byteorder::{ByteOrder, BigEndian};
|
||||
|
||||
|
||||
use crate::basic::{pack_datetime, pack_bitarray, pack_ascii, pack_int2, pack_int4, pack_real8}; #[warn(unused_imports)]
|
||||
use crate::basic::{parse_datetime, parse_bitarray, parse_ascii, parse_int2, parse_int4, parse_real8}; #[warn(unused_imports)]
|
||||
use crate::basic::{OResult, IResult, fail, parse_u16, take_bytes}; //ErrType,
|
||||
use crate::records;
|
||||
|
||||
|
||||
//#[no_mangle]
|
||||
//pub extern "C" fn write_record_header(
|
||||
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RecordHeader {
|
||||
pub tag: u16,
|
||||
pub data_size: u16,
|
||||
}
|
||||
|
||||
impl RecordHeader {
|
||||
pub fn read(input: &[u8]) -> IResult<RecordHeader> {
|
||||
let (input, size) = parse_u16(input)?;
|
||||
let (input, tag) = parse_u16(input)?;
|
||||
Ok((input, RecordHeader{tag, data_size:size - 4}))
|
||||
}
|
||||
|
||||
pub fn pack_into(&self) -> [u8; 4] {
|
||||
assert!(self.data_size < 0xffff - 4, "Record too big!");
|
||||
let vals = [self.data_size, self.tag];
|
||||
let mut buf = [0x77; 4];
|
||||
BigEndian::write_u16_into(&vals, &mut buf);
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn write<W: Write>(&self, ww: &mut W) -> OResult {
|
||||
let bytes = self.pack_into();
|
||||
ww.write(&bytes)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub trait RecordData {
|
||||
type BareData<'a>;
|
||||
type InData : ?Sized;
|
||||
type ByteData : AsRef<[u8]>;
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>>;
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData);
|
||||
//fn size(data: &Self::BareData<'_>) -> u16;
|
||||
fn pack(data: &Self::InData) -> Self::ByteData;
|
||||
}
|
||||
|
||||
|
||||
pub trait Record<RData: RecordData> {
|
||||
fn tag() -> u16;
|
||||
fn expected_size() -> Option<u16>;
|
||||
|
||||
fn check_size(actual_size: u16) -> Result<(), String> {
|
||||
match Self::expected_size() {
|
||||
Some(size) => if size == actual_size {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected record size {}, got {}", size, actual_size))
|
||||
},
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_header(input: &[u8]) -> IResult<RecordHeader> {
|
||||
RecordHeader::read(input)
|
||||
}
|
||||
|
||||
fn write_header<W: Write>(ww: &mut W, data_size: u16) -> OResult {
|
||||
RecordHeader{tag: Self::tag(), data_size}.write(ww)
|
||||
}
|
||||
|
||||
fn read_data(input: &[u8], size: u16) -> IResult<RData::BareData<'_>> {
|
||||
RData::read(input, size)
|
||||
}
|
||||
|
||||
fn pack_data(buf: &mut [u8], data: &RData::InData) {
|
||||
RData::pack_into(buf, data)
|
||||
}
|
||||
|
||||
///
|
||||
/// Skip to the end of the next occurence of this record.
|
||||
///
|
||||
/// Return:
|
||||
/// True if the record was encountered and skipped.
|
||||
/// False if the end of the library was reached.
|
||||
///
|
||||
fn skip_past(input: &[u8]) -> IResult<bool> {
|
||||
let original_input = input;
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != Self::tag() {
|
||||
(input, _) = take_bytes(input, header.data_size)?;
|
||||
if header.tag == records::RTAG_ENDLIB {
|
||||
return Ok((original_input, false))
|
||||
}
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
(input, _) = take_bytes(input, header.data_size)?;
|
||||
Ok((input, true))
|
||||
}
|
||||
|
||||
fn skip_and_read(input: &[u8]) -> IResult<RData::BareData<'_>> {
|
||||
let (mut input, mut header) = RecordHeader::read(input)?;
|
||||
while header.tag != Self::tag() {
|
||||
(input, _) = take_bytes(input, header.data_size)?;
|
||||
(input, header) = RecordHeader::read(input)?;
|
||||
}
|
||||
let (input, data) = Self::read_data(input, header.data_size)?;
|
||||
Ok((input, data))
|
||||
}
|
||||
|
||||
fn expect_header(input: &[u8]) -> IResult<u16> {
|
||||
let (input, header) = RecordHeader::read(input)?;
|
||||
if header.tag != Self::tag() {
|
||||
fail(input, format!("Unexpected record! Got tag 0x{:04x}, expected 0x{:04x}", header.tag, Self::tag()))
|
||||
} else {
|
||||
Ok((input, header.data_size))
|
||||
}
|
||||
}
|
||||
|
||||
fn read(input: &[u8]) -> IResult<RData::BareData<'_>> {
|
||||
let (input, size) = Self::expect_header(input)?;
|
||||
Self::check_size(size).unwrap();
|
||||
let (input, data) = Self::read_data(input, size)?;
|
||||
Ok((input, data))
|
||||
}
|
||||
|
||||
fn write<W: Write>(ww: &mut W, data: &RData::InData) -> OResult {
|
||||
let packed_data = RData::pack(data);
|
||||
let data_bytes = packed_data.as_ref();
|
||||
let len: u16 = data_bytes.len().try_into().expect("Record longer than max size (u16)!");
|
||||
let mut size = 0;
|
||||
size += Self::write_header(ww, len)?;
|
||||
size += ww.write(data_bytes)?;
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BitArray;
|
||||
impl RecordData for BitArray {
|
||||
type BareData<'a> = [bool; 16];
|
||||
type InData = [bool; 16];
|
||||
type ByteData = [u8; 2];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 2);
|
||||
parse_bitarray(input)
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_bitarray(buf, data);
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 2];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Int2;
|
||||
impl RecordData for Int2 {
|
||||
type BareData<'a> = i16;
|
||||
type InData = i16;
|
||||
type ByteData = [u8; 2];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 2);
|
||||
parse_int2(input)
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_int2(buf, *data)
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 2];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Int4;
|
||||
impl RecordData for Int4 {
|
||||
type BareData<'a> = i32;
|
||||
type InData = i32;
|
||||
type ByteData = [u8; 4];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 4);
|
||||
parse_int4(input)
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_int4(buf, *data)
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 4];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct Int2Array;
|
||||
impl RecordData for Int2Array {
|
||||
type BareData<'a> = Int2ArrayReader<'a>;
|
||||
type InData = [i16];
|
||||
type ByteData = Vec<u8>;
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size % 2 == 0, "Record must contain an integer quantity of integers");
|
||||
//let mut input = input;
|
||||
let (input, bytes) = take_bytes(input, size)?;
|
||||
Ok((input, Int2ArrayReader{bytes}))
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
BigEndian::write_i16_into(data, buf)
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = Vec::with_capacity(data.len() * 2);
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Int2ArrayReader<'a> {
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
impl Iterator for Int2ArrayReader<'_> {
|
||||
type Item = i16;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.bytes.len() < 2 {
|
||||
None
|
||||
} else {
|
||||
let (remaining, val) = parse_int2(self.bytes).unwrap();
|
||||
self.bytes = remaining;
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Int4Array;
|
||||
impl RecordData for Int4Array {
|
||||
type BareData<'a> = Int4ArrayReader<'a>;
|
||||
type InData = [i32];
|
||||
type ByteData = Vec<u8>;
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size % 4 == 0, "Record must contain an integer quantity of integers");
|
||||
//let mut input = input;
|
||||
let (input, bytes) = take_bytes(input, size)?;
|
||||
Ok((input, Int4ArrayReader{bytes}))
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
BigEndian::write_i32_into(data, buf)
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = Vec::with_capacity(data.len() * 4);
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Int4ArrayReader<'a> {
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
impl Iterator for Int4ArrayReader<'_> {
|
||||
type Item = i32;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.bytes.len() < 4 {
|
||||
None
|
||||
} else {
|
||||
let (remaining, val) = parse_int4(self.bytes).unwrap();
|
||||
self.bytes = remaining;
|
||||
Some(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Real8;
|
||||
impl RecordData for Real8 {
|
||||
type BareData<'a> = f64;
|
||||
type InData = f64;
|
||||
type ByteData = [u8; 8];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 8);
|
||||
parse_real8(input)
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_real8(buf, *data).unwrap_or_else(|_| panic!("Float {0} too big for Real8", data))
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 8];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Real8Pair;
|
||||
impl RecordData for Real8Pair {
|
||||
type BareData<'a> = (f64, f64);
|
||||
type InData = (f64, f64);
|
||||
type ByteData = [u8; 2 * 8];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 2 * 8);
|
||||
let (input, data0) = parse_real8(input)?;
|
||||
let (input, data1) = parse_real8(input)?;
|
||||
Ok((input, (data0, data1)))
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_real8(&mut buf[8 * 0..], data.0).unwrap_or_else(|_| panic!("Float.0 {0} too big for Real8", data.0));
|
||||
pack_real8(&mut buf[8 * 1..], data.1).unwrap_or_else(|_| panic!("Float.1 {0} too big for Real8", data.1));
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 2 * 8];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
|
||||
//fn write<W: Write>(ww: &mut W, data: &Self::BareData) -> OResult {
|
||||
// let mut buf = [u8; 2 * 6 * 2];
|
||||
// Self::pack_into(&mut buf, data)
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct ASCII;
|
||||
impl RecordData for ASCII {
|
||||
type BareData<'a> = Vec<u8>;
|
||||
type InData = [u8];
|
||||
type ByteData = Vec<u8>;
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
parse_ascii(input, size)
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_ascii(buf, data);
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = Vec::with_capacity(data.len() * 4);
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub struct DateTimePair;
|
||||
impl RecordData for DateTimePair {
|
||||
type BareData<'a> = [[i16; 6]; 2];
|
||||
type InData = [[i16; 6]; 2];
|
||||
type ByteData = [u8; 2 * 6 * 2];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 2 * 6 * 2);
|
||||
let (input, data0) = parse_datetime(input)?;
|
||||
let (input, data1) = parse_datetime(input)?;
|
||||
Ok((input, [data0, data1]))
|
||||
}
|
||||
|
||||
fn pack_into(buf: &mut [u8], data: &Self::InData) {
|
||||
pack_datetime(&mut buf[6 * 2 * 0..], &data[0]);
|
||||
pack_datetime(&mut buf[6 * 2 * 1..], &data[1]);
|
||||
}
|
||||
|
||||
fn pack(data: &Self::InData) -> Self::ByteData {
|
||||
let mut buf = [0; 2 * 6 * 2];
|
||||
Self::pack_into(&mut buf, data);
|
||||
buf
|
||||
}
|
||||
|
||||
//fn write<W: Write>(ww: &mut W, data: &Self::BareData) -> OResult {
|
||||
// let mut buf = [u8; 2 * 6 * 2];
|
||||
// Self::pack_into(&mut buf, data)
|
||||
//}
|
||||
}
|
||||
|
||||
pub struct Empty;
|
||||
impl RecordData for Empty {
|
||||
type BareData<'a> = ();
|
||||
type InData = ();
|
||||
type ByteData = [u8; 0];
|
||||
|
||||
fn read(input: &[u8], size: u16) -> IResult<Self::BareData<'_>> {
|
||||
assert!(size == 0);
|
||||
Ok((input, ()))
|
||||
}
|
||||
|
||||
fn pack_into(_buf: &mut [u8], _data: &Self::InData) {
|
||||
}
|
||||
|
||||
fn pack(_data: &Self::InData) -> Self::ByteData {
|
||||
[]
|
||||
}
|
||||
//fn write<W: Write>(ww: &mut W, data: &Self::BareData) {
|
||||
//}
|
||||
}
|
547
src/records.rs
Normal file
547
src/records.rs
Normal file
@ -0,0 +1,547 @@
|
||||
///
|
||||
/// Record type and tag definitions
|
||||
///
|
||||
|
||||
use crate::record::{Record, Int2, Int4, Int2Array, Int4Array, Real8, Real8Pair, DateTimePair, BitArray, ASCII, Empty};
|
||||
|
||||
//use std::io::Write;
|
||||
|
||||
// record tags
|
||||
pub const RTAG_HEADER: u16 = 0x0002;
|
||||
pub const RTAG_BGNLIB: u16 = 0x0102;
|
||||
pub const RTAG_LIBNAME: u16 = 0x0206;
|
||||
pub const RTAG_UNITS: u16 = 0x0305; // (user_units_per_db_unit, db_units_per_meter)
|
||||
pub const RTAG_ENDLIB: u16 = 0x0400;
|
||||
pub const RTAG_BGNSTR: u16 = 0x0502;
|
||||
pub const RTAG_STRNAME: u16 = 0x0606;
|
||||
pub const RTAG_ENDSTR: u16 = 0x0700;
|
||||
pub const RTAG_BOUNDARY: u16 = 0x0800;
|
||||
pub const RTAG_PATH: u16 = 0x0900;
|
||||
pub const RTAG_SREF: u16 = 0x0a00;
|
||||
pub const RTAG_AREF: u16 = 0x0b00;
|
||||
pub const RTAG_TEXT: u16 = 0x0c00;
|
||||
pub const RTAG_LAYER: u16 = 0x0d02;
|
||||
pub const RTAG_DATATYPE: u16 = 0x0e02;
|
||||
pub const RTAG_WIDTH: u16 = 0x0f03;
|
||||
pub const RTAG_XY: u16 = 0x1003;
|
||||
pub const RTAG_ENDEL: u16 = 0x1100;
|
||||
pub const RTAG_SNAME: u16 = 0x1206;
|
||||
pub const RTAG_COLROW: u16 = 0x1302;
|
||||
pub const RTAG_NODE: u16 = 0x1500;
|
||||
pub const RTAG_TEXTTYPE: u16 = 0x1602;
|
||||
pub const RTAG_PRESENTATION: u16 = 0x1701;
|
||||
pub const RTAG_SPACING: u16 = 0x1802; // unused; not sure about 02
|
||||
pub const RTAG_STRING: u16 = 0x1906;
|
||||
pub const RTAG_STRANS: u16 = 0x1a01;
|
||||
pub const RTAG_MAG: u16 = 0x1b05;
|
||||
pub const RTAG_ANGLE: u16 = 0x1c05;
|
||||
pub const RTAG_UINTEGER: u16 = 0x1d02; // unused; not sure about 02
|
||||
pub const RTAG_USTRING: u16 = 0x1e06; // unused; not sure about 06
|
||||
pub const RTAG_REFLIBS: u16 = 0x1f06;
|
||||
pub const RTAG_FONTS: u16 = 0x2006;
|
||||
pub const RTAG_PATHTYPE: u16 = 0x2102;
|
||||
pub const RTAG_GENERATIONS: u16 = 0x2202;
|
||||
pub const RTAG_ATTRTABLE: u16 = 0x2306;
|
||||
pub const RTAG_STYPTABLE: u16 = 0x2406; // unused; not sure about 06
|
||||
pub const RTAG_STRTYPE: u16 = 0x2502; // unused
|
||||
pub const RTAG_ELFLAGS: u16 = 0x2601;
|
||||
pub const RTAG_ELKEY: u16 = 0x2703; // unused
|
||||
pub const RTAG_LINKTYPE: u16 = 0x2803; // unused
|
||||
pub const RTAG_LINKKEYS: u16 = 0x2903; // unused
|
||||
pub const RTAG_NODETYPE: u16 = 0x2a02;
|
||||
pub const RTAG_PROPATTR: u16 = 0x2b02;
|
||||
pub const RTAG_PROPVALUE: u16 = 0x2c06;
|
||||
pub const RTAG_BOX: u16 = 0x2d00;
|
||||
pub const RTAG_BOXTYPE: u16 = 0x2e02;
|
||||
pub const RTAG_PLEX: u16 = 0x2f03;
|
||||
pub const RTAG_BGNEXTN: u16 = 0x3003;
|
||||
pub const RTAG_ENDEXTN: u16 = 0x3103;
|
||||
pub const RTAG_TAPENUM: u16 = 0x3202;
|
||||
pub const RTAG_TAPECODE: u16 = 0x3302;
|
||||
pub const RTAG_STRCLASS: u16 = 0x3401;
|
||||
pub const RTAG_RESERVED: u16 = 0x3503;
|
||||
pub const RTAG_FORMAT: u16 = 0x3602;
|
||||
pub const RTAG_MASK: u16 = 0x3706; // list of Layers and dtypes
|
||||
pub const RTAG_ENDMASKS: u16 = 0x3800; // end of MASKS records
|
||||
pub const RTAG_LIBDIRSIZE: u16 = 0x3902;
|
||||
pub const RTAG_SRFNAME: u16 = 0x3a06;
|
||||
pub const RTAG_LIBSECUR: u16 = 0x3b02;
|
||||
pub const RTAG_BORDER: u16 = 0x3c00;
|
||||
pub const RTAG_SOFTFENCE: u16 = 0x3d00;
|
||||
pub const RTAG_HARDFENCE: u16 = 0x3f00;
|
||||
pub const RTAG_SOFTWIRE: u16 = 0x3f00;
|
||||
pub const RTAG_HARDWIRE: u16 = 0x4000;
|
||||
pub const RTAG_PATHPORT: u16 = 0x4100;
|
||||
pub const RTAG_NODEPORT: u16 = 0x4200;
|
||||
pub const RTAG_USERCONSTRAINT: u16 = 0x4300;
|
||||
pub const RTAG_SPACERERROR: u16 = 0x4400;
|
||||
pub const RTAG_CONTACT: u16 = 0x4500;
|
||||
|
||||
/*
|
||||
// data types
|
||||
pub const DATA_TYPE_NONE: u16 = 0x00;
|
||||
pub const DATA_TYPE_BIT: u16 = 0x01;
|
||||
pub const DATA_TYPE_INT16: u16 = 0x02;
|
||||
pub const DATA_TYPE_INT32: u16 = 0x03;
|
||||
pub const DATA_TYPE_REAL32: u16 = 0x04;
|
||||
pub const DATA_TYPE_REAL64: u16 = 0x05;
|
||||
pub const DATA_TYPE_STR: u16 = 0x06;
|
||||
|
||||
pub const MAX_DATA_SIZE: usize = 8;
|
||||
|
||||
/// Returns the size of the given data type in bytes.
|
||||
pub fn data_size(t: u16) -> Option<u16> {
|
||||
match t {
|
||||
x if x == DATA_TYPE_NONE => 0,
|
||||
x if x == DATA_TYPE_BIT => 2,
|
||||
x if x == DATA_TYPE_INT16 => 2,
|
||||
x if x == DATA_TYPE_INT32 => 4,
|
||||
x if x == DATA_TYPE_REAL32 => 4,
|
||||
x if x == DATA_TYPE_REAL64 => 8,
|
||||
_ => 0
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct HEADER;
|
||||
impl Record<Int2> for HEADER {
|
||||
fn tag() -> u16 { RTAG_HEADER }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
//impl Record<Int2> for HEADER;
|
||||
|
||||
pub struct BGNLIB;
|
||||
impl Record<DateTimePair> for BGNLIB {
|
||||
fn tag() -> u16 { RTAG_BGNLIB }
|
||||
fn expected_size() -> Option<u16> { Some(2 * 2 * 6) }
|
||||
}
|
||||
|
||||
pub struct LIBNAME;
|
||||
impl Record<ASCII> for LIBNAME {
|
||||
fn tag() -> u16 { RTAG_LIBNAME }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct UNITS;
|
||||
impl Record<Real8Pair> for UNITS {
|
||||
// (user_units_per_db_unit, db_units_per_meter)
|
||||
fn tag() -> u16 { RTAG_UNITS }
|
||||
fn expected_size() -> Option<u16> { Some(2 * 8) }
|
||||
}
|
||||
|
||||
pub struct ENDLIB;
|
||||
impl Record<Empty> for ENDLIB {
|
||||
fn tag() -> u16 { RTAG_ENDLIB }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct BGNSTR;
|
||||
impl Record<DateTimePair> for BGNSTR {
|
||||
fn tag() -> u16 { RTAG_BGNSTR }
|
||||
fn expected_size() -> Option<u16> { Some(2 * 6) }
|
||||
}
|
||||
|
||||
pub struct STRNAME;
|
||||
impl Record<ASCII> for STRNAME {
|
||||
fn tag() -> u16 { RTAG_STRNAME }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct ENDSTR;
|
||||
impl Record<Empty> for ENDSTR {
|
||||
fn tag() -> u16 { RTAG_ENDSTR }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct BOUNDARY;
|
||||
impl Record<Empty> for BOUNDARY {
|
||||
fn tag() -> u16 { RTAG_BOUNDARY }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct PATH;
|
||||
impl Record<Empty> for PATH {
|
||||
fn tag() -> u16 { RTAG_PATH }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct SREF;
|
||||
impl Record<Empty> for SREF {
|
||||
fn tag() -> u16 { RTAG_SREF }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct AREF;
|
||||
impl Record<Empty> for AREF {
|
||||
fn tag() -> u16 { RTAG_AREF }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct TEXT;
|
||||
impl Record<Empty> for TEXT {
|
||||
fn tag() -> u16 { RTAG_TEXT }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct LAYER;
|
||||
impl Record<Int2> for LAYER {
|
||||
fn tag() -> u16 { RTAG_LAYER }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct DATATYPE;
|
||||
impl Record<Int2> for DATATYPE {
|
||||
fn tag() -> u16 { RTAG_DATATYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct WIDTH;
|
||||
impl Record<Int4> for WIDTH {
|
||||
fn tag() -> u16 { RTAG_WIDTH }
|
||||
fn expected_size() -> Option<u16> { Some(4) }
|
||||
}
|
||||
|
||||
pub struct XY;
|
||||
impl Record<Int4Array> for XY {
|
||||
fn tag() -> u16 { RTAG_XY }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct ENDEL;
|
||||
impl Record<Empty> for ENDEL {
|
||||
fn tag() -> u16 { RTAG_ENDEL }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct SNAME;
|
||||
impl Record<ASCII> for SNAME {
|
||||
fn tag() -> u16 { RTAG_SNAME }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct COLROW;
|
||||
impl Record<Int2Array> for COLROW {
|
||||
fn tag() -> u16 { RTAG_COLROW }
|
||||
fn expected_size() -> Option<u16> { Some(4) }
|
||||
}
|
||||
|
||||
pub struct NODE;
|
||||
impl Record<Empty> for NODE {
|
||||
fn tag() -> u16 { RTAG_NODE }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct TEXTTYPE;
|
||||
impl Record<Int2> for TEXTTYPE {
|
||||
fn tag() -> u16 { RTAG_TEXTTYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct PRESENTATION;
|
||||
impl Record<BitArray> for PRESENTATION {
|
||||
fn tag() -> u16 { RTAG_PRESENTATION }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct SPACING;
|
||||
impl Record<Int2> for SPACING {
|
||||
fn tag() -> u16 { RTAG_SPACING }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct STRING;
|
||||
impl Record<ASCII> for STRING {
|
||||
fn tag() -> u16 { RTAG_STRING }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct STRANS;
|
||||
impl Record<BitArray> for STRANS {
|
||||
fn tag() -> u16 { RTAG_STRANS }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct MAG;
|
||||
impl Record<Real8> for MAG {
|
||||
fn tag() -> u16 { RTAG_MAG }
|
||||
fn expected_size() -> Option<u16> { Some(8) }
|
||||
}
|
||||
|
||||
pub struct ANGLE;
|
||||
impl Record<Real8> for ANGLE {
|
||||
fn tag() -> u16 { RTAG_ANGLE }
|
||||
fn expected_size() -> Option<u16> { Some(8) }
|
||||
}
|
||||
|
||||
pub struct UINTEGER;
|
||||
impl Record<Int2> for UINTEGER {
|
||||
fn tag() -> u16 { RTAG_UINTEGER }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct USTRING;
|
||||
impl Record<ASCII> for USTRING {
|
||||
fn tag() -> u16 { RTAG_USTRING }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct REFLIBS;
|
||||
impl Record<ASCII> for REFLIBS {
|
||||
fn tag() -> u16 { RTAG_REFLIBS }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
impl REFLIBS {
|
||||
pub fn check_size(actual_size: usize) -> Result<(), String> {
|
||||
if actual_size % 44 == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected record size divisible by 44, got {}", actual_size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FONTS;
|
||||
impl Record<ASCII> for FONTS {
|
||||
fn tag() -> u16 { RTAG_FONTS }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
impl FONTS {
|
||||
pub fn check_size(actual_size: usize) -> Result<(), String> {
|
||||
if actual_size % 44 == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected record size divisible by 44, got {}", actual_size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PATHTYPE;
|
||||
impl Record<Int2> for PATHTYPE {
|
||||
fn tag() -> u16 { RTAG_PATHTYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct GENERATIONS;
|
||||
impl Record<Int2> for GENERATIONS {
|
||||
fn tag() -> u16 { RTAG_GENERATIONS }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct ATTRTABLE;
|
||||
impl Record<ASCII> for ATTRTABLE {
|
||||
fn tag() -> u16 { RTAG_ATTRTABLE }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
impl ATTRTABLE {
|
||||
pub fn check_size(actual_size: usize) -> Result<(), String> {
|
||||
if actual_size % 44 == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("Expected record size divisible by 44, got {}", actual_size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct STYPTABLE;
|
||||
impl Record<ASCII> for STYPTABLE {
|
||||
fn tag() -> u16 { RTAG_STYPTABLE }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct STRTYPE;
|
||||
impl Record<Int2> for STRTYPE {
|
||||
fn tag() -> u16 { RTAG_STRTYPE }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct ELFLAGS;
|
||||
impl Record<BitArray> for ELFLAGS {
|
||||
fn tag() -> u16 { RTAG_ELFLAGS }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct ELKEY;
|
||||
impl Record<Int2> for ELKEY {
|
||||
fn tag() -> u16 { RTAG_ELKEY }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct LINKTYPE;
|
||||
impl Record<Int2> for LINKTYPE {
|
||||
fn tag() -> u16 { RTAG_LINKTYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct LINKKEYS;
|
||||
impl Record<Int2> for LINKKEYS {
|
||||
fn tag() -> u16 { RTAG_LINKKEYS }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct NODETYPE;
|
||||
impl Record<Int2> for NODETYPE {
|
||||
fn tag() -> u16 { RTAG_NODETYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct PROPATTR;
|
||||
impl Record<Int2> for PROPATTR {
|
||||
fn tag() -> u16 { RTAG_PROPATTR }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct PROPVALUE;
|
||||
impl Record<ASCII> for PROPVALUE {
|
||||
fn tag() -> u16 { RTAG_PROPVALUE }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct BOX;
|
||||
impl Record<Empty> for BOX {
|
||||
fn tag() -> u16 { RTAG_BOX }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct BOXTYPE;
|
||||
impl Record<Int2> for BOXTYPE {
|
||||
fn tag() -> u16 { RTAG_BOXTYPE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct PLEX;
|
||||
impl Record<Int4> for PLEX {
|
||||
fn tag() -> u16 { RTAG_PLEX }
|
||||
fn expected_size() -> Option<u16> { Some(4) }
|
||||
}
|
||||
|
||||
pub struct BGNEXTN;
|
||||
impl Record<Int4> for BGNEXTN {
|
||||
fn tag() -> u16 { RTAG_BGNEXTN }
|
||||
fn expected_size() -> Option<u16> { Some(4) }
|
||||
}
|
||||
|
||||
pub struct ENDEXTN;
|
||||
impl Record<Int4> for ENDEXTN {
|
||||
fn tag() -> u16 { RTAG_ENDEXTN }
|
||||
fn expected_size() -> Option<u16> { Some(4) }
|
||||
}
|
||||
|
||||
pub struct TAPENUM;
|
||||
impl Record<Int2> for TAPENUM {
|
||||
fn tag() -> u16 { RTAG_TAPENUM }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct TAPECODE;
|
||||
impl Record<Int2Array> for TAPECODE {
|
||||
fn tag() -> u16 { RTAG_TAPECODE }
|
||||
fn expected_size() -> Option<u16> { Some(2 * 6) }
|
||||
}
|
||||
|
||||
pub struct STRCLASS;
|
||||
impl Record<Int2> for STRCLASS {
|
||||
fn tag() -> u16 { RTAG_STRCLASS }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct RESERVED;
|
||||
impl Record<Int2Array> for RESERVED {
|
||||
fn tag() -> u16 { RTAG_RESERVED }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct FORMAT;
|
||||
impl Record<Int2> for FORMAT {
|
||||
fn tag() -> u16 { RTAG_FORMAT }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct MASK;
|
||||
impl Record<ASCII> for MASK {
|
||||
fn tag() -> u16 { RTAG_MASK }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
/// End of MASKS records
|
||||
pub struct ENDMASKS;
|
||||
impl Record<Empty> for ENDMASKS {
|
||||
fn tag() -> u16 { RTAG_ENDMASKS }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct LIBDIRSIZE;
|
||||
impl Record<Int2> for LIBDIRSIZE {
|
||||
fn tag() -> u16 { RTAG_LIBDIRSIZE }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct SRFNAME;
|
||||
impl Record<ASCII> for SRFNAME {
|
||||
fn tag() -> u16 { RTAG_SRFNAME }
|
||||
fn expected_size() -> Option<u16> { None }
|
||||
}
|
||||
|
||||
pub struct LIBSECUR;
|
||||
impl Record<Int2> for LIBSECUR {
|
||||
fn tag() -> u16 { RTAG_LIBSECUR }
|
||||
fn expected_size() -> Option<u16> { Some(2) }
|
||||
}
|
||||
|
||||
pub struct BORDER;
|
||||
impl Record<Empty> for BORDER {
|
||||
fn tag() -> u16 { RTAG_BORDER }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct SOFTFENCE;
|
||||
impl Record<Empty> for SOFTFENCE {
|
||||
fn tag() -> u16 { RTAG_SOFTFENCE }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct HARDFENCE;
|
||||
impl Record<Empty> for HARDFENCE {
|
||||
fn tag() -> u16 { RTAG_HARDFENCE }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct SOFTWIRE;
|
||||
impl Record<Empty> for SOFTWIRE {
|
||||
fn tag() -> u16 { RTAG_SOFTWIRE }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct HARDWIRE;
|
||||
impl Record<Empty> for HARDWIRE {
|
||||
fn tag() -> u16 { RTAG_HARDWIRE }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct PATHPORT;
|
||||
impl Record<Empty> for PATHPORT {
|
||||
fn tag() -> u16 { RTAG_PATHPORT }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct NODEPORT;
|
||||
impl Record<Empty> for NODEPORT {
|
||||
fn tag() -> u16 { RTAG_NODEPORT }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct USERCONSTRAINT;
|
||||
impl Record<Empty> for USERCONSTRAINT {
|
||||
fn tag() -> u16 { RTAG_USERCONSTRAINT }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct SPACERERROR;
|
||||
impl Record<Empty> for SPACERERROR {
|
||||
fn tag() -> u16 { RTAG_SPACERERROR }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
||||
|
||||
pub struct CONTACT;
|
||||
impl Record<Empty> for CONTACT {
|
||||
fn tag() -> u16 { RTAG_CONTACT }
|
||||
fn expected_size() -> Option<u16> { Some(0) }
|
||||
}
|
35
test.py
Normal file
35
test.py
Normal file
@ -0,0 +1,35 @@
|
||||
import ctypes
|
||||
import pyarrow
|
||||
from pyarrow.cffi import ffi
|
||||
|
||||
#c_schema = ffi.new('struct ArrowSchema*')
|
||||
#c_array = ffi.new('struct ArrowArray*')
|
||||
#ptr_schema = int(ffi.cast('uintptr_t', c_schema))
|
||||
#ptr_array = int(ffi.cast('uintptr_t', c_array))
|
||||
|
||||
|
||||
path = '/home/jan/projects/masque/test.gds'
|
||||
|
||||
#clib = ctypes.CDLL('target/debug/libklamath_rs_ext.so')
|
||||
clib = ffi.dlopen('target/debug/libklamath_rs_ext.so')
|
||||
|
||||
|
||||
ret_ptr_array = ffi.new('struct ArrowArray[]', 1)
|
||||
ret_ptr_schema = ffi.new('struct ArrowSchema[]', 1)
|
||||
ffi.cdef('void read_path(char* path, struct ArrowArray* array, struct ArrowSchema* schema);')
|
||||
print(f'{ret_ptr_array[0]=}, {ret_ptr_schema[0]=}')
|
||||
|
||||
clib.read_path(path.encode(), ret_ptr_array, ret_ptr_schema)
|
||||
|
||||
ptr_schema = int(ffi.cast('uintptr_t', ret_ptr_schema))
|
||||
ptr_array = int(ffi.cast('uintptr_t', ret_ptr_array))
|
||||
|
||||
|
||||
print(f'{ret_ptr_array[0]=}, {ret_ptr_schema[0]=}')
|
||||
print(f'python {ptr_array=:x} {ptr_schema=:x}')
|
||||
#print(f'{ret_ptr_array[0].buffers=} {ret_ptr_schema[0][0]=}')
|
||||
|
||||
arr_new = pyarrow.Array._import_from_c(ptr_array, ptr_schema)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user