gds/
write.rs

1//! Utilities for byte-encoding and writing.
2
3// Local imports
4use super::*;
5
6/// A GDS writer.
7pub struct GdsWriter<'wr> {
8    /// Write destination.
9    dest: Box<dyn Write + 'wr>,
10}
11
12impl<'wr> GdsWriter<'wr> {
13    /// Creates new [GdsWriter] with destination file `fname`.
14    pub fn open(fname: impl AsRef<Path>) -> GdsResult<Self> {
15        Ok(Self::new(File::create(fname)?))
16    }
17
18    /// Creates a new [GdsWriter] to destination `dest`.
19    ///
20    /// Writes to `dest` are buffered, since records are encoded a few bytes at a time.
21    pub fn new(dest: impl Write + 'wr) -> Self {
22        Self {
23            dest: Box::new(BufWriter::new(dest)),
24        }
25    }
26
27    /// Writes [GdsLibrary] `lib` to our destination.
28    pub fn write_lib(&mut self, lib: &GdsLibrary) -> GdsResult<()> {
29        // `write_lib` is our typicaly entry point when writing to file.
30        // It quickly dispatches most behavior off to our implementation of the [Encode] trait.
31        self.encode_lib(lib)?;
32        // Flush explicitly: dropping the buffer would discard any write error.
33        self.dest.flush()?;
34        Ok(())
35    }
36
37    /// Helper to write a sequence of [GdsRecord] references.
38    fn write_records(&mut self, records: &[GdsRecord]) -> GdsResult<()> {
39        for r in records {
40            self.write_record(r)?;
41        }
42        Ok(())
43    }
44
45    /// Encodes into bytes and write ontos `dest`.
46    fn write_record(&mut self, record: &GdsRecord) -> GdsResult<()> {
47        // This is split in two parts - header and data - largely to ease handling the variety of datatypes
48        self.write_record_header(record)?;
49        self.write_record_content(record)?;
50        Ok(())
51    }
52
53    fn write_record_header(&mut self, record: &GdsRecord) -> GdsResult<()> {
54        // A quick closure for GDS's "even-lengths-only allowed" strings
55        let gds_strlen = |s: &str| -> usize { s.len() + s.len() % 2 };
56        // First grab the header info: RecordType, DataType, and length
57        use GdsDataType::{BitArray, F64, I16, I32, NoData, Str};
58        let (rtype, dtype, len) = match record {
59            // Library-Level Records
60            GdsRecord::Header { .. } => (GdsRecordType::Header, I16, 2),
61            GdsRecord::BgnLib { .. } => (GdsRecordType::BgnLib, I16, 24),
62            GdsRecord::LibName(s) => (GdsRecordType::LibName, Str, gds_strlen(s)),
63            GdsRecord::Units(_, _) => (GdsRecordType::Units, F64, 16),
64            GdsRecord::EndLib => (GdsRecordType::EndLib, NoData, 0),
65
66            // Structure (Cell) Level Records
67            GdsRecord::BgnStruct { .. } => (GdsRecordType::BgnStruct, I16, 24),
68            GdsRecord::StructName(s) => (GdsRecordType::StructName, Str, gds_strlen(s)),
69            GdsRecord::StructRefName(s) => (GdsRecordType::StructRefName, Str, gds_strlen(s)),
70            GdsRecord::EndStruct => (GdsRecordType::EndStruct, NoData, 0),
71
72            // Element-Level Records
73            GdsRecord::Boundary => (GdsRecordType::Boundary, NoData, 0),
74            GdsRecord::Path => (GdsRecordType::Path, NoData, 0),
75            GdsRecord::StructRef => (GdsRecordType::StructRef, NoData, 0),
76            GdsRecord::ArrayRef => (GdsRecordType::ArrayRef, NoData, 0),
77            GdsRecord::Text => (GdsRecordType::Text, NoData, 0),
78            GdsRecord::Layer(_) => (GdsRecordType::Layer, I16, 2),
79            GdsRecord::DataType(_) => (GdsRecordType::DataType, I16, 2),
80            GdsRecord::Width(_) => (GdsRecordType::Width, I32, 4),
81            GdsRecord::Xy(d) => (GdsRecordType::Xy, I32, 4 * d.len()),
82            GdsRecord::EndElement => (GdsRecordType::EndElement, NoData, 0),
83
84            // More (less well-categorized here) record-types
85            GdsRecord::ColRow { .. } => (GdsRecordType::ColRow, I16, 4),
86            GdsRecord::Node => (GdsRecordType::Node, NoData, 0),
87            GdsRecord::TextType(_) => (GdsRecordType::TextType, I16, 2),
88            GdsRecord::Presentation(_, _) => (GdsRecordType::Presentation, BitArray, 2),
89            GdsRecord::String(s) => (GdsRecordType::String, Str, gds_strlen(s)),
90            GdsRecord::Strans(_, _) => (GdsRecordType::Strans, BitArray, 2),
91            GdsRecord::Mag(_) => (GdsRecordType::Mag, F64, 8),
92            GdsRecord::Angle(_) => (GdsRecordType::Angle, F64, 8),
93            GdsRecord::RefLibs(s) => (GdsRecordType::RefLibs, Str, gds_strlen(s)),
94            GdsRecord::Fonts(s) => (GdsRecordType::Fonts, Str, gds_strlen(s)),
95            GdsRecord::PathType(_) => (GdsRecordType::PathType, I16, 2),
96            GdsRecord::Generations(_) => (GdsRecordType::Generations, I16, 2),
97            GdsRecord::AttrTable(s) => (GdsRecordType::AttrTable, Str, gds_strlen(s)),
98            GdsRecord::ElemFlags(_, _) => (GdsRecordType::ElemFlags, BitArray, 2),
99            GdsRecord::Nodetype(_) => (GdsRecordType::Nodetype, I16, 2),
100            GdsRecord::PropAttr(_) => (GdsRecordType::PropAttr, I16, 2),
101            GdsRecord::PropValue(s) => (GdsRecordType::PropValue, Str, gds_strlen(s)),
102            GdsRecord::Box => (GdsRecordType::Box, NoData, 0),
103            GdsRecord::BoxType(_) => (GdsRecordType::BoxType, I16, 2),
104            GdsRecord::Plex(_) => (GdsRecordType::Plex, I32, 4),
105            GdsRecord::BeginExtn(_) => (GdsRecordType::BeginExtn, I32, 4),
106            GdsRecord::EndExtn(_) => (GdsRecordType::EndExtn, I32, 4),
107            GdsRecord::TapeNum(_) => (GdsRecordType::TapeNum, I16, 2),
108            GdsRecord::TapeCode(_) => (GdsRecordType::TapeCode, I16, 12),
109            GdsRecord::Format(_) => (GdsRecordType::Format, I16, 2),
110            GdsRecord::Mask(s) => (GdsRecordType::Mask, Str, gds_strlen(s)),
111            GdsRecord::EndMasks => (GdsRecordType::EndMasks, NoData, 0),
112            GdsRecord::LibDirSize(_) => (GdsRecordType::LibDirSize, I16, 2),
113            GdsRecord::SrfName(s) => (GdsRecordType::SrfName, Str, gds_strlen(s)),
114            GdsRecord::LibSecur(_) => (GdsRecordType::LibSecur, I16, 2),
115        };
116        // Send those header-bytes to the writer.
117        // Include the four header bytes in total-length.
118        match u16::try_from(len + 4) {
119            Ok(val) => self.dest.write_u16::<BigEndian>(val)?,
120            Err(_) => return Err(GdsError::RecordLen(len)),
121        };
122        self.dest.write_u8(rtype as u8)?;
123        self.dest.write_u8(dtype as u8)?;
124        Ok(())
125    }
126
127    fn write_record_content(&mut self, record: &GdsRecord) -> GdsResult<()> {
128        // Now write the data portion
129        // This section is generally organized by DataType
130        match record {
131            // NoData
132            GdsRecord::EndLib
133            | GdsRecord::EndStruct
134            | GdsRecord::Boundary
135            | GdsRecord::Path
136            | GdsRecord::StructRef
137            | GdsRecord::ArrayRef
138            | GdsRecord::Text
139            | GdsRecord::EndElement
140            | GdsRecord::Node
141            | GdsRecord::Box
142            | GdsRecord::EndMasks => (),
143
144            // BitArrays
145            GdsRecord::Presentation(d0, d1)
146            | GdsRecord::Strans(d0, d1)
147            | GdsRecord::ElemFlags(d0, d1) => {
148                self.dest.write_u8(*d0)?;
149                self.dest.write_u8(*d1)?;
150            }
151            // Single I16s
152            GdsRecord::Header { version: d }
153            | GdsRecord::Layer(d)
154            | GdsRecord::DataType(d)
155            | GdsRecord::TextType(d)
156            | GdsRecord::PathType(d)
157            | GdsRecord::Generations(d)
158            | GdsRecord::Nodetype(d)
159            | GdsRecord::PropAttr(d)
160            | GdsRecord::BoxType(d)
161            | GdsRecord::TapeNum(d)
162            | GdsRecord::Format(d)
163            | GdsRecord::LibDirSize(d)
164            | GdsRecord::LibSecur(d) => self.dest.write_i16::<BigEndian>(*d)?,
165
166            // Single I32s
167            GdsRecord::Width(d)
168            | GdsRecord::Plex(d)
169            | GdsRecord::BeginExtn(d)
170            | GdsRecord::EndExtn(d) => self.dest.write_i32::<BigEndian>(*d)?,
171            // Single F64s
172            GdsRecord::Mag(d) | GdsRecord::Angle(d) => {
173                self.dest.write_u64::<BigEndian>(GdsFloat64::encode(*d))?
174            }
175            // "Structs"
176            GdsRecord::Units(d0, d1) => {
177                self.dest.write_u64::<BigEndian>(GdsFloat64::encode(*d0))?;
178                self.dest.write_u64::<BigEndian>(GdsFloat64::encode(*d1))?;
179            }
180            GdsRecord::ColRow { cols, rows } => {
181                self.dest.write_i16::<BigEndian>(*cols)?;
182                self.dest.write_i16::<BigEndian>(*rows)?;
183            }
184            // Fixed-Length Arrays
185            GdsRecord::BgnLib { dates: d } | GdsRecord::BgnStruct { dates: d } => {
186                for val in d.iter() {
187                    self.dest.write_i16::<BigEndian>(*val)?;
188                }
189            }
190            // Vectors
191            GdsRecord::TapeCode(d) => {
192                for val in d.iter() {
193                    self.dest.write_i16::<BigEndian>(*val)?;
194                }
195            }
196            GdsRecord::Xy(d) => {
197                for val in d.iter() {
198                    self.dest.write_i32::<BigEndian>(*val)?;
199                }
200            }
201            // Strings
202            GdsRecord::LibName(s)
203            | GdsRecord::StructName(s)
204            | GdsRecord::StructRefName(s)
205            | GdsRecord::String(s)
206            | GdsRecord::RefLibs(s)
207            | GdsRecord::Fonts(s)
208            | GdsRecord::AttrTable(s)
209            | GdsRecord::PropValue(s)
210            | GdsRecord::Mask(s)
211            | GdsRecord::SrfName(s) => {
212                for b in s.as_bytes() {
213                    self.dest.write_u8(*b)?;
214                }
215                if s.len() % 2 != 0 {
216                    // Pad odd-length strings with a zero-valued byte
217                    self.dest.write_u8(0x00)?;
218                }
219            }
220        };
221        Ok(())
222    }
223}
224
225/// [Encode] implementation for [GdsWriter].
226///
227/// Dispatches record-level calls back to the `write_record(s)` methods.
228impl Encode for GdsWriter<'_> {
229    fn encode_record(&mut self, record: GdsRecord) -> GdsResult<()> {
230        self.write_record(&record)
231    }
232    fn encode_records(&mut self, records: &[GdsRecord]) -> GdsResult<()> {
233        self.write_records(records)
234    }
235}
236
237/// An object that can be encoded in a GDS file.
238///
239/// Performs conversion of each element in the [GdsLibrary] tree to [GdsRecord]s,
240/// each passed to its `encode_record` (singular) or `encode_records` (plural) methods.
241/// Each type is encoded in the order recommended by the GDSII spec.
242///
243/// Most of the behavior required of [GdsWriter] is implemented in this trait.
244/// It is broken out into a trait to enable alternate "destinations" for the encoded records,
245/// such as collecting them in a [Vec] as done by [GdsRecordList].
246trait Encode {
247    // Virtual / Required Methods
248    /// Encodes a single [GdsRecord].
249    fn encode_record(&mut self, record: GdsRecord) -> GdsResult<()>;
250    /// Encodes an array of [GdsRecord]s.
251    fn encode_records(&mut self, records: &[GdsRecord]) -> GdsResult<()>;
252
253    // Default Methods
254    /// Encodes a [GdsLibrary].
255    fn encode_lib(&mut self, lib: &GdsLibrary) -> GdsResult<()> {
256        // Write our header content
257        self.encode_records(&[
258            GdsRecord::Header {
259                version: lib.version,
260            },
261            GdsRecord::BgnLib {
262                dates: lib.dates.encode().to_vec(),
263            },
264            GdsRecord::LibName(lib.name.clone()),
265            GdsRecord::Units(lib.units.0, lib.units.1),
266        ])?;
267        // Write all of our Structs/Cells
268        for strukt in lib.structs.iter() {
269            self.encode_struct(strukt)?;
270        }
271        // And finally, the library terminator
272        self.encode_record(GdsRecord::EndLib)?;
273        Ok(())
274    }
275
276    /// Encodes a [GdsStruct].
277    fn encode_struct(&mut self, strukt: &GdsStruct) -> GdsResult<()> {
278        // Write the header content
279        self.encode_records(&[
280            GdsRecord::BgnStruct {
281                dates: strukt.dates.encode().to_vec(),
282            },
283            GdsRecord::StructName(strukt.name.clone()),
284        ])?;
285        // Write each of our elements
286        for elem in strukt.elems.iter() {
287            self.encode_element(elem)?;
288        }
289        // And its terminator
290        self.encode_record(GdsRecord::EndStruct)?;
291        Ok(())
292    }
293
294    /// Encodes a [GdsElement], dispatching across its variants.
295    fn encode_element(&mut self, elem: &GdsElement) -> GdsResult<()> {
296        use GdsElement::*;
297        match elem {
298            GdsBoundary(e) => self.encode_boundary(e)?,
299            GdsPath(e) => self.encode_path(e)?,
300            GdsStructRef(e) => self.encode_struct_ref(e)?,
301            GdsArrayRef(e) => self.encode_array_ref(e)?,
302            GdsTextElem(e) => self.encode_text_elem(e)?,
303            GdsNode(e) => self.encode_node(e)?,
304            GdsBox(e) => self.encode_box(e)?,
305        };
306        Ok(())
307    }
308
309    /// Encodes a [GdsPath].
310    fn encode_path(&mut self, path: &GdsPath) -> GdsResult<()> {
311        self.encode_record(GdsRecord::Path)?;
312        if let Some(ref e) = path.elflags {
313            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
314        }
315        if let Some(ref e) = path.plex {
316            self.encode_record(GdsRecord::Plex(e.0))?;
317        }
318        self.encode_record(GdsRecord::Layer(path.layer))?;
319        self.encode_record(GdsRecord::DataType(path.datatype))?;
320        if let Some(ref e) = path.path_type {
321            self.encode_record(GdsRecord::PathType(*e))?;
322        }
323        if let Some(ref e) = path.width {
324            self.encode_record(GdsRecord::Width(*e))?;
325        }
326        if let Some(ref e) = path.begin_extn {
327            self.encode_record(GdsRecord::BeginExtn(*e))?;
328        }
329        if let Some(ref e) = path.end_extn {
330            self.encode_record(GdsRecord::EndExtn(*e))?;
331        }
332        self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(&path.xy)))?;
333        for prop in path.properties.iter() {
334            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
335            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
336        }
337        self.encode_record(GdsRecord::EndElement)?;
338        Ok(())
339    }
340
341    /// Encodes a [GdsBoundary].
342    fn encode_boundary(&mut self, boundary: &GdsBoundary) -> GdsResult<()> {
343        self.encode_record(GdsRecord::Boundary)?;
344        if let Some(ref e) = boundary.elflags {
345            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
346        }
347        if let Some(ref e) = boundary.plex {
348            self.encode_record(GdsRecord::Plex(e.0))?;
349        }
350        self.encode_record(GdsRecord::Layer(boundary.layer))?;
351        self.encode_record(GdsRecord::DataType(boundary.datatype))?;
352        self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(&boundary.xy)))?;
353        for prop in boundary.properties.iter() {
354            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
355            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
356        }
357        self.encode_record(GdsRecord::EndElement)?;
358        Ok(())
359    }
360
361    /// Encodes a [GdsStructRef].
362    fn encode_struct_ref(&mut self, sref: &GdsStructRef) -> GdsResult<()> {
363        self.encode_record(GdsRecord::StructRef)?;
364        if let Some(ref e) = sref.elflags {
365            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
366        }
367        if let Some(ref e) = sref.plex {
368            self.encode_record(GdsRecord::Plex(e.0))?;
369        }
370        self.encode_record(GdsRecord::StructRefName(sref.name.clone()))?;
371        if let Some(ref e) = sref.strans {
372            self.encode_strans(e)?;
373        }
374        self.encode_record(GdsRecord::Xy(GdsPoint::flatten(&sref.xy)))?;
375        for prop in sref.properties.iter() {
376            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
377            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
378        }
379        self.encode_record(GdsRecord::EndElement)?;
380        Ok(())
381    }
382
383    /// Encodes a [GdsArrayRef].
384    fn encode_array_ref(&mut self, aref: &GdsArrayRef) -> GdsResult<()> {
385        self.encode_record(GdsRecord::ArrayRef)?;
386        if let Some(ref e) = aref.elflags {
387            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
388        }
389        if let Some(ref e) = aref.plex {
390            self.encode_record(GdsRecord::Plex(e.0))?;
391        }
392        self.encode_record(GdsRecord::StructRefName(aref.name.clone()))?;
393        if let Some(ref e) = aref.strans {
394            self.encode_strans(e)?;
395        }
396        self.encode_record(GdsRecord::ColRow {
397            cols: aref.cols,
398            rows: aref.rows,
399        })?;
400        let mut xy = GdsPoint::flatten(&aref.xy[0]);
401        xy.extend(GdsPoint::flatten(&aref.xy[1]));
402        xy.extend(GdsPoint::flatten(&aref.xy[2]));
403        self.encode_record(GdsRecord::Xy(xy))?;
404        for prop in aref.properties.iter() {
405            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
406            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
407        }
408        self.encode_record(GdsRecord::EndElement)?;
409        Ok(())
410    }
411
412    /// Encodes a [GdsTextElem].
413    fn encode_text_elem(&mut self, text: &GdsTextElem) -> GdsResult<()> {
414        self.encode_record(GdsRecord::Text)?;
415        if let Some(ref e) = text.elflags {
416            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
417        }
418        if let Some(ref e) = text.plex {
419            self.encode_record(GdsRecord::Plex(e.0))?;
420        }
421        self.encode_record(GdsRecord::Layer(text.layer))?;
422        self.encode_record(GdsRecord::TextType(text.texttype))?;
423        if let Some(ref e) = text.presentation {
424            self.encode_record(GdsRecord::Presentation(e.0, e.1))?;
425        }
426        if let Some(ref e) = text.path_type {
427            self.encode_record(GdsRecord::PathType(*e))?;
428        }
429        if let Some(ref e) = text.width {
430            self.encode_record(GdsRecord::Width(*e))?;
431        }
432        if let Some(ref e) = text.strans {
433            self.encode_strans(e)?;
434        }
435        self.encode_record(GdsRecord::Xy(GdsPoint::flatten(&text.xy)))?;
436        self.encode_record(GdsRecord::String(text.string.clone()))?;
437        for prop in text.properties.iter() {
438            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
439            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
440        }
441        self.encode_record(GdsRecord::EndElement)?;
442        Ok(())
443    }
444
445    /// Encodes a [GdsNode].
446    fn encode_node(&mut self, node: &GdsNode) -> GdsResult<()> {
447        self.encode_record(GdsRecord::Node)?;
448        if let Some(ref e) = node.elflags {
449            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
450        }
451        if let Some(ref e) = node.plex {
452            self.encode_record(GdsRecord::Plex(e.0))?;
453        }
454        self.encode_record(GdsRecord::Layer(node.layer))?;
455        self.encode_record(GdsRecord::Nodetype(node.nodetype))?;
456        self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(&node.xy)))?;
457        for prop in node.properties.iter() {
458            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
459            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
460        }
461        self.encode_record(GdsRecord::EndElement)?;
462        Ok(())
463    }
464
465    /// Encodes a [GdsBox].
466    fn encode_box(&mut self, box_: &GdsBox) -> GdsResult<()> {
467        self.encode_record(GdsRecord::Box)?;
468        if let Some(ref e) = box_.elflags {
469            self.encode_record(GdsRecord::ElemFlags(e.0, e.1))?;
470        }
471        if let Some(ref e) = box_.plex {
472            self.encode_record(GdsRecord::Plex(e.0))?;
473        }
474        self.encode_record(GdsRecord::Layer(box_.layer))?;
475        self.encode_record(GdsRecord::BoxType(box_.boxtype))?;
476        self.encode_record(GdsRecord::Xy(GdsPoint::flatten_vec(box_.xy.as_ref())))?;
477        for prop in box_.properties.iter() {
478            self.encode_record(GdsRecord::PropAttr(prop.attr))?;
479            self.encode_record(GdsRecord::PropValue(prop.value.clone()))?;
480        }
481        self.encode_record(GdsRecord::EndElement)?;
482        Ok(())
483    }
484
485    /// Encodes a [GdsStrans].
486    fn encode_strans(&mut self, strans: &GdsStrans) -> GdsResult<()> {
487        self.encode_record(GdsRecord::Strans(
488            (strans.reflected as u8) << 7,
489            ((strans.abs_mag as u8) << 2) | ((strans.abs_angle as u8) << 1),
490        ))?;
491        if let Some(ref e) = strans.mag {
492            self.encode_record(GdsRecord::Mag(*e))?;
493        }
494        if let Some(ref e) = strans.angle {
495            self.encode_record(GdsRecord::Angle(*e))?;
496        }
497        Ok(())
498    }
499}
500
501impl GdsDateTimes {
502    /// Encodes a date-time in GDSII's vector of i16's format.
503    pub fn encode(&self) -> [i16; 12] {
504        [
505            self.modified.date().year() as i16,
506            self.modified.date().month() as i16,
507            self.modified.date().day() as i16,
508            self.modified.time().hour() as i16,
509            self.modified.time().minute() as i16,
510            self.modified.time().second() as i16,
511            self.accessed.date().year() as i16,
512            self.accessed.date().month() as i16,
513            self.accessed.date().day() as i16,
514            self.accessed.time().hour() as i16,
515            self.accessed.time().minute() as i16,
516            self.accessed.time().second() as i16,
517        ]
518    }
519}