1use alloc::{vec::Vec};
2use crate::provided::{DslError, Tree};
3use crate::list::{List, VariableList};
4
5pub const META_GET: &[u8] = b"_get";
8pub const META_SET: &[u8] = b"_set";
9pub const META_STATE: &[u8] = b"_state";
10
11pub const PROP_STORE: &[u8] = b"store";
14pub const PROP_KEY: &[u8] = b"key";
15pub const PROP_MAP: &[u8] = b"map";
16
17pub const PATH_IS_LEAF_SHIFT: u64 = 63;
28pub const PATH_KEYWORD_ID_SHIFT: u64 = 48;
29pub const PATH_PARENT_ID_SHIFT: u64 = 32;
30pub const PATH_CHILD_ID_SHIFT: u64 = 16;
31pub const PATH_VALUE_ID_SHIFT: u64 = 0;
32
33pub const PATH_IS_LEAF_MASK: u64 = 0x1 << PATH_IS_LEAF_SHIFT;
34pub const PATH_KEYWORD_ID_MASK: u64 = 0x7fff << PATH_KEYWORD_ID_SHIFT;
35pub const PATH_PARENT_ID_MASK: u64 = 0xffff << PATH_PARENT_ID_SHIFT;
36pub const PATH_CHILD_ID_MASK: u64 = 0xffff << PATH_CHILD_ID_SHIFT;
37pub const PATH_VALUE_ID_MASK: u64 = 0xffff;
38
39pub const LEAF_WIDTH: usize = 12;
57
58pub const LEAF_GET_STORE_ID: usize = 0;
59pub const LEAF_GET_KEY_ID: usize = 1;
60pub const LEAF_SET_STORE_ID: usize = 2;
61pub const LEAF_SET_KEY_ID: usize = 3;
62pub const LEAF_GET_MAP_KEY_ID: usize = 4;
63pub const LEAF_GET_MAP_VAL_ID: usize = 5;
64pub const LEAF_GET_ARGS_KEY_ID: usize = 6;
65pub const LEAF_GET_ARGS_VAL_ID: usize = 7;
66pub const LEAF_SET_MAP_KEY_ID: usize = 8;
67pub const LEAF_SET_MAP_VAL_ID: usize = 9;
68pub const LEAF_SET_ARGS_KEY_ID: usize = 10;
69pub const LEAF_SET_ARGS_VAL_ID: usize = 11;
70
71pub const VALUE_IS_PLACEHOLDER_MASK: u16 = 0x8000;
76pub const VALUE_WORD_ID_MASK: u16 = 0x7fff;
77
78pub struct Dsl;
81
82const MAX_PATH_ID: usize = 0xffff; const MAX_WORD_ID: usize = 0x7fff; const MAX_STORE_ID: usize = 0xff; const MAX_VL_U16_DATA: usize = 0xffff; const MAX_VL_U8_DATA: usize = 0xffff; impl Dsl {
92 pub fn compile(tree: &Tree, store_ids: &[&str]) -> Result<(
109 List<u64>,
110 VariableList<u16>,
111 VariableList<u16>,
112 VariableList<u16>,
113 VariableList<u8>,
114 VariableList<u16>,
115 VariableList<u16>,
116 VariableList<u16>,
117 VariableList<u16>,
118 ), DslError> {
119 if store_ids.len() > MAX_STORE_ID {
120 return Err(DslError::LimitExceeded(alloc::format!(
121 "store_ids length {} exceeds max {}", store_ids.len(), MAX_STORE_ID
122 )));
123 }
124 let mut compiler = Compiler::new(store_ids);
125 compiler.intern_word(b"")?; if let Tree::Mapping(pairs) = tree {
129 let field_pairs: Vec<_> = pairs.iter()
130 .filter(|(k, _)| k.first() != Some(&b'_'))
131 .collect();
132
133 let children_id = compiler.alloc_children_slots(field_pairs.len())?;
134
135 for (i, (k, v)) in field_pairs.iter().enumerate() {
136 let child_id = compiler.paths.data.len() as u16;
137 compiler.set_child(children_id, i, child_id);
138 compiler.walk_field_key(k, v, 0, None, None)?;
139 }
140
141 let root = (children_id as u64) << PATH_CHILD_ID_SHIFT;
142 compiler.paths.data[0] = root;
143 }
144
145 compiler.resolve_map_dst()?;
146 compiler.finish()
147 }
148
149 #[cfg(feature = "precompile")]
151 pub fn write(src: &[u8], store_ids: &[&str], out_path: &str) -> Result<(), alloc::string::String> {
152 extern crate std;
153 use std::string::{String, ToString};
154 use std::format;
155
156 let tree = parse_yaml(src)?;
157 let (paths, children, leaves, values, words, map_keys, map_vals, args_keys, args_vals)
158 = Self::compile(&tree, store_ids).map_err(|e| e.to_string())?;
159
160 check_vl_u16_identity(&children, "children")?;
162 check_vl_u16_identity(&leaves, "leaves")?;
163 check_vl_u16_identity(&values, "values")?;
164 check_vl_u16_identity_u8(&words, "words")?;
165 check_vl_u16_identity(&map_keys, "map_keys")?;
166 check_vl_u16_identity(&map_vals, "map_vals")?;
167 check_vl_u16_identity(&args_keys, "args_keys")?;
168 check_vl_u16_identity(&args_vals, "args_vals")?;
169
170 let mut out = String::new();
171 out.push_str("// @generated — do not edit by hand\n\n");
172 emit_u64_slice(&mut out, "PATHS", &paths.data);
173 emit_u16_slice(&mut out, "CHILDREN_IDENTITY", &children.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
174 emit_u16_slice(&mut out, "CHILDREN_DATA", &children.data);
175 emit_u16_slice(&mut out, "LEAVES_IDENTITY", &leaves.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
176 emit_u16_slice(&mut out, "LEAVES_DATA", &leaves.data);
177 emit_u16_slice(&mut out, "VALUES_IDENTITY", &values.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
178 emit_u16_slice(&mut out, "VALUES_DATA", &values.data);
179 emit_u16_slice(&mut out, "WORDS_IDENTITY", &words.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
180 emit_u8_slice (&mut out, "WORDS_DATA", &words.data);
181 emit_u16_slice(&mut out, "MAP_KEYS_IDENTITY", &map_keys.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
182 emit_u16_slice(&mut out, "MAP_KEYS_DATA", &map_keys.data);
183 emit_u16_slice(&mut out, "MAP_VALS_IDENTITY", &map_vals.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
184 emit_u16_slice(&mut out, "MAP_VALS_DATA", &map_vals.data);
185 emit_u16_slice(&mut out, "ARGS_KEYS_IDENTITY", &args_keys.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
186 emit_u16_slice(&mut out, "ARGS_KEYS_DATA", &args_keys.data);
187 emit_u16_slice(&mut out, "ARGS_VALS_IDENTITY", &args_vals.identity.iter().map(|&x| x as u16).collect::<alloc::vec::Vec<_>>());
188 emit_u16_slice(&mut out, "ARGS_VALS_DATA", &args_vals.data);
189
190 std::fs::write(out_path, out)
191 .map_err(|e| format!("write error: {e}"))
192 }
193}
194
195#[derive(Clone)]
198struct MetaBlock {
199 store_id: u8,
200 defined_at: u16, key_value: Vec<u16>, map_entries: Vec<(u16, u16)>, arg_entries: Vec<(u16, Vec<u16>)>, }
205
206struct Compiler<'s> {
209 store_ids: &'s [&'s str],
210 paths: List<u64>,
211 children: VariableList<u16>,
212 leaves: VariableList<u16>,
213 values: VariableList<u16>,
214 words: VariableList<u8>,
215 map_keys: VariableList<u16>,
216 map_vals: VariableList<u16>,
217 args_keys: VariableList<u16>,
218 args_vals: VariableList<u16>,
219 map_dst_pending: Vec<(u16, u16)>,
221}
222
223impl<'s> Compiler<'s> {
224 fn new(store_ids: &'s [&'s str]) -> Self {
225 Self {
226 store_ids,
227 paths: List::new(1), children: VariableList::new(),
229 leaves: VariableList::new(),
230 values: VariableList::new(),
231 words: VariableList::new(),
232 map_keys: VariableList::new(),
233 map_vals: VariableList::new(),
234 args_keys: VariableList::new(),
235 args_vals: VariableList::new(),
236 map_dst_pending: Vec::new(),
237 }
238 }
239
240 fn push_path(&mut self, entry: u64) -> Result<u16, DslError> {
243 let id = self.paths.data.len();
244 if id > MAX_PATH_ID {
245 return Err(DslError::LimitExceeded(alloc::format!(
246 "path_id {} exceeds max {}", id, MAX_PATH_ID
247 )));
248 }
249 self.paths.data.push(entry);
250 Ok(id as u16)
251 }
252
253 fn alloc_children_slots(&mut self, count: usize) -> Result<usize, DslError> {
256 if count == 0 { return Ok(0); }
257 let zeros: Vec<u16> = alloc::vec![0u16; count];
258 match self.children.set(&0, &zeros, false) {
259 Ok(crate::required::SetOutcome::Created(id)) => Ok(id),
260 _ => Err(DslError::LimitExceeded("children allocation failed".into())),
261 }
262 }
263
264 fn set_child(&mut self, children_id: usize, slot: usize, path_id: u16) {
265 if children_id == 0 { return; }
266 let identity_start = children_id * 2;
267 let start = self.children.identity[identity_start];
268 self.children.data[start + slot] = path_id;
269 }
270
271 fn walk_field_key(
274 &mut self,
275 keyword: &[u8],
276 value: &Tree,
277 parent_id: u16,
278 inh_get: Option<&MetaBlock>,
279 inh_set: Option<&MetaBlock>,
280 ) -> Result<(), DslError> {
281 let path_id = self.push_path(0u64)?; let keyword_id = self.intern_word(keyword)?;
284
285 match value {
286 Tree::Mapping(pairs) => {
287 let get = self.resolve_meta(pairs, META_GET, inh_get, path_id)?;
288 let set = self.resolve_meta(pairs, META_SET, inh_set, path_id)?;
289
290 let field_pairs: Vec<_> = pairs.iter()
291 .filter(|(k, _)| k.first() != Some(&b'_'))
292 .collect();
293 let child_count = field_pairs.len();
294
295 if child_count == 0 {
296 self.write_leaf(path_id, keyword_id, parent_id, &Tree::Null, get.as_ref(), set.as_ref())?;
297 } else {
298 let children_id = self.alloc_children_slots(child_count)?;
299 for (i, (k, v)) in field_pairs.iter().enumerate() {
300 let child_id = self.paths.data.len() as u16;
301 self.set_child(children_id, i, child_id);
302 self.walk_field_key(k, v, path_id, get.as_ref(), set.as_ref())?;
303 }
304 self.paths.data[path_id as usize] =
305 ((keyword_id as u64) << PATH_KEYWORD_ID_SHIFT)
306 | ((parent_id as u64) << PATH_PARENT_ID_SHIFT)
307 | ((children_id as u64) << PATH_CHILD_ID_SHIFT);
308 }
309 }
310 _ => {
311 self.write_leaf(path_id, keyword_id, parent_id, value, inh_get, inh_set)?;
312 }
313 }
314 Ok(())
315 }
316
317 fn resolve_meta(
320 &mut self,
321 pairs: &[(Vec<u8>, Tree)],
322 meta_key: &[u8],
323 inherited: Option<&MetaBlock>,
324 current_path_id: u16,
325 ) -> Result<Option<MetaBlock>, DslError> {
326 let local = pairs.iter().find(|(k, _)| k.as_slice() == meta_key);
327 match (local, inherited) {
328 (None, None) => Ok(None),
329 (None, Some(inh)) => Ok(Some(inh.clone())),
330 (Some((_, Tree::Mapping(meta_pairs))), inh) => {
331 let mut store_id = inh.map(|b| b.store_id).unwrap_or(0);
332 let mut defined_at = inh.map(|b| b.defined_at).unwrap_or(0);
333 let mut key_value: Vec<u16> = inh.map(|b| b.key_value.clone()).unwrap_or_default();
334 let mut map_entries: Vec<(u16, u16)> = inh.map(|b| b.map_entries.clone()).unwrap_or_default();
335 let mut arg_entries: Vec<(u16, Vec<u16>)> = inh.map(|b| b.arg_entries.clone()).unwrap_or_default();
336
337 for (k, v) in meta_pairs {
338 if k.as_slice() == PROP_STORE {
339 if let Tree::Scalar(b) = v {
340 store_id = self.resolve_store_id(b);
341 map_entries.clear();
342 defined_at = 0;
343 }
344 } else if k.as_slice() == PROP_KEY {
345 key_value = self.encode_value(v)?;
346 } else if k.as_slice() == PROP_MAP {
347 if let Tree::Mapping(map_pairs) = v {
348 map_entries.clear();
349 for (mk, mv) in map_pairs {
350 let dst = self.intern_word(mk)?;
351 let src = if let Tree::Scalar(b) = mv { self.intern_word(b)? } else { 0 };
352 map_entries.push((dst, src));
353 }
354 defined_at = current_path_id;
355 }
356 } else if k.as_slice() != META_GET
357 && k.as_slice() != META_SET
358 && k.as_slice() != META_STATE {
359 let ak = self.intern_word(k)?;
360 let av = self.encode_value(v)?;
361 if let Some(entry) = arg_entries.iter_mut().find(|(ek, _)| *ek == ak) {
362 entry.1 = av;
363 } else {
364 arg_entries.push((ak, av));
365 }
366 }
367 }
368 Ok(Some(MetaBlock { store_id, defined_at, key_value, map_entries, arg_entries }))
369 }
370 _ => Ok(inherited.cloned()),
371 }
372 }
373
374 fn write_leaf(
377 &mut self,
378 path_id: u16,
379 keyword_id: u16,
380 parent_id: u16,
381 value: &Tree,
382 get: Option<&MetaBlock>,
383 set: Option<&MetaBlock>,
384 ) -> Result<(), DslError> {
385 let value_frags = self.encode_value(value)?;
386 let value_id = if value_frags.is_empty() {
387 0u16
388 } else {
389 self.push_values(&value_frags)?
390 };
391
392 let (get_store_id, get_key_id, get_map_key_id, get_map_val_id, get_args_key_id, get_args_val_id)
393 = self.encode_meta(get)?;
394 let (set_store_id, set_key_id, set_map_key_id, set_map_val_id, set_args_key_id, set_args_val_id)
395 = self.encode_meta(set)?;
396
397 let leaf_data: [u16; LEAF_WIDTH] = [
398 get_store_id, get_key_id,
399 set_store_id, set_key_id,
400 get_map_key_id, get_map_val_id,
401 get_args_key_id, get_args_val_id,
402 set_map_key_id, set_map_val_id,
403 set_args_key_id, set_args_val_id,
404 ];
405
406 let leaf_id = match self.leaves.set(&0, &leaf_data, false) {
407 Ok(crate::required::SetOutcome::Created(id)) => {
408 if id > MAX_PATH_ID {
409 return Err(DslError::LimitExceeded(alloc::format!(
410 "leaf_id {} exceeds max {}", id, MAX_PATH_ID
411 )));
412 }
413 id as u16
414 }
415 _ => return Err(DslError::LimitExceeded("leaf allocation failed".into())),
416 };
417
418 self.paths.data[path_id as usize] =
419 PATH_IS_LEAF_MASK
420 | ((keyword_id as u64) << PATH_KEYWORD_ID_SHIFT)
421 | ((parent_id as u64) << PATH_PARENT_ID_SHIFT)
422 | ((leaf_id as u64) << PATH_CHILD_ID_SHIFT)
423 | (value_id as u64);
424 Ok(())
425 }
426
427 fn encode_meta(&mut self, meta: Option<&MetaBlock>) -> Result<(u16, u16, u16, u16, u16, u16), DslError> {
428 let Some(b) = meta else {
429 return Ok((0, 0, 0, 0, 0, 0));
430 };
431
432 let key_id = if b.key_value.is_empty() { 0u16 } else { self.push_values(&b.key_value)? };
433
434 let (map_key_id, map_val_id) = if b.map_entries.is_empty() {
435 (0u16, 0u16)
436 } else {
437 let dsts: Vec<u16> = b.map_entries.iter().map(|&(d, _)| d).collect();
438 let srcs: Vec<u16> = b.map_entries.iter().map(|&(_, s)| s).collect();
439 let mk_id = self.push_map_keys(&dsts)?;
440 let mv_id = self.push_map_vals(&srcs)?;
441 self.map_dst_pending.push((mk_id, b.defined_at));
442 (mk_id, mv_id)
443 };
444
445 let (args_key_id, args_val_id) = if b.arg_entries.is_empty() {
446 (0u16, 0u16)
447 } else {
448 let keys: Vec<u16> = b.arg_entries.iter().map(|&(k, _)| k).collect();
449 let mut val_ids: Vec<u16> = Vec::with_capacity(b.arg_entries.len());
450 for (_, frags) in &b.arg_entries {
451 let id = if frags.is_empty() { 0u16 } else { self.push_values(frags)? };
452 val_ids.push(id);
453 }
454 let ak = match self.args_keys.set(&0, &keys, false) {
455 Ok(crate::required::SetOutcome::Created(id)) => {
456 if id > MAX_PATH_ID { return Err(DslError::LimitExceeded("args_keys id overflow".into())); }
457 id as u16
458 }
459 _ => return Err(DslError::LimitExceeded("args_keys allocation failed".into())),
460 };
461 let av = match self.args_vals.set(&0, &val_ids, false) {
462 Ok(crate::required::SetOutcome::Created(id)) => {
463 if id > MAX_PATH_ID { return Err(DslError::LimitExceeded("args_vals id overflow".into())); }
464 id as u16
465 }
466 _ => return Err(DslError::LimitExceeded("args_vals allocation failed".into())),
467 };
468 (ak, av)
469 };
470
471 Ok((b.store_id as u16, key_id, map_key_id, map_val_id, args_key_id, args_val_id))
472 }
473
474 fn push_values(&mut self, frags: &[u16]) -> Result<u16, DslError> {
477 match self.values.set(&0, frags, false) {
478 Ok(crate::required::SetOutcome::Created(id)) => {
479 if id > MAX_PATH_ID {
480 return Err(DslError::LimitExceeded(alloc::format!(
481 "values id {} exceeds max {}", id, MAX_PATH_ID
482 )));
483 }
484 Ok(id as u16)
485 }
486 _ => Err(DslError::LimitExceeded("values allocation failed".into())),
487 }
488 }
489
490 fn push_map_keys(&mut self, data: &[u16]) -> Result<u16, DslError> {
491 match self.map_keys.set(&0, data, false) {
492 Ok(crate::required::SetOutcome::Created(id)) => {
493 if id > MAX_PATH_ID {
494 return Err(DslError::LimitExceeded(alloc::format!(
495 "map_keys id {} exceeds max {}", id, MAX_PATH_ID
496 )));
497 }
498 Ok(id as u16)
499 }
500 _ => Err(DslError::LimitExceeded("map_keys allocation failed".into())),
501 }
502 }
503
504 fn push_map_vals(&mut self, data: &[u16]) -> Result<u16, DslError> {
505 match self.map_vals.set(&0, data, false) {
506 Ok(crate::required::SetOutcome::Created(id)) => {
507 if id > MAX_PATH_ID {
508 return Err(DslError::LimitExceeded(alloc::format!(
509 "map_vals id {} exceeds max {}", id, MAX_PATH_ID
510 )));
511 }
512 Ok(id as u16)
513 }
514 _ => Err(DslError::LimitExceeded("map_vals allocation failed".into())),
515 }
516 }
517
518 fn encode_value(&mut self, value: &Tree) -> Result<Vec<u16>, DslError> {
521 let Tree::Scalar(b) = value else { return Ok(Vec::new()); };
522 let mut frags: Vec<u16> = Vec::new();
523 let mut rest = b.as_slice();
524 while !rest.is_empty() {
525 if let Some(start) = rest.windows(2).position(|w| w == b"${") {
526 if start > 0 {
527 let word_id = self.intern_word(&rest[..start])?;
528 frags.push(word_id & VALUE_WORD_ID_MASK);
529 }
530 rest = &rest[start + 2..];
531 if let Some(end) = rest.iter().position(|&c| c == b'}') {
532 let word_id = self.intern_word(&rest[..end])?;
533 frags.push(VALUE_IS_PLACEHOLDER_MASK | (word_id & VALUE_WORD_ID_MASK));
534 rest = &rest[end + 1..];
535 } else {
536 let word_id = self.intern_word(rest)?;
537 frags.push(word_id & VALUE_WORD_ID_MASK);
538 break;
539 }
540 } else {
541 let word_id = self.intern_word(rest)?;
542 frags.push(word_id & VALUE_WORD_ID_MASK);
543 break;
544 }
545 }
546 Ok(frags)
547 }
548
549 fn intern_word(&mut self, s: &[u8]) -> Result<u16, DslError> {
552 match self.words.set(&0, s, true) {
553 Ok(crate::required::SetOutcome::Created(id)) => {
554 if id > MAX_WORD_ID {
555 return Err(DslError::LimitExceeded(alloc::format!(
556 "word_id {} exceeds max {} (15-bit limit)", id, MAX_WORD_ID
557 )));
558 }
559 Ok(id as u16)
560 }
561 _ => Err(DslError::LimitExceeded("word interning failed".into())),
562 }
563 }
564
565 fn resolve_store_id(&self, name: &[u8]) -> u8 {
568 self.store_ids.iter().position(|s| s.as_bytes() == name)
569 .map(|i| (i + 1) as u8) .unwrap_or(0)
571 }
572
573 fn resolve_map_dst(&mut self) -> Result<(), DslError> {
576 let pending = core::mem::take(&mut self.map_dst_pending);
577 for (map_key_id, defined_at) in pending {
578 let mk_start = self.map_keys.identity[map_key_id as usize * 2];
579 let mk_end = self.map_keys.identity[map_key_id as usize * 2 + 1];
580 for i in mk_start..mk_end {
581 let word_id = self.map_keys.data[i];
582 let kw_start = self.words.identity[word_id as usize * 2];
583 let kw_end = self.words.identity[word_id as usize * 2 + 1];
584 let keyword = self.words.data[kw_start..kw_end].to_vec();
585 let path_id = self.find_path_by_keyword_chain(defined_at, &keyword)?;
586 self.map_keys.data[i] = path_id;
587 }
588 }
589 Ok(())
590 }
591
592 fn find_path_by_keyword_chain(&self, parent_id: u16, keyword: &[u8]) -> Result<u16, DslError> {
594 let mut current = parent_id;
595 for segment in keyword.split(|&b| b == b'.') {
596 current = self.find_child_path(current, segment)
597 .ok_or_else(|| DslError::LimitExceeded(alloc::format!(
598 "map dst {:?} not found under path_id {}",
599 core::str::from_utf8(keyword).unwrap_or("?"), parent_id
600 )))?;
601 }
602 Ok(current)
603 }
604
605 fn find_child_path(&self, path_id: u16, keyword: &[u8]) -> Option<u16> {
606 let path = self.paths.data[path_id as usize];
607 if path & PATH_IS_LEAF_MASK != 0 { return None; }
608 let children_id = ((path & PATH_CHILD_ID_MASK) >> PATH_CHILD_ID_SHIFT) as usize;
609 if children_id == 0 { return None; }
610 let ck_start = self.children.identity[children_id * 2];
611 let ck_end = self.children.identity[children_id * 2 + 1];
612 for &child_id in &self.children.data[ck_start..ck_end] {
613 let child_path = self.paths.data[child_id as usize];
614 let word_id = ((child_path & PATH_KEYWORD_ID_MASK) >> PATH_KEYWORD_ID_SHIFT) as usize;
615 let wk_start = self.words.identity[word_id * 2];
616 let wk_end = self.words.identity[word_id * 2 + 1];
617 if &self.words.data[wk_start..wk_end] == keyword {
618 return Some(child_id);
619 }
620 }
621 None
622 }
623
624 fn finish(self) -> Result<(
627 List<u64>,
628 VariableList<u16>,
629 VariableList<u16>,
630 VariableList<u16>,
631 VariableList<u8>,
632 VariableList<u16>,
633 VariableList<u16>,
634 VariableList<u16>,
635 VariableList<u16>,
636 ), DslError> {
637 check_vl_data_u16(&self.children, "children")?;
639 check_vl_data_u16(&self.leaves, "leaves")?;
640 check_vl_data_u16(&self.values, "values")?;
641 check_vl_data_u16_from_u8(&self.words, "words")?;
642 check_vl_data_u16(&self.map_keys, "map_keys")?;
643 check_vl_data_u16(&self.map_vals, "map_vals")?;
644 check_vl_data_u16(&self.args_keys, "args_keys")?;
645 check_vl_data_u16(&self.args_vals, "args_vals")?;
646 Ok((
647 self.paths,
648 self.children,
649 self.leaves,
650 self.values,
651 self.words,
652 self.map_keys,
653 self.map_vals,
654 self.args_keys,
655 self.args_vals,
656 ))
657 }
658}
659
660fn check_vl_data_u16<T>(vl: &VariableList<T>, name: &str) -> Result<(), DslError> {
663 if vl.data.len() > MAX_VL_U16_DATA {
664 return Err(DslError::LimitExceeded(alloc::format!(
665 "{} data length {} exceeds max {}", name, vl.data.len(), MAX_VL_U16_DATA
666 )));
667 }
668 Ok(())
669}
670
671fn check_vl_data_u16_from_u8(vl: &VariableList<u8>, name: &str) -> Result<(), DslError> {
672 if vl.data.len() > MAX_VL_U8_DATA {
673 return Err(DslError::LimitExceeded(alloc::format!(
674 "{} data length {} exceeds max {}", name, vl.data.len(), MAX_VL_U8_DATA
675 )));
676 }
677 Ok(())
678}
679
680#[cfg(feature = "precompile")]
681fn check_vl_u16_identity<T>(vl: &VariableList<T>, name: &str) -> Result<(), alloc::string::String> {
682 for &offset in &vl.identity {
683 if offset > MAX_VL_U16_DATA {
684 return Err(alloc::format!(
685 "{} identity offset {} exceeds u16 max {}", name, offset, MAX_VL_U16_DATA
686 ));
687 }
688 }
689 Ok(())
690}
691
692#[cfg(feature = "precompile")]
693fn check_vl_u16_identity_u8(vl: &VariableList<u8>, name: &str) -> Result<(), alloc::string::String> {
694 for &offset in &vl.identity {
695 if offset > MAX_VL_U8_DATA {
696 return Err(alloc::format!(
697 "{} identity offset {} exceeds u16 max {}", name, offset, MAX_VL_U8_DATA
698 ));
699 }
700 }
701 Ok(())
702}
703
704#[cfg(feature = "precompile")]
707pub fn parse_yaml(src: &[u8]) -> Result<Tree, alloc::string::String> {
708 extern crate std;
709 use std::format;
710
711 let s = std::str::from_utf8(src)
712 .map_err(|e| format!("UTF-8 error: {e}"))?;
713 let yaml: serde_yaml_ng::Value = serde_yaml_ng::from_str(s)
714 .map_err(|e| format!("YAML parse error: {e}"))?;
715 Ok(yaml_value_to_tree(yaml))
716}
717
718#[cfg(feature = "precompile")]
719fn yaml_value_to_tree(v: serde_yaml_ng::Value) -> Tree {
720 extern crate std;
721 use std::string::ToString;
722
723 match v {
724 serde_yaml_ng::Value::Mapping(m) => Tree::Mapping(
725 m.into_iter()
726 .filter_map(|(k, v)| {
727 if let serde_yaml_ng::Value::String(s) = k {
728 Some((s.into_bytes(), yaml_value_to_tree(v)))
729 } else {
730 None
731 }
732 })
733 .collect(),
734 ),
735 serde_yaml_ng::Value::Sequence(s) => {
736 Tree::Sequence(s.into_iter().map(yaml_value_to_tree).collect())
737 }
738 serde_yaml_ng::Value::String(s) => Tree::Scalar(s.into_bytes()),
739 serde_yaml_ng::Value::Number(n) => Tree::Scalar(n.to_string().into_bytes()),
740 serde_yaml_ng::Value::Bool(b) => Tree::Scalar(b.to_string().into_bytes()),
741 serde_yaml_ng::Value::Null => Tree::Null,
742 _ => Tree::Null,
743 }
744}
745
746#[cfg(feature = "precompile")]
747fn emit_u64_slice(out: &mut alloc::string::String, name: &str, data: &[u64]) {
748 extern crate std;
749 use std::format;
750 out.push_str(&format!("pub static {name}: &[u64] = &[\n"));
751 for chunk in data.chunks(8) {
752 out.push_str(" ");
753 for v in chunk { out.push_str(&format!("0x{v:016x}, ")); }
754 out.push('\n');
755 }
756 out.push_str("];\n\n");
757}
758
759#[cfg(feature = "precompile")]
760fn emit_u16_slice(out: &mut alloc::string::String, name: &str, data: &[u16]) {
761 extern crate std;
762 use std::format;
763 out.push_str(&format!("pub static {name}: &[u16] = &[\n"));
764 for chunk in data.chunks(8) {
765 out.push_str(" ");
766 for v in chunk { out.push_str(&format!("0x{v:04x}, ")); }
767 out.push('\n');
768 }
769 out.push_str("];\n\n");
770}
771
772#[cfg(feature = "precompile")]
773fn emit_u8_slice(out: &mut alloc::string::String, name: &str, data: &[u8]) {
774 extern crate std;
775 use std::format;
776 out.push_str(&format!("pub static {name}: &[u8] = &[\n"));
777 for chunk in data.chunks(16) {
778 out.push_str(" ");
779 for v in chunk { out.push_str(&format!("0x{v:02x}, ")); }
780 out.push('\n');
781 }
782 out.push_str("];\n\n");
783}
784
785#[cfg(test)]
788mod tests {
789 use super::*;
790 use alloc::vec;
791
792 fn scalar(s: &str) -> Tree { Tree::Scalar(s.as_bytes().to_vec()) }
793 fn mapping(pairs: Vec<(&str, Tree)>) -> Tree {
794 Tree::Mapping(pairs.into_iter().map(|(k, v)| (k.as_bytes().to_vec(), v)).collect())
795 }
796
797 fn compile(tree: &Tree) -> (List<u64>, VariableList<u16>, VariableList<u16>, VariableList<u16>, VariableList<u8>, VariableList<u16>, VariableList<u16>, VariableList<u16>, VariableList<u16>) {
798 Dsl::compile(tree, &[]).unwrap()
799 }
800
801 fn compile_with_stores<'a>(tree: &Tree, store_ids: &[&'a str]) -> (List<u64>, VariableList<u16>, VariableList<u16>, VariableList<u16>, VariableList<u8>, VariableList<u16>, VariableList<u16>, VariableList<u16>, VariableList<u16>) {
802 Dsl::compile(tree, store_ids).unwrap()
803 }
804
805 #[test]
808 fn single_leaf() {
809 let (paths, ..) = compile(&mapping(vec![
810 ("name", Tree::Null),
811 ]));
812 assert_eq!(paths.data.len(), 2); assert!(paths.data[0] & PATH_IS_LEAF_MASK == 0); assert!(paths.data[1] & PATH_IS_LEAF_MASK != 0); }
816
817 #[test]
820 fn nested() {
821 let (paths, children, ..) = compile(&mapping(vec![
822 ("user", mapping(vec![
823 ("id", Tree::Null),
824 ("name", Tree::Null),
825 ])),
826 ]));
827 assert_eq!(paths.data.len(), 4); assert!(paths.data[1] & PATH_IS_LEAF_MASK == 0); assert!(children.data.len() >= 3); }
831
832 #[test]
835 fn meta_key_excluded_from_paths() {
836 let (paths, ..) = compile(&mapping(vec![
837 ("user", mapping(vec![
838 ("_get", mapping(vec![
839 ("store", scalar("Memory")),
840 ("key", scalar("user:1")),
841 ])),
842 ("id", Tree::Null),
843 ])),
844 ]));
845 assert_eq!(paths.data.len(), 3);
847 }
848
849 #[test]
852 fn get_store_id_set_in_leaf() {
853 let store_ids = &["Memory", "Kvs"];
854 let (paths, _, leaves, ..) = compile_with_stores(&mapping(vec![
855 ("user", mapping(vec![
856 ("_get", mapping(vec![
857 ("store", scalar("Memory")),
858 ("key", scalar("user:1")),
859 ])),
860 ("id", Tree::Null),
861 ])),
862 ]), store_ids);
863 let id_path = paths.data[2];
865 assert!(id_path & PATH_IS_LEAF_MASK != 0);
866 let leaf_id = ((id_path & PATH_CHILD_ID_MASK) >> PATH_CHILD_ID_SHIFT) as usize;
867 let leaf_start = leaves.identity[leaf_id * 2];
868 let get_store_id = leaves.data[leaf_start + LEAF_GET_STORE_ID];
869 assert_eq!(get_store_id, 1);
871 }
872
873 #[test]
876 fn store_inherited_to_child_leaf() {
877 let store_ids = &["Memory", "Kvs"];
878 let (paths, _, leaves, ..) = compile_with_stores(&mapping(vec![
879 ("session", mapping(vec![
880 ("_set", mapping(vec![
881 ("store", scalar("Kvs")),
882 ("key", scalar("session:1")),
883 ])),
884 ("user", mapping(vec![
885 ("id", Tree::Null),
886 ])),
887 ])),
888 ]), store_ids);
889 let id_path = paths.data[3];
891 assert!(id_path & PATH_IS_LEAF_MASK != 0);
892 let leaf_id = ((id_path & PATH_CHILD_ID_MASK) >> PATH_CHILD_ID_SHIFT) as usize;
893 let leaf_start = leaves.identity[leaf_id * 2];
894 let set_store_id = leaves.data[leaf_start + LEAF_SET_STORE_ID];
895 assert_eq!(set_store_id, 2);
897 }
898
899 #[test]
902 fn intern_dedup() {
903 let (_, _, _, _, words, ..) = compile(&mapping(vec![
904 ("a", scalar("hello")),
905 ("b", scalar("hello")),
906 ]));
907 let hello_count = (1..words.identity.len() / 2).filter(|&i| {
908 let start = words.identity[i * 2];
909 let end = words.identity[i * 2 + 1];
910 words.data.get(start..end) == Some(b"hello" as &[u8])
911 }).count();
912 assert_eq!(hello_count, 1);
913 }
914
915 #[test]
918 fn static_value_encoded_as_single_fragment() {
919 let (paths, _, _, values, words, ..) = compile(&mapping(vec![
920 ("key", scalar("hello")),
921 ]));
922 let path = paths.data[1];
923 assert!(path & PATH_IS_LEAF_MASK != 0);
924 let value_id = (path & PATH_VALUE_ID_MASK) as usize;
925 assert!(value_id != 0);
926 let vstart = values.identity[value_id * 2];
927 let vend = values.identity[value_id * 2 + 1];
928 let frags = &values.data[vstart..vend];
929 assert_eq!(frags.len(), 1);
930 assert_eq!(frags[0] & VALUE_IS_PLACEHOLDER_MASK, 0); let word_id = (frags[0] & VALUE_WORD_ID_MASK) as usize;
932 let wstart = words.identity[word_id * 2];
933 let wend = words.identity[word_id * 2 + 1];
934 assert_eq!(&words.data[wstart..wend], b"hello");
935 }
936
937 #[test]
938 fn placeholder_value_encoded_as_single_fragment_is_placeholder() {
939 let (paths, _, _, values, ..) = compile(&mapping(vec![
940 ("copy", scalar("${session.user.id}")),
941 ]));
942 let path = paths.data[1];
943 let value_id = (path & PATH_VALUE_ID_MASK) as usize;
944 let vstart = values.identity[value_id * 2];
945 let vend = values.identity[value_id * 2 + 1];
946 let frags = &values.data[vstart..vend];
947 assert_eq!(frags.len(), 1);
948 assert_ne!(frags[0] & VALUE_IS_PLACEHOLDER_MASK, 0); }
950
951 #[test]
952 fn template_value_encoded_as_multiple_fragments() {
953 let (paths, _, _, values, ..) = compile(&mapping(vec![
954 ("key", scalar("prefix.${some.path}.suffix")),
955 ]));
956 let path = paths.data[1];
957 let value_id = (path & PATH_VALUE_ID_MASK) as usize;
958 let vstart = values.identity[value_id * 2];
959 let vend = values.identity[value_id * 2 + 1];
960 let frags = &values.data[vstart..vend];
961 assert_eq!(frags.len(), 3);
962 assert_eq!(frags[0] & VALUE_IS_PLACEHOLDER_MASK, 0); assert_ne!(frags[1] & VALUE_IS_PLACEHOLDER_MASK, 0); assert_eq!(frags[2] & VALUE_IS_PLACEHOLDER_MASK, 0); }
966
967 #[test]
970 fn local_get_overrides_inherited_store() {
971 let store_ids = &["ClientA", "ClientB"];
972 let (paths, _, leaves, ..) = compile_with_stores(&mapping(vec![
973 ("parent", mapping(vec![
974 ("_get", mapping(vec![
975 ("store", scalar("ClientA")),
976 ("key", scalar("k")),
977 ])),
978 ("child", mapping(vec![
979 ("_get", mapping(vec![
980 ("store", scalar("ClientB")),
981 ("key", scalar("k")),
982 ])),
983 ("leaf", Tree::Null),
984 ])),
985 ])),
986 ]), store_ids);
987 let leaf_path = paths.data[3];
989 assert!(leaf_path & PATH_IS_LEAF_MASK != 0);
990 let leaf_id = ((leaf_path & PATH_CHILD_ID_MASK) >> PATH_CHILD_ID_SHIFT) as usize;
991 let leaf_start = leaves.identity[leaf_id * 2];
992 let get_store_id = leaves.data[leaf_start + LEAF_GET_STORE_ID];
993 assert_eq!(get_store_id, 2);
995 }
996
997 #[test]
998 fn get_inherited_when_no_local_override() {
999 let store_ids = &["Inherited"];
1000 let (paths, _, leaves, ..) = compile_with_stores(&mapping(vec![
1001 ("parent", mapping(vec![
1002 ("_get", mapping(vec![
1003 ("store", scalar("Inherited")),
1004 ("key", scalar("k")),
1005 ])),
1006 ("leaf", Tree::Null),
1007 ])),
1008 ]), store_ids);
1009 let leaf_path = paths.data[2];
1011 assert!(leaf_path & PATH_IS_LEAF_MASK != 0);
1012 let leaf_id = ((leaf_path & PATH_CHILD_ID_MASK) >> PATH_CHILD_ID_SHIFT) as usize;
1013 let leaf_start = leaves.identity[leaf_id * 2];
1014 let get_store_id = leaves.data[leaf_start + LEAF_GET_STORE_ID];
1015 assert_eq!(get_store_id, 1);
1017 }
1018
1019 #[cfg(feature = "precompile")]
1022 #[test]
1023 fn write_tenant_yml() {
1024 extern crate std;
1025 let src = std::include_bytes!("../examples/tenant.yml");
1026 let out = std::env::temp_dir().join("tenant_compiled.rs");
1027 std::fs::remove_file(&out).ok();
1028 Dsl::write(src, &["Memory", "Kvs", "TenantDb", "CommonDb", "Env"], out.to_str().unwrap()).expect("write failed");
1029 let content = std::fs::read_to_string(&out).expect("output not written");
1030 assert!(content.contains("pub static PATHS:"));
1031 }
1032}