libreda_oasis/lib.rs
1// Copyright (c) 2018-2021 Thomas Kramer.
2// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Library for reading and writing OASIS files.
7//!
8//! OASIS is a binary format for storing two-dimensional geometrical data as it is commonly used
9//! for silicon chip layouts. Its purpose is very similar to the older GDS2 format.
10//!
11//! # Examples
12//!
13//! ## Read a layout from OASIS
14//!
15//! ```
16//! use std::fs::File;
17//! use libreda_oasis::OASISStreamReader;
18//! // Import the `LayoutStreamReader` trait.
19//! use libreda_db::prelude::*;
20//!
21//! let filename = "./tests/data/INVX1_no_compression.oas";
22//! // Open the OASIS file for reading.
23//! let mut f = File::open(filename).unwrap();
24//!
25//! // Create an empty layout that will be populated by the OASIS reader.
26//! let mut layout = Chip::new();
27//!
28//! // Create a default OASIS reader and parse the data from the file.
29//! let result = OASISStreamReader::default()
30//! .read_layout(&mut f, &mut layout);
31//!
32//! // Assert that there was no error.
33//! assert!(result.is_ok());
34//! ```
35//!
36//! ## Write a layout to OASIS
37//!
38//! ```
39//! use std::fs::File;
40//! use libreda_oasis::OASISStreamWriter;
41//! // Import the `LayoutStreamReader` trait.
42//! use libreda_db::prelude::*;
43//!
44//! // Create an empty layout.
45//! let layout = Chip::new();
46//!
47//! let mut f = File::create("./tests/data/empty_layout_out.oas").unwrap();
48//!
49//! let writer = OASISStreamWriter::default();
50//!
51//! // Write the (empty) layout to the file.
52//! let write_result = writer.write_layout(&mut f, &layout);
53//!
54//! // Assert that there was no error.
55//! assert!(write_result.is_ok());
56//! ```
57//!
58//! # References
59//!
60//! * [OASIS specification draft](https://web.archive.org/web/20200727024915if_/http://www.wrcad.com/oasis/oasis-3626-042303-draft.pdf)
61
62#![deny(missing_docs)]
63// As long as there are some unimplemented parts be less strict with warnings.
64// TODO: Remove this.
65#![allow(dead_code, unused_assignments)]
66
67#[macro_use]
68extern crate log;
69
70pub use libreda_db;
71
72mod base_types;
73mod reader;
74mod writer;
75
76use crate::base_types::Repetition;
77use base_types::*;
78use std::io::{Read, Write};
79
80pub use libreda_db::prelude::{LayoutBase, LayoutEdit, LayoutStreamReader, LayoutStreamWriter};
81pub use writer::OASISWriterConfig;
82
83/// Reader for the OASIS file format.
84#[derive(Clone, Default, Debug)]
85pub struct OASISStreamReader {}
86
87impl OASISStreamReader {
88 /// Create a new OASIS reader.
89 pub fn new() -> Self {
90 OASISStreamReader {}
91 }
92}
93
94/// Writer for the OASIS file format.
95#[derive(Clone, Default, Debug)]
96pub struct OASISStreamWriter {
97 config: OASISWriterConfig,
98}
99
100impl OASISStreamWriter {
101 /// Create a new OASIS writer with a custom configuration.
102 pub fn with_config(config: OASISWriterConfig) -> Self {
103 Self { config }
104 }
105}
106
107/// Reader implementation.
108impl LayoutStreamReader for OASISStreamReader {
109 type Error = OASISReadError;
110 fn read_layout<R: Read, L: LayoutEdit<Coord = SInt>>(
111 &self,
112 r: &mut R,
113 layout: &mut L,
114 ) -> Result<(), Self::Error> {
115 reader::read_layout(r, layout)
116 }
117}
118
119/// Writer implementation.
120impl LayoutStreamWriter for OASISStreamWriter {
121 type Error = OASISWriteError;
122 fn write_layout<W: Write, L: LayoutBase<Coord = SInt>>(
123 &self,
124 w: &mut W,
125 layout: &L,
126 ) -> Result<(), Self::Error> {
127 writer::write_layout(w, layout, &self.config)
128 }
129}
130
131/// Type for the `xy-mode` modal variable.
132#[derive(Copy, Clone, Debug, Default)]
133enum XYMode {
134 #[default]
135 Absolute,
136 Relative,
137}
138
139/// Datatype for either a name string or a reference number to a name string.
140/// In OASIS names can be given explicitly with a string or by reference number.
141/// In case of a reference number, the name string might be specified later in the file.
142#[derive(Debug, Clone)]
143pub(crate) enum NameOrRef {
144 /// A known name string.
145 Name(String),
146 /// A reference number to a name string.
147 NameRef(UInt),
148}
149
150/// Modal variables.
151/// Modal variables are used for implicit and context dependent values.
152/// At the beginning at the file or of a CELL record all modal variables are set to `None` except
153/// `placement_x`, `placement_y`, `geometry_x`, `geometry_y`, `text_x`, and `text_y`.
154#[derive(Debug)]
155struct Modal<C> {
156 /// Related records: PLACEMENT, TEXT, POLYGON, PATH,
157 /// RECTANGLE, TRAPEZOID, CTRAPEZOID,
158 /// CIRCLE, XGEOMETRY
159 repetition: Option<Repetition>,
160
161 /// Related records: PLACEMENT
162 placement_x: SInt,
163 /// Related records: PLACEMENT
164 placement_y: SInt,
165 /// Reference to an implicit cell to be used in the PLACEMENT record.
166 /// Related records: PLACEMENT
167 placement_cell: Option<C>,
168
169 /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID,
170 /// CTRAPEZOID, CIRCLE, XGEOMETRY
171 layer: Option<UInt>,
172 /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID,
173 /// CTRAPEZOID, CIRCLE, XGEOMETRY
174 datatype: Option<UInt>,
175
176 /// Related records: TEXT
177 textlayer: Option<UInt>,
178 /// Related records: TEXT
179 texttype: Option<UInt>,
180 /// Related records: TEXT
181 text_x: SInt,
182 /// Related records: TEXT
183 text_y: SInt,
184 /// Related records: TEXT
185 text_string: Option<String>,
186
187 /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID
188 /// CTRAPEZOID, CIRCLE, XGEOMETRY
189 geometry_x: SInt,
190 /// Related records: POLYGON, PATH, RECTANGLE, TRAPEZOID
191 /// CTRAPEZOID, CIRCLE, XGEOMETRY
192 geometry_y: SInt,
193
194 /// Related records: PLACEMENT, TEXT, POLYGON, PATH,
195 /// RECTANGLE, TRAPEZOID, CTRAPEZOID,
196 /// CIRCLE, XGEOMETRY,
197 /// XYABSOLUTE, XYRELATIVE
198 /// Controls how `x` and `y` values are treated in the related records.
199 xy_mode: XYMode,
200
201 /// Related records: RECTANGLE, TRAPEZOID, CTRAPEZOID
202 geometry_w: Option<UInt>,
203 /// Related records: RECTANGLE, TRAPEZOID, CTRAPEZOID
204 geometry_h: Option<UInt>,
205
206 /// Related records: POLYGON
207 polygon_point_list: Option<PointList>,
208
209 /// Related records: PATH
210 path_halfwidth: Option<UInt>,
211 /// Related records: PATH
212 path_point_list: Option<PointList>,
213 /// Related records: PATH
214 path_start_extension: Option<SInt>,
215 /// Related records: PATH
216 path_end_extension: Option<SInt>,
217
218 /// Related records: CTRAPEZOID
219 ctrapezoid_type: Option<()>,
220
221 /// Related records: CIRCLE
222 circle_radius: Option<()>,
223
224 /// Related records: PROPERTY
225 last_property_name: Option<NameOrRef>,
226 /// Related records: PROPERTY
227 last_value_list: Option<Vec<PropertyValue>>,
228}
229
230impl<C> Default for Modal<C> {
231 fn default() -> Self {
232 Self {
233 repetition: None,
234 placement_x: Default::default(),
235 placement_y: Default::default(),
236 placement_cell: None,
237 layer: None,
238 datatype: None,
239 textlayer: None,
240 texttype: None,
241 text_x: Default::default(),
242 text_y: Default::default(),
243 text_string: None,
244 geometry_x: Default::default(),
245 geometry_y: Default::default(),
246 xy_mode: Default::default(),
247 geometry_w: None,
248 geometry_h: None,
249 polygon_point_list: None,
250 path_halfwidth: None,
251 path_point_list: None,
252 path_start_extension: None,
253 path_end_extension: None,
254 ctrapezoid_type: None,
255 circle_radius: None,
256 last_property_name: None,
257 last_value_list: None,
258 }
259 }
260}
261
262impl<C> Modal<C> {
263 /// Set all modal variables to `None` except
264 /// `placement_x`, `placement_y`, `geometry_x`, `geometry_y`, `text_x`, and `text_y` which are set to `0`.
265 fn reset(&mut self) {
266 let reset = Self::default();
267 *self = reset;
268 }
269}