Skip to main content

lutra_bin/
tabular.rs

1#![cfg(feature = "std")]
2
3//! Low-level tabular data reader.
4
5use std::collections::HashMap;
6use std::rc::Rc;
7
8use crate::TupleReader;
9use crate::ir;
10
11use crate::string::ToString;
12use crate::vec;
13
14/// Utility for iterating over arbitrary data in tabular manner (as rows and columns).
15#[derive(Clone)]
16pub struct TabularReader<'d, 't> {
17    inner: TableCell<'d, 't>,
18
19    rem_items: usize,
20    array_item_size: usize,
21
22    types: Rc<HashMap<&'t ir::Path, &'t ir::Ty>>,
23}
24
25#[derive(Clone, Copy)]
26pub struct TableCell<'d, 't> {
27    data: &'d [u8],
28    ty: &'t ir::Ty,
29    ty_defs: &'t [ir::TyDef],
30}
31
32impl<'d, 't> TableCell<'d, 't> {
33    pub fn new(data: &'d [u8], ty: &'t ir::Ty, ty_defs: &'t [ir::TyDef]) -> Self {
34        Self { data, ty, ty_defs }
35    }
36
37    pub fn data(&self) -> &'d [u8] {
38        self.data
39    }
40
41    pub fn ty(&self) -> &'t ir::Ty {
42        self.ty
43    }
44
45    pub fn ty_defs(&self) -> &'t [ir::TyDef] {
46        self.ty_defs
47    }
48}
49
50impl<'d, 't> TabularReader<'d, 't> {
51    pub fn new(data: &'d [u8], ty: &'t ir::Ty, ty_defs: &'t [ir::TyDef]) -> Self {
52        let mut r = TabularReader {
53            inner: TableCell { data, ty, ty_defs },
54            rem_items: 0,
55            array_item_size: 0,
56            types: Rc::new(HashMap::from_iter(ty_defs.iter().map(|d| (&d.name, &d.ty)))),
57        };
58
59        match &r.get_ty_mat(ty).kind {
60            ir::TyKind::Primitive(_) | ir::TyKind::Tuple(_) | ir::TyKind::Enum(_) => {
61                r.rem_items = 1;
62            }
63            ir::TyKind::Array(item) => {
64                let (offset, len) =
65                    crate::ArrayReader::<&[u8]>::read_head(TableCell { data, ty, ty_defs }.data);
66                r.inner.data = &TableCell { data, ty, ty_defs }.data[offset..];
67                r.rem_items = len;
68                r.array_item_size = item.layout.as_ref().unwrap().head_size.div_ceil(8) as usize;
69            }
70            ir::TyKind::Function(_) | ir::TyKind::Ident(_) => unreachable!(),
71        }
72        r
73    }
74
75    pub(crate) fn ty(&self) -> &'t ir::Ty {
76        self.inner.ty()
77    }
78
79    pub(super) fn get_ty_mat(&self, ty: &'t ir::Ty) -> &'t ir::Ty {
80        let mut ty = ty;
81        while let ir::TyKind::Ident(path) = &ty.kind {
82            ty = self.types.get(path).unwrap();
83        }
84        ty
85    }
86
87    pub fn remaining(&self) -> usize {
88        self.rem_items
89    }
90
91    pub fn column_names(&self) -> Vec<String> {
92        self.column_names_of_ty(self.inner.ty)
93    }
94
95    fn column_names_of_ty(&self, ty: &ir::Ty) -> Vec<String> {
96        match &self.get_ty_mat(ty).kind {
97            // arrays are iterated over, columns come from inner type
98            ir::TyKind::Array(item) => self.column_names_of_ty(item),
99
100            // tuple fields become columns
101            ir::TyKind::Tuple(fields) => fields
102                .iter()
103                .enumerate()
104                .map(|(i, f)| {
105                    if let Some(name) = &f.name {
106                        name.clone()
107                    } else {
108                        i.to_string()
109                    }
110                })
111                .collect(),
112
113            // primitives and enums become a single column (we also infer name from ident)
114            ir::TyKind::Primitive(_) | ir::TyKind::Enum(_) => {
115                if let ir::TyKind::Ident(path) = &ty.kind {
116                    vec![path.0.last().unwrap().clone()]
117                } else {
118                    vec!["value".into()]
119                }
120            }
121            ir::TyKind::Ident(_) | ir::TyKind::Function(_) => unreachable!(),
122        }
123    }
124}
125
126impl<'d, 't> Iterator for TabularReader<'d, 't> {
127    type Item = vec::Vec<TableCell<'d, 't>>;
128
129    fn next(&mut self) -> Option<Self::Item> {
130        if self.rem_items == 0 {
131            return None;
132        }
133        let mut row = self.inner;
134        if let ir::TyKind::Array(item) = &row.ty.kind {
135            row.ty = item.as_ref();
136        }
137
138        // advance
139        self.rem_items -= 1;
140        if let ir::TyKind::Array(_) = &self.inner.ty.kind {
141            self.inner.data = &self.inner.data[self.array_item_size..];
142        }
143
144        // unpack row
145        let row_ty_mat = self.get_ty_mat(row.ty);
146        Some(match &row_ty_mat.kind {
147            ir::TyKind::Primitive(_) | ir::TyKind::Array(_) | ir::TyKind::Enum(_) => {
148                vec![row]
149            }
150            ir::TyKind::Tuple(fields) => {
151                let mut cells = Vec::with_capacity(fields.len());
152                let reader = TupleReader::new_for_ty(row.data, row_ty_mat);
153                for (i, f) in fields.iter().enumerate() {
154                    cells.push(TableCell {
155                        data: reader.get_field(i),
156                        ty: &f.ty,
157                        ty_defs: row.ty_defs,
158                    })
159                }
160                cells
161            }
162            ir::TyKind::Function(_) | ir::TyKind::Ident(_) => unreachable!(),
163        })
164    }
165}