prjunnamed_netlist/
metadata.rs

1//! Metadata storage.
2//!
3//! This module is intentionally tightly coupled to `crate::design`, and intentionally avoids exporting any APIs that
4//! expose indices. The exported APIs are based on [`MetaStringRef`] and [`MetaItemRef`], which parallel [`CellRef`].
5//!
6//! [`CellRef`]: crate::CellRef
7use std::{
8    borrow::Cow,
9    cell::Ref,
10    collections::BTreeSet,
11    fmt::{Debug, Display},
12    hash::Hash,
13};
14use indexmap::IndexSet;
15
16use crate::{Design, ParamValue};
17
18/// Position within a source file.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct SourcePosition {
21    /// Zero-based line number.
22    pub line: u32,
23    /// Zero-based column number.
24    pub column: u32,
25}
26
27/// Metadata item.
28///
29/// Metadata items represent information about cells that does not affect computational semantics. Some metadata items
30/// only carry information about the source code, and other metadata items affect how the netlist is transformed.
31#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
32pub enum MetaItem<'a> {
33    /// Absence of a metadata item.
34    /// The purpose of this variant is to make metadata indices take less memory.
35    #[default]
36    None,
37    /// Multiple metadata items.
38    /// The purpose of this variant is to make collections of metadata indices take less memory.
39    /// A metadata set cannot contain less than two metadata items.
40    Set(BTreeSet<MetaItemRef<'a>>),
41    /// Source location.
42    Source {
43        /// Filename.  Must not be empty.
44        file: MetaStringRef<'a>,
45        /// Start of the range (inclusive).
46        start: SourcePosition,
47        /// End of the range (exclusive).
48        end: SourcePosition,
49    },
50    /// Scope identified by a name.
51    /// A top-level named scope could be a module declaration. A named scope with a parent could be a block within
52    /// a module.
53    NamedScope {
54        /// Name.  Must not be empty.
55        name: MetaStringRef<'a>,
56        /// Source location.  Must reference [`MetaItem::None`] or [`MetaItem::Source`].
57        source: MetaItemRef<'a>,
58        /// Parent scope.  Must reference [`MetaItem::None`], [`MetaItem::NamedScope`], or [`MetaItem::IndexedScope`].
59        parent: MetaItemRef<'a>,
60    },
61    /// Scope identified by an index.
62    /// A top-level named scope could be a module declaration. A named scope with a parent could be a named instance
63    /// of a module within another module.
64    IndexedScope {
65        index: i32,
66        /// Source location.  Must reference [`MetaItem::None`] or [`MetaItem::Source`].
67        source: MetaItemRef<'a>,
68        /// Parent scope.  Must reference [`MetaItem::None`], [`MetaItem::NamedScope`], or [`MetaItem::IndexedScope`].
69        parent: MetaItemRef<'a>,
70    },
71    /// Identifier within source code.
72    Ident {
73        /// Name.  Must not be empty.
74        name: MetaStringRef<'a>,
75        /// Containing scope.
76        /// Must reference a [`MetaItem::NamedScope`], or [`MetaItem::IndexedScope`].
77        scope: MetaItemRef<'a>,
78    },
79    Attr {
80        /// Name.  Must not be empty.
81        name: MetaStringRef<'a>,
82        /// Value.
83        value: ParamValue,
84    },
85    // to be added:
86    // - MemoryKind
87    // - EnumEncoding
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
91pub(crate) struct MetaStringIndex(usize);
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
94pub(crate) struct MetaItemIndex(usize);
95
96#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
97enum MetaItemRepr {
98    #[default]
99    None,
100    Set(Vec<MetaItemIndex>),
101    Source {
102        file: MetaStringIndex,
103        start: SourcePosition,
104        end: SourcePosition,
105    },
106    NamedScope {
107        name: MetaStringIndex,
108        source: MetaItemIndex,
109        parent: MetaItemIndex,
110    },
111    IndexedScope {
112        index: i32,
113        source: MetaItemIndex,
114        parent: MetaItemIndex,
115    },
116    Ident {
117        name: MetaStringIndex,
118        scope: MetaItemIndex,
119    },
120    Attr {
121        name: MetaStringIndex,
122        value: ParamValue,
123    },
124}
125
126#[derive(Clone, Debug)]
127pub struct MetadataStore {
128    strings: IndexSet<String>,
129    items: IndexSet<MetaItemRepr>,
130}
131
132#[derive(Clone, Copy)]
133pub struct MetaStringRef<'a> {
134    design: &'a Design,
135    index: MetaStringIndex,
136}
137
138#[derive(Clone, Copy)]
139pub struct MetaItemRef<'a> {
140    design: &'a Design,
141    index: MetaItemIndex,
142}
143
144pub struct MetaItemIterator<'a> {
145    item: MetaItemRef<'a>,
146    offset: usize,
147}
148
149impl MetaItem<'_> {
150    pub fn validate(&self) {
151        match self {
152            MetaItem::None => (),
153            MetaItem::Set(items) => {
154                assert!(items.len() > 1, "MetaItem::Set must contain more than one element");
155                for item in items {
156                    assert!(
157                        !matches!(&*item.get_repr(), MetaItemRepr::None | MetaItemRepr::Set(..)),
158                        "MetaItem::Set item must not be MetaItem::None or another MetaItem::Set"
159                    )
160                }
161            }
162            MetaItem::Source { file, start, end } => {
163                assert!(!file.get().is_empty(), "MetaItem::Source must have a file");
164                assert!(
165                    end.line > start.line || (end.line == start.line && end.column >= start.column),
166                    "MetaItem::Source must specify a range in non-descending order"
167                );
168            }
169            MetaItem::NamedScope { name: _, source, parent } | MetaItem::IndexedScope { index: _, source, parent } => {
170                if let MetaItem::NamedScope { name, .. } = self {
171                    assert!(!name.get().is_empty(), "MetaItem::NamedScope must have a name");
172                }
173                assert!(
174                    matches!(&*source.get_repr(), MetaItemRepr::None | MetaItemRepr::Source { .. }),
175                    concat!(
176                        "source of a MetaItem::NamedScope or MetaItem::IndexedScope must be ",
177                        "MetaItem::None or MetaItem::Source"
178                    )
179                );
180                assert!(
181                    matches!(
182                        &*parent.get_repr(),
183                        MetaItemRepr::None | MetaItemRepr::NamedScope { .. } | MetaItemRepr::IndexedScope { .. }
184                    ),
185                    concat!(
186                        "parent of a MetaItem::NamedScope or MetaItem::IndexedScope must be ",
187                        "MetaItem::None, MetaItem::NamedScope, or MetaItem::IndexedScope"
188                    )
189                );
190            }
191            MetaItem::Ident { name, scope } => {
192                assert!(!name.get().is_empty(), "MetaItem::Ident must have a name");
193                assert!(
194                    matches!(&*scope.get_repr(), MetaItemRepr::NamedScope { .. } | MetaItemRepr::IndexedScope { .. }),
195                    "scope of a MetaItem::Ident must be MetaItem::NamedScope or MetaItem::IndexedScope"
196                );
197            }
198            MetaItem::Attr { name, value: _ } => {
199                assert!(!name.get().is_empty(), "MetaItem::Attr must have a name");
200            }
201        }
202    }
203}
204
205impl MetaStringIndex {
206    pub const EMPTY: MetaStringIndex = MetaStringIndex(0);
207}
208
209impl MetaItemIndex {
210    pub const NONE: MetaItemIndex = MetaItemIndex(0);
211}
212
213impl MetadataStore {
214    pub(crate) fn new() -> Self {
215        Self { strings: IndexSet::from(["".to_owned()]), items: IndexSet::from([MetaItemRepr::None]) }
216    }
217
218    pub(crate) fn add_string<'a>(&mut self, string: impl Into<Cow<'a, str>>) -> MetaStringIndex {
219        let string = string.into();
220        match self.strings.get_index_of(string.as_ref()) {
221            Some(index) => MetaStringIndex(index),
222            None => MetaStringIndex(self.strings.insert_full(string.into()).0),
223        }
224    }
225
226    pub(crate) fn ref_string<'a>(&self, design: &'a Design, index: MetaStringIndex) -> MetaStringRef<'a> {
227        MetaStringRef { design, index }
228    }
229
230    pub(crate) fn add_item(&mut self, item: &MetaItem) -> MetaItemIndex {
231        // The item must be validated before adding; however it cannot be done here because `.validate()` would need
232        // to reborrow the store.
233        let repr = match item {
234            MetaItem::None => MetaItemRepr::None,
235            MetaItem::Set(items) => MetaItemRepr::Set(items.iter().map(|item| item.index).collect()),
236            MetaItem::Source { file, start, end } => {
237                MetaItemRepr::Source { file: file.index, start: *start, end: *end }
238            }
239            MetaItem::NamedScope { name, source, parent } => {
240                MetaItemRepr::NamedScope { parent: parent.index, source: source.index, name: name.index }
241            }
242            MetaItem::IndexedScope { index, source, parent } => {
243                MetaItemRepr::IndexedScope { parent: parent.index, source: source.index, index: *index }
244            }
245            MetaItem::Ident { name, scope } => MetaItemRepr::Ident { scope: scope.index, name: name.index },
246            MetaItem::Attr { name, value } => MetaItemRepr::Attr { name: name.index, value: value.clone() },
247        };
248        MetaItemIndex(self.items.insert_full(repr).0)
249    }
250
251    pub(crate) fn ref_item<'a>(&self, design: &'a Design, index: MetaItemIndex) -> MetaItemRef<'a> {
252        MetaItemRef { design, index }
253    }
254
255    pub(crate) fn iter_items<'a>(&self, design: &'a Design) -> impl Iterator<Item = MetaItemRef<'a>> + use<'a> {
256        (0..self.items.len()).map(|index| MetaItemRef { design, index: MetaItemIndex(index) })
257    }
258}
259
260impl<'a> MetaStringRef<'a> {
261    pub(crate) fn index(&self) -> MetaStringIndex {
262        self.index
263    }
264
265    pub fn is_empty(&self) -> bool {
266        self.index == MetaStringIndex::EMPTY
267    }
268
269    pub fn get(&self) -> Ref<'a, str> {
270        Ref::map(self.design.metadata(), |store| {
271            store.strings.get_index(self.index.0).expect("invalid metadata string reference").as_str()
272        })
273    }
274}
275
276impl<'a> MetaItemRef<'a> {
277    pub(crate) fn index(&self) -> MetaItemIndex {
278        self.index
279    }
280
281    pub fn is_none(&self) -> bool {
282        self.index == MetaItemIndex::NONE
283    }
284
285    fn get_repr(&self) -> Ref<'a, MetaItemRepr> {
286        Ref::map(self.design.metadata(), |store| {
287            store.items.get_index(self.index.0).expect("invalid metadata item reference")
288        })
289    }
290
291    pub fn get(&self) -> MetaItem<'a> {
292        let design = self.design;
293        match &*self.get_repr() {
294            MetaItemRepr::None => MetaItem::None,
295            MetaItemRepr::Set(items) => {
296                MetaItem::Set(items.iter().map(|&index| MetaItemRef { index, design }).collect())
297            }
298            MetaItemRepr::Source { file: filename, start, end } => {
299                MetaItem::Source { file: MetaStringRef { index: *filename, design }, start: *start, end: *end }
300            }
301            MetaItemRepr::NamedScope { name, source, parent } => MetaItem::NamedScope {
302                name: MetaStringRef { index: *name, design },
303                source: MetaItemRef { index: *source, design },
304                parent: MetaItemRef { index: *parent, design },
305            },
306            MetaItemRepr::IndexedScope { index, source, parent } => MetaItem::IndexedScope {
307                index: *index,
308                source: MetaItemRef { index: *source, design },
309                parent: MetaItemRef { index: *parent, design },
310            },
311            MetaItemRepr::Ident { name, scope } => MetaItem::Ident {
312                name: MetaStringRef { index: *name, design },
313                scope: MetaItemRef { index: *scope, design },
314            },
315            MetaItemRepr::Attr { name, value } => {
316                MetaItem::Attr { name: MetaStringRef { index: *name, design }, value: value.clone() }
317            }
318        }
319    }
320
321    pub fn iter(&self) -> impl Iterator<Item = MetaItemRef<'a>> + use<'a> {
322        MetaItemIterator { item: *self, offset: 0 }
323    }
324
325    pub fn from_iter(design: &'a Design, iter: impl IntoIterator<Item = MetaItemRef<'a>>) -> Self {
326        let items = BTreeSet::from_iter(iter);
327        if items.len() == 0 {
328            MetaItemRef { design, index: MetaItemIndex::NONE }
329        } else if items.len() == 1 {
330            *items.first().unwrap()
331        } else {
332            MetaItemRef { design, index: design.add_metadata_item(&MetaItem::Set(items)).index() }
333        }
334    }
335
336    pub fn from_merge(design: &'a Design, iter: impl IntoIterator<Item = MetaItemRef<'a>>) -> Self {
337        Self::from_iter(
338            design,
339            iter.into_iter().flat_map(|item| item.iter()).filter(|item| match &*item.get_repr() {
340                MetaItemRepr::Source { .. }
341                | MetaItemRepr::NamedScope { .. }
342                | MetaItemRepr::IndexedScope { .. }
343                | MetaItemRepr::Ident { .. } => true,
344                _ => false,
345            }),
346        )
347    }
348
349    pub fn merge(&self, other: MetaItemRef<'a>) -> Self {
350        Self::from_merge(&self.design, [*self, other])
351    }
352}
353
354impl<'a> Iterator for MetaItemIterator<'a> {
355    type Item = MetaItemRef<'a>;
356
357    fn next(&mut self) -> Option<Self::Item> {
358        let repr = self.item.get_repr();
359        let slice = match &*repr {
360            MetaItemRepr::None => &[][..],
361            MetaItemRepr::Set(items) => &items[..],
362            _ => std::slice::from_ref(&self.item.index),
363        };
364        if self.offset < slice.len() {
365            let item = MetaItemRef { design: self.item.design, index: slice[self.offset] };
366            self.offset += 1;
367            Some(item)
368        } else {
369            None
370        }
371    }
372}
373
374impl Display for MetaItemIndex {
375    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376        let index = self.0;
377        assert!(index > 0);
378        write!(f, "!{}", index - 1)
379    }
380}
381
382impl Debug for MetaStringRef<'_> {
383    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384        write!(f, "{:?}", self.get())
385    }
386}
387
388impl PartialEq<MetaStringRef<'_>> for MetaStringRef<'_> {
389    fn eq(&self, other: &MetaStringRef<'_>) -> bool {
390        std::ptr::eq(self.design, other.design) && self.index == other.index
391    }
392}
393
394impl Eq for MetaStringRef<'_> {}
395
396impl PartialOrd<MetaStringRef<'_>> for MetaStringRef<'_> {
397    fn partial_cmp(&self, other: &MetaStringRef<'_>) -> Option<std::cmp::Ordering> {
398        Some(self.cmp(other))
399    }
400}
401
402impl Ord for MetaStringRef<'_> {
403    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
404        match (self.design as *const Design).cmp(&(other.design as *const Design)) {
405            core::cmp::Ordering::Equal => self.index.cmp(&other.index),
406            ord => ord,
407        }
408    }
409}
410
411impl Hash for MetaStringRef<'_> {
412    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
413        self.index.hash(state);
414    }
415}
416
417impl Debug for MetaItemRef<'_> {
418    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419        write!(f, "{:?}", self.get())
420    }
421}
422
423impl PartialEq<MetaItemRef<'_>> for MetaItemRef<'_> {
424    fn eq(&self, other: &MetaItemRef<'_>) -> bool {
425        std::ptr::eq(self.design, other.design) && self.index == other.index
426    }
427}
428
429impl Eq for MetaItemRef<'_> {}
430
431impl PartialOrd<MetaItemRef<'_>> for MetaItemRef<'_> {
432    fn partial_cmp(&self, other: &MetaItemRef<'_>) -> Option<std::cmp::Ordering> {
433        Some(self.cmp(other))
434    }
435}
436
437impl Ord for MetaItemRef<'_> {
438    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
439        match (self.design as *const Design).cmp(&(other.design as *const Design)) {
440            core::cmp::Ordering::Equal => self.index.cmp(&other.index),
441            ord => ord,
442        }
443    }
444}
445
446impl Hash for MetaItemRef<'_> {
447    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
448        self.index.hash(state);
449    }
450}