vortex_array/
normalize.rs1use smallvec::SmallVec;
5use vortex_error::VortexResult;
6use vortex_error::vortex_bail;
7use vortex_session::registry::Id;
8use vortex_utils::aliases::hash_set::HashSet;
9
10use crate::ArrayRef;
11use crate::ExecutionCtx;
12
13pub struct NormalizeOptions<'a> {
15 pub allowed: &'a HashSet<Id>,
18 pub operation: Operation<'a>,
20}
21
22pub enum Operation<'a> {
24 Error,
25 Execute(&'a mut ExecutionCtx),
26}
27
28impl ArrayRef {
29 pub fn normalize(self, options: &mut NormalizeOptions) -> VortexResult<ArrayRef> {
34 match &mut options.operation {
35 Operation::Error => {
36 self.normalize_with_error(options.allowed)?;
37 Ok(self)
39 }
40 Operation::Execute(ctx) => self.normalize_with_execution(options.allowed, ctx),
41 }
42 }
43
44 fn normalize_with_error(&self, allowed: &HashSet<Id>) -> VortexResult<()> {
45 if !self.is_allowed_encoding(allowed) {
46 vortex_bail!(AssertionFailed: "normalize forbids encoding ({})", self.encoding_id())
47 }
48
49 for child in self.children() {
50 child.normalize_with_error(allowed)?
51 }
52 Ok(())
53 }
54
55 fn normalize_with_execution(
56 self,
57 allowed: &HashSet<Id>,
58 ctx: &mut ExecutionCtx,
59 ) -> VortexResult<ArrayRef> {
60 let mut normalized = self;
61
62 while !normalized.is_allowed_encoding(allowed) {
64 normalized = normalized.execute(ctx)?;
65 }
66
67 let slots = normalized.slots();
69 let mut normalized_slots = SmallVec::with_capacity(slots.len());
70 let mut any_slot_changed = false;
71
72 for slot in slots {
73 match slot {
74 Some(child) => {
75 let normalized_child = child.clone().normalize(&mut NormalizeOptions {
76 allowed,
77 operation: Operation::Execute(ctx),
78 })?;
79 any_slot_changed |= !ArrayRef::ptr_eq(child, &normalized_child);
80 normalized_slots.push(Some(normalized_child));
81 }
82 None => normalized_slots.push(None),
83 }
84 }
85
86 if any_slot_changed {
87 normalized = unsafe { normalized.with_slots(normalized_slots) }?;
90 }
91
92 Ok(normalized)
93 }
94
95 fn is_allowed_encoding(&self, allowed: &HashSet<Id>) -> bool {
96 allowed.contains(&self.encoding_id()) || self.is_canonical()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use vortex_error::VortexResult;
103 use vortex_utils::aliases::hash_set::HashSet;
104
105 use super::NormalizeOptions;
106 use super::Operation;
107 use crate::ArrayRef;
108 use crate::Canonical;
109 use crate::IntoArray;
110 use crate::VortexSessionExecute;
111 use crate::array::VTable;
112 use crate::arrays::Dict;
113 use crate::arrays::DictArray;
114 use crate::arrays::Primitive;
115 use crate::arrays::PrimitiveArray;
116 use crate::arrays::Slice;
117 use crate::arrays::SliceArray;
118 use crate::arrays::StructArray;
119 use crate::assert_arrays_eq;
120 use crate::validity::Validity;
121
122 #[test]
123 fn normalize_with_execution_keeps_parent_when_children_are_unchanged() -> VortexResult<()> {
124 let field = PrimitiveArray::from_iter(0i32..4).into_array();
125 let array = StructArray::try_new(
126 ["field"].into(),
127 vec![field.clone()],
128 field.len(),
129 Validity::NonNullable,
130 )?
131 .into_array();
132 let allowed = HashSet::from_iter([array.encoding_id(), field.encoding_id()]);
133 let mut ctx = crate::array_session().create_execution_ctx();
134
135 let normalized = array.clone().normalize(&mut NormalizeOptions {
136 allowed: &allowed,
137 operation: Operation::Execute(&mut ctx),
138 })?;
139
140 assert!(ArrayRef::ptr_eq(&array, &normalized));
141 Ok(())
142 }
143
144 #[test]
145 fn normalize_with_error_allows_canonical_arrays() -> VortexResult<()> {
146 let field = PrimitiveArray::from_iter(0i32..4).into_array();
147 let array = StructArray::try_new(
148 ["field"].into(),
149 vec![field.clone()],
150 field.len(),
151 Validity::NonNullable,
152 )?
153 .into_array();
154 let allowed = HashSet::default();
155
156 let normalized = array.clone().normalize(&mut NormalizeOptions {
157 allowed: &allowed,
158 operation: Operation::Error,
159 })?;
160
161 assert!(ArrayRef::ptr_eq(&array, &normalized));
162 Ok(())
163 }
164
165 #[test]
166 fn normalize_with_execution_rebuilds_parent_when_a_child_changes() -> VortexResult<()> {
167 let unchanged = PrimitiveArray::from_iter(0i32..4).into_array();
168 let sliced =
169 SliceArray::new(PrimitiveArray::from_iter(10i32..20).into_array(), 2..6).into_array();
170 let array = StructArray::try_new(
171 ["lhs", "rhs"].into(),
172 vec![unchanged.clone(), sliced],
173 unchanged.len(),
174 Validity::NonNullable,
175 )?
176 .into_array();
177 let allowed = HashSet::from_iter([array.encoding_id(), unchanged.encoding_id()]);
178 let mut ctx = crate::array_session().create_execution_ctx();
179
180 let normalized = array.clone().normalize(&mut NormalizeOptions {
181 allowed: &allowed,
182 operation: Operation::Execute(&mut ctx),
183 })?;
184
185 assert!(!ArrayRef::ptr_eq(&array, &normalized));
186
187 let original_children = array.children();
188 let normalized_children = normalized.children();
189 assert!(ArrayRef::ptr_eq(
190 &original_children[0],
191 &normalized_children[0]
192 ));
193 assert!(!ArrayRef::ptr_eq(
194 &original_children[1],
195 &normalized_children[1]
196 ));
197 assert!(normalized_children[1].is::<Primitive>());
198 assert_arrays_eq!(
199 normalized,
200 StructArray::try_from_iter([
201 ("lhs", PrimitiveArray::from_iter(0i32..4).into_array()),
202 ("rhs", PrimitiveArray::from_iter(12i32..16).into_array()),
203 ])?,
204 &mut ctx
205 );
206
207 Ok(())
208 }
209
210 #[test]
211 fn normalize_slice_of_dict_returns_dict() -> VortexResult<()> {
212 let codes = PrimitiveArray::from_iter(vec![0u32, 1, 0, 1, 2]).into_array();
213 let values = PrimitiveArray::from_iter(vec![10i32, 20, 30]).into_array();
214 let dict = DictArray::try_new(codes, values)?.into_array();
215
216 let sliced = SliceArray::new(dict, 1..4).into_array();
218 assert_eq!(sliced.encoding_id(), Slice.id());
219
220 let allowed = HashSet::from_iter([Dict.id(), Primitive.id()]);
221 let mut ctx = crate::array_session().create_execution_ctx();
222
223 let normalized = sliced.normalize(&mut NormalizeOptions {
224 allowed: &allowed,
225 operation: Operation::Execute(&mut ctx),
226 })?;
227
228 assert_eq!(normalized.encoding_id(), Dict.id());
230 assert_eq!(normalized.len(), 3);
231
232 let normalized_canonical = normalized.execute::<Canonical>(&mut ctx)?;
234 assert_arrays_eq!(
235 normalized_canonical,
236 PrimitiveArray::from_iter(vec![20i32, 10, 20]),
237 &mut ctx
238 );
239
240 Ok(())
241 }
242}