Skip to main content

klayout_core/
cell.rs

1//! Cells: frozen, immutable artifacts produced by freezing a `CellBuilder`.
2//!
3//! Invariants held after `CellBuilder::freeze`:
4//! * `content_hash` is a deterministic blake3 of the canonical encoding.
5//!   Same shapes/instances/ports/properties → same hash, byte-stable across
6//!   runs and platforms.
7//! * `local_bbox` is the union of shape bboxes (instance subtrees not included).
8//! * `shapes` keys are sorted by `LayerIndex`; per-layer shape order is the
9//!   insertion order from the builder, but hashing sorts canonically.
10//! * `instances`, `ports`, `properties` are stored in insertion order;
11//!   hashing sorts canonically. UI/IO consumers may sort if they care.
12//!
13//! Mutation after freeze is impossible — there is no public `&mut Cell`.
14
15use crate::coord::{Bbox, Point, Trans};
16use crate::hash::ContentHash;
17use crate::instance::{Instance, Repetition};
18use crate::layer::LayerIndex;
19use crate::library::Library;
20use crate::port::Port;
21use crate::properties::{Properties, PropertyValue};
22use crate::shape::Shape;
23use smol_str::SmolStr;
24use std::collections::BTreeMap;
25use std::fmt;
26use std::num::NonZeroU32;
27
28#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
29pub struct CellName(pub SmolStr);
30
31impl CellName {
32    pub fn new(s: impl Into<SmolStr>) -> Self {
33        Self(s.into())
34    }
35
36    pub fn as_str(&self) -> &str {
37        self.0.as_str()
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.0.is_empty()
42    }
43}
44
45impl fmt::Display for CellName {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.write_str(self.0.as_str())
48    }
49}
50
51impl<S: Into<SmolStr>> From<S> for CellName {
52    fn from(s: S) -> Self {
53        Self(s.into())
54    }
55}
56
57#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
58pub struct CellId(NonZeroU32);
59
60impl CellId {
61    pub(crate) fn from_slot(slot: usize) -> Self {
62        // CellId packs a 1-based slot into a NonZeroU32; the +1 cannot
63        // wrap because `slot` comes from a Vec index in the library
64        // arena (well below 2^32 in any plausible design), and the
65        // result is therefore always ≥ 1.
66        let n = u32::try_from(slot + 1)
67            .expect("CellId arena slot exceeded u32::MAX (≈4B cells)");
68        Self(NonZeroU32::new(n).expect("CellId: slot+1 ≥ 1 by construction"))
69    }
70
71    pub(crate) fn slot(self) -> usize {
72        self.0.get() as usize - 1
73    }
74
75    pub fn raw(self) -> u32 {
76        self.0.get()
77    }
78}
79
80#[derive(Clone, Debug, Default)]
81pub struct ShapeBag {
82    shapes: Vec<Shape>,
83}
84
85impl ShapeBag {
86    pub fn new() -> Self {
87        Self::default()
88    }
89
90    pub fn push(&mut self, s: Shape) {
91        self.shapes.push(s);
92    }
93
94    pub fn iter(&self) -> impl Iterator<Item = &Shape> {
95        self.shapes.iter()
96    }
97
98    pub fn len(&self) -> usize {
99        self.shapes.len()
100    }
101
102    pub fn is_empty(&self) -> bool {
103        self.shapes.is_empty()
104    }
105}
106
107#[derive(Clone, Debug)]
108pub struct Cell {
109    pub(crate) name: CellName,
110    pub(crate) shapes: BTreeMap<LayerIndex, ShapeBag>,
111    pub(crate) instances: Vec<Instance>,
112    pub(crate) ports: Vec<Port>,
113    pub(crate) properties: Properties,
114    pub(crate) local_bbox: Bbox,
115    pub(crate) content_hash: ContentHash,
116}
117
118impl Cell {
119    pub fn name(&self) -> &CellName {
120        &self.name
121    }
122
123    pub fn content_hash(&self) -> ContentHash {
124        self.content_hash
125    }
126
127    pub fn local_bbox(&self) -> Bbox {
128        self.local_bbox
129    }
130
131    pub fn shapes_on(&self, layer: LayerIndex) -> impl Iterator<Item = &Shape> {
132        self.shapes
133            .get(&layer)
134            .map(|b| b.iter())
135            .into_iter()
136            .flatten()
137    }
138
139    pub fn layers(&self) -> impl Iterator<Item = LayerIndex> + '_ {
140        self.shapes.keys().copied()
141    }
142
143    pub fn instances(&self) -> &[Instance] {
144        &self.instances
145    }
146
147    pub fn ports(&self) -> &[Port] {
148        &self.ports
149    }
150
151    pub fn port(&self, name: &str) -> Option<&Port> {
152        self.ports.iter().find(|p| p.name == name)
153    }
154
155    pub fn properties(&self) -> &Properties {
156        &self.properties
157    }
158
159    /// Full bbox including transformed local bboxes of instance subtrees.
160    /// Walks the hierarchy via `lib`. Repetitions are accounted for.
161    pub fn full_bbox(&self, lib: &Library) -> Bbox {
162        let mut b = self.local_bbox;
163        for inst in &self.instances {
164            let child = lib.get(inst.cell);
165            let child_b = child.full_bbox(lib);
166            let placed = inst.trans.apply_bbox(child_b);
167            if let Some(rep) = &inst.repetition {
168                let rb = rep.placement_bbox();
169                if !rb.is_empty() {
170                    b = b.union(&Bbox::new(
171                        Point::new(placed.min.x + rb.min.x, placed.min.y + rb.min.y),
172                        Point::new(placed.max.x + rb.max.x, placed.max.y + rb.max.y),
173                    ));
174                    continue;
175                }
176            }
177            b = b.union(&placed);
178        }
179        b
180    }
181}
182
183#[derive(Clone, Debug)]
184pub struct CellBuilder {
185    name: CellName,
186    shapes: BTreeMap<LayerIndex, ShapeBag>,
187    instances: Vec<Instance>,
188    ports: Vec<Port>,
189    properties: Properties,
190}
191
192impl CellBuilder {
193    pub fn new(name: impl Into<CellName>) -> Self {
194        Self {
195            name: name.into(),
196            shapes: BTreeMap::new(),
197            instances: Vec::new(),
198            ports: Vec::new(),
199            properties: Properties::new(),
200        }
201    }
202
203    pub fn name(&self) -> &CellName {
204        &self.name
205    }
206
207    pub fn set_name(&mut self, name: impl Into<CellName>) {
208        self.name = name.into();
209    }
210
211    pub fn add_shape(&mut self, layer: LayerIndex, shape: impl Into<Shape>) {
212        self.shapes.entry(layer).or_default().push(shape.into());
213    }
214
215    pub fn add_instance(&mut self, inst: Instance) {
216        self.instances.push(inst);
217    }
218
219    pub fn instantiate(&mut self, cell: CellId, trans: Trans) {
220        self.instances.push(Instance::new(cell, trans));
221    }
222
223    pub fn add_port(&mut self, port: Port) {
224        self.ports.push(port);
225    }
226
227    pub fn set_property(&mut self, key: impl Into<SmolStr>, value: PropertyValue) {
228        self.properties.set(key, value);
229    }
230
231    pub fn properties_mut(&mut self) -> &mut Properties {
232        &mut self.properties
233    }
234
235    pub fn freeze(self, lib: &Library) -> Cell {
236        let local_bbox = compute_local_bbox(&self.shapes);
237        let content_hash = canonical_hash(
238            &self.name,
239            &self.shapes,
240            &self.instances,
241            &self.ports,
242            &self.properties,
243            lib,
244        );
245        Cell {
246            name: self.name,
247            shapes: self.shapes,
248            instances: self.instances,
249            ports: self.ports,
250            properties: self.properties,
251            local_bbox,
252            content_hash,
253        }
254    }
255}
256
257fn compute_local_bbox(shapes: &BTreeMap<LayerIndex, ShapeBag>) -> Bbox {
258    let mut b = Bbox::EMPTY;
259    for bag in shapes.values() {
260        for s in bag.iter() {
261            b = b.union(&s.bbox());
262        }
263    }
264    b
265}
266
267// ---------- canonical hashing ----------
268
269fn canonical_hash(
270    name: &CellName,
271    shapes: &BTreeMap<LayerIndex, ShapeBag>,
272    instances: &[Instance],
273    ports: &[Port],
274    properties: &Properties,
275    lib: &Library,
276) -> ContentHash {
277    let mut h = blake3::Hasher::new();
278
279    // The cell *name* is intentionally not hashed: two cells with the same
280    // geometry but different names should dedup. IO can preserve the name
281    // outside the hash.
282    let _ = name;
283
284    // Shapes: hash keyed by GDS (layer, datatype), NOT LayerIndex —
285    // LayerIndex is a per-library handle and depends on registration order,
286    // but the GDS pair is stable across libraries. This keeps content
287    // hashes invariant after a write/read round-trip.
288    let mut by_gds: Vec<((u16, u16), &ShapeBag)> = shapes
289        .iter()
290        .map(|(idx, bag)| (lib.layer_info(*idx).key(), bag))
291        .collect();
292    by_gds.sort_by_key(|(k, _)| *k);
293    write_u32(&mut h, by_gds.len() as u32);
294    for ((l, dt), bag) in by_gds {
295        write_u16(&mut h, l);
296        write_u16(&mut h, dt);
297        let mut encoded: Vec<Vec<u8>> = bag.iter().map(canonical_shape_bytes).collect();
298        encoded.sort();
299        write_u32(&mut h, encoded.len() as u32);
300        for e in &encoded {
301            write_bytes(&mut h, e);
302        }
303    }
304
305    // Instances: child content hash + canonical trans + repetition + props.
306    // Sort by the canonical encoding for determinism regardless of insertion order.
307    let mut inst_enc: Vec<Vec<u8>> = instances
308        .iter()
309        .map(|i| canonical_instance_bytes(i, lib))
310        .collect();
311    inst_enc.sort();
312    write_u32(&mut h, inst_enc.len() as u32);
313    for e in &inst_enc {
314        write_bytes(&mut h, e);
315    }
316
317    // Ports: sorted by name. Layer reference encoded via the stable GDS
318    // (layer, datatype) pair, not the per-library LayerIndex.
319    let mut port_enc: Vec<Vec<u8>> = ports
320        .iter()
321        .map(|p| canonical_port_bytes(p, lib))
322        .collect();
323    port_enc.sort();
324    write_u32(&mut h, port_enc.len() as u32);
325    for e in &port_enc {
326        write_bytes(&mut h, e);
327    }
328
329    // Properties: sorted by key.
330    let props_sorted = properties.sorted_iter();
331    write_u32(&mut h, props_sorted.len() as u32);
332    for (k, v) in props_sorted {
333        write_bytes(&mut h, k.as_bytes());
334        write_property(&mut h, v);
335    }
336
337    ContentHash(*h.finalize().as_bytes())
338}
339
340fn canonical_shape_bytes(s: &Shape) -> Vec<u8> {
341    let mut buf = Vec::with_capacity(64);
342    buf.push(s.discriminant());
343    match s {
344        Shape::Polygon(p) => {
345            put_u32(&mut buf, p.hull.len() as u32);
346            for pt in &p.hull {
347                put_point(&mut buf, *pt);
348            }
349            put_u32(&mut buf, p.holes.len() as u32);
350            for hole in &p.holes {
351                put_u32(&mut buf, hole.len() as u32);
352                for pt in hole {
353                    put_point(&mut buf, *pt);
354                }
355            }
356        }
357        Shape::Path(p) => {
358            put_u32(&mut buf, p.points.len() as u32);
359            for pt in &p.points {
360                put_point(&mut buf, *pt);
361            }
362            put_i64(&mut buf, p.width);
363            put_i64(&mut buf, p.begin_ext);
364            put_i64(&mut buf, p.end_ext);
365            buf.push(match p.cap {
366                crate::shape::PathCap::Flat => 0,
367                crate::shape::PathCap::Round => 1,
368                crate::shape::PathCap::Extended => 2,
369            });
370        }
371        Shape::Box(r) => {
372            put_point(&mut buf, r.bbox.min);
373            put_point(&mut buf, r.bbox.max);
374        }
375        Shape::Text(t) => {
376            put_u32(&mut buf, t.string.len() as u32);
377            buf.extend_from_slice(t.string.as_bytes());
378            put_point(&mut buf, t.anchor);
379            put_i32(&mut buf, t.size);
380            buf.push(t.halign as u8);
381            buf.push(t.valign as u8);
382        }
383    }
384    buf
385}
386
387fn canonical_trans_bytes(buf: &mut Vec<u8>, t: &Trans) {
388    buf.push(t.rot as u8);
389    buf.push(t.mirror as u8);
390    put_i64(buf, t.disp.x);
391    put_i64(buf, t.disp.y);
392}
393
394fn canonical_repetition_bytes(buf: &mut Vec<u8>, r: &Option<Repetition>) {
395    match r {
396        None => buf.push(0),
397        Some(Repetition::Regular {
398            row,
399            col,
400            n_rows,
401            n_cols,
402        }) => {
403            buf.push(1);
404            put_i64(buf, row.x);
405            put_i64(buf, row.y);
406            put_i64(buf, col.x);
407            put_i64(buf, col.y);
408            put_u32(buf, *n_rows);
409            put_u32(buf, *n_cols);
410        }
411        Some(Repetition::Irregular { offsets }) => {
412            buf.push(2);
413            put_u32(buf, offsets.len() as u32);
414            for o in offsets {
415                put_i64(buf, o.x);
416                put_i64(buf, o.y);
417            }
418        }
419    }
420}
421
422fn canonical_instance_bytes(i: &Instance, lib: &Library) -> Vec<u8> {
423    let mut buf = Vec::with_capacity(64);
424    let child = lib.get(i.cell);
425    buf.extend_from_slice(&child.content_hash.0);
426    canonical_trans_bytes(&mut buf, &i.trans);
427    canonical_repetition_bytes(&mut buf, &i.repetition);
428    let props = i.properties.sorted_iter();
429    put_u32(&mut buf, props.len() as u32);
430    for (k, v) in props {
431        put_u32(&mut buf, k.len() as u32);
432        buf.extend_from_slice(k.as_bytes());
433        canonical_property(&mut buf, v);
434    }
435    buf
436}
437
438fn canonical_port_bytes(p: &Port, lib: &Library) -> Vec<u8> {
439    let mut buf = Vec::with_capacity(48);
440    put_u32(&mut buf, p.name.len() as u32);
441    buf.extend_from_slice(p.name.as_bytes());
442    put_u32(&mut buf, p.kind.0);
443    put_point(&mut buf, p.center);
444    buf.push(p.angle as u8);
445    put_i64(&mut buf, p.width);
446    let info = lib.layer_info(p.layer);
447    put_u16(&mut buf, info.layer);
448    put_u16(&mut buf, info.datatype);
449    buf
450}
451
452fn canonical_property(buf: &mut Vec<u8>, v: &PropertyValue) {
453    match v {
454        PropertyValue::Int(i) => {
455            buf.push(0);
456            put_i64(buf, *i);
457        }
458        PropertyValue::Float(f) => {
459            buf.push(1);
460            buf.extend_from_slice(&f.to_bits().to_le_bytes());
461        }
462        PropertyValue::String(s) => {
463            buf.push(2);
464            put_u32(buf, s.len() as u32);
465            buf.extend_from_slice(s.as_bytes());
466        }
467        PropertyValue::Bytes(b) => {
468            buf.push(3);
469            put_u32(buf, b.len() as u32);
470            buf.extend_from_slice(b);
471        }
472        PropertyValue::List(items) => {
473            buf.push(4);
474            put_u32(buf, items.len() as u32);
475            for it in items {
476                canonical_property(buf, it);
477            }
478        }
479    }
480}
481
482fn write_property(h: &mut blake3::Hasher, v: &PropertyValue) {
483    let mut buf = Vec::new();
484    canonical_property(&mut buf, v);
485    write_bytes(h, &buf);
486}
487
488// ---- byte writers ----
489fn put_u16(buf: &mut Vec<u8>, v: u16) {
490    buf.extend_from_slice(&v.to_le_bytes());
491}
492fn put_u32(buf: &mut Vec<u8>, v: u32) {
493    buf.extend_from_slice(&v.to_le_bytes());
494}
495fn put_i32(buf: &mut Vec<u8>, v: i32) {
496    buf.extend_from_slice(&v.to_le_bytes());
497}
498fn put_i64(buf: &mut Vec<u8>, v: i64) {
499    buf.extend_from_slice(&v.to_le_bytes());
500}
501fn put_point(buf: &mut Vec<u8>, p: Point) {
502    put_i64(buf, p.x);
503    put_i64(buf, p.y);
504}
505
506fn write_u16(h: &mut blake3::Hasher, v: u16) {
507    h.update(&v.to_le_bytes());
508}
509fn write_u32(h: &mut blake3::Hasher, v: u32) {
510    h.update(&v.to_le_bytes());
511}
512fn write_bytes(h: &mut blake3::Hasher, b: &[u8]) {
513    h.update(&(b.len() as u32).to_le_bytes());
514    h.update(b);
515}