1use crate::ecl::{Expr, Hierarchy, MAX_DEPTH, MAX_NODES};
3use crate::store::NumericStore;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::{error::Error, fmt};
6mod descriptions;
7mod filters;
8mod history;
9mod members;
10mod membership;
11pub use members::QueryResult;
12mod refinement;
13mod values;
14
15#[derive(Clone, Copy)]
16pub struct Limits {
17 pub max_work: u64,
18 pub max_live_set_values: usize,
19}
20impl Default for Limits {
21 fn default() -> Self {
22 Self {
23 max_work: 100_000_000,
24 max_live_set_values: 8_000_000,
25 }
26 }
27}
28#[derive(Debug, PartialEq, Eq)]
29pub enum EvalError {
30 WorkLimit,
31 MemoryLimit,
32 Cancelled,
33 InvalidAst,
34 Unsupported(&'static str),
36 Semantic(String),
38 Index(String),
39 Text(String),
40 InvalidField(String),
41 TypeMismatch,
42 MissingReference(String),
44 UnconfiguredAlias(String),
45}
46impl fmt::Display for EvalError {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 write!(f, "Evaluation failed: {self:?}")
49 }
50}
51impl Error for EvalError {}
52type Result<T> = std::result::Result<T, EvalError>;
53
54pub fn evaluate(store: &NumericStore, expression: &Expr) -> Result<Vec<u32>> {
55 evaluate_with_limits(store, expression, Limits::default(), None)
56}
57pub fn evaluate_result(store: &NumericStore, expression: &Expr) -> Result<QueryResult> {
58 evaluate_result_with_limits(store, expression, Limits::default(), None)
59}
60pub fn evaluate_result_with_limits(
61 store: &NumericStore,
62 expression: &Expr,
63 limits: Limits,
64 cancelled: Option<&AtomicBool>,
65) -> Result<QueryResult> {
66 let mut context = Context {
67 store,
68 limits,
69 cancelled,
70 work: 0,
71 live: 0,
72 nodes: 0,
73 marks: Marks::borrow(),
74 };
75 context.result(expression, 0, true)
76}
77pub fn evaluate_with_limits(
78 store: &NumericStore,
79 expression: &Expr,
80 limits: Limits,
81 cancelled: Option<&AtomicBool>,
82) -> Result<Vec<u32>> {
83 Context {
84 store,
85 limits,
86 cancelled,
87 work: 0,
88 live: 0,
89 nodes: 0,
90 marks: Marks::borrow(),
91 }
92 .eval(expression, 0)
93}
94const SMALL_FOCUS: usize = 4096;
97
98enum Focus {
100 Everything,
101 Set(Vec<u32>),
102 Below(Hierarchy, Vec<u32>),
104}
105
106struct Context<'a> {
107 store: &'a NumericStore,
108 limits: Limits,
109 cancelled: Option<&'a AtomicBool>,
110 work: u64,
111 live: usize,
112 nodes: usize,
113 marks: Marks,
114}
115
116#[derive(Default)]
128struct Marks {
129 seen: Vec<u8>,
130 selected: Vec<u8>,
131 stamp: u8,
132}
133impl Marks {
134 fn next(&mut self, n: usize) -> u8 {
135 if self.seen.len() != n {
136 self.seen = vec![0; n];
137 self.selected = vec![0; n];
138 self.stamp = 0;
139 }
140 if self.stamp == u8::MAX {
141 self.seen.fill(0);
142 self.selected.fill(0);
143 self.stamp = 0;
144 }
145 self.stamp += 1;
146 self.stamp
147 }
148 fn borrow() -> Self {
150 MARKS.with(|slot| std::mem::take(&mut *slot.borrow_mut()))
151 }
152}
153
154thread_local! {
155 static MARKS: std::cell::RefCell<Marks> = std::cell::RefCell::new(Marks::default());
156}
157
158impl Drop for Context<'_> {
159 fn drop(&mut self) {
160 let marks = std::mem::take(&mut self.marks);
161 MARKS.with(|slot| *slot.borrow_mut() = marks);
162 }
163}
164
165impl Context<'_> {
166 fn tick(&mut self, work: usize) -> Result<()> {
167 if self
168 .cancelled
169 .is_some_and(|flag| flag.load(Ordering::Relaxed))
170 {
171 return Err(EvalError::Cancelled);
172 }
173 self.work = self
174 .work
175 .checked_add(work as u64)
176 .ok_or(EvalError::WorkLimit)?;
177 if self.work > self.limits.max_work {
178 return Err(EvalError::WorkLimit);
179 }
180 Ok(())
181 }
182 fn reserve(&mut self, values: usize) -> Result<Vec<u32>> {
183 self.claim(values)?;
184 Ok(Vec::with_capacity(values))
185 }
186 fn claim(&mut self, values: usize) -> Result<()> {
187 self.live = self
188 .live
189 .checked_add(values)
190 .ok_or(EvalError::MemoryLimit)?;
191 if self.live > self.limits.max_live_set_values {
192 return Err(EvalError::MemoryLimit);
193 }
194 Ok(())
195 }
196 fn release(&mut self, values: Vec<u32>) {
197 self.live -= values.capacity();
198 }
199 fn eval(&mut self, expr: &Expr, depth: usize) -> Result<Vec<u32>> {
200 let result = self.result(expr, depth, false)?;
201 self.concept_values(result)
202 }
203 fn eval_concepts(&mut self, expr: &Expr, depth: usize) -> Result<Vec<u32>> {
204 self.tick(1)?;
205 self.nodes += 1;
206 if depth > MAX_DEPTH * 3 || self.nodes > MAX_NODES {
207 return Err(EvalError::InvalidAst);
208 }
209 match expr {
210 Expr::AlternateIdentifier { scheme, code } => {
211 let id = self
212 .store
213 .config
214 .identifier_schemes
215 .get(&scheme.to_ascii_lowercase())
216 .ok_or_else(|| EvalError::UnconfiguredAlias(scheme.clone()))?;
217 let index = self
218 .store
219 .identifiers
220 .get()
221 .map_err(|e| EvalError::Index(e.to_string()))?
222 .ok_or(EvalError::Unsupported(
223 "Identifier index is absent; rebuild from RF2",
224 ))?;
225 self.tick(index.rows.len().checked_ilog2().unwrap_or(0) as usize + code.len() + 1)?;
226 let mut result = self.reserve(1)?;
227 if let Some(code) = index.lookup(*id, code) {
228 let ordinal = self.store.ordinal(code).ok_or_else(|| {
229 EvalError::Index("Identifier refers to an absent concept".into())
230 })?;
231 result.push(ordinal);
232 }
233 Ok(result)
234 }
235 Expr::DialectAlias(alias) => {
236 let id = self
237 .store
238 .config
239 .dialects
240 .get(&alias.to_ascii_lowercase())
241 .ok_or_else(|| EvalError::UnconfiguredAlias(alias.clone()))?;
242 let mut result = self.reserve(1)?;
243 if let Some(ordinal) = self.store.ordinal(*id) {
244 result.push(ordinal);
245 }
246 Ok(result)
247 }
248 Expr::History(inner, supplement) => self.history(inner, supplement, depth + 1),
249 Expr::DescriptionFiltered(inner, filters) => {
250 let candidates = self.eval(inner, depth + 1)?;
251 self.description_filters(candidates, filters, depth + 1)
252 }
253 Expr::ConceptFiltered(inner, filters) => {
254 let candidates = self.eval(inner, depth + 1)?;
255 self.concept_filters(candidates, filters, depth + 1)
256 }
257 Expr::MemberOf(inner) | Expr::RefsetContainingAny(inner) => {
258 let candidates = self.eval(inner, depth + 1)?;
259 let result =
260 self.membership(&candidates, matches!(expr, Expr::RefsetContainingAny(_)))?;
261 self.release(candidates);
262 Ok(result)
263 }
264 Expr::Refined(focus, refinement) => {
265 let prepared = self.prepare(refinement, depth + 1, false)?;
266 let focus = match focus.as_ref() {
271 Expr::All => Focus::Everything,
272 Expr::Hierarchy(op, inner) if !op.ancestors() => {
273 let seeds = self.eval(inner, depth + 1)?;
274 match self.hierarchy_within(*op, &seeds, SMALL_FOCUS)? {
275 Some(set) => {
276 self.release(seeds);
277 Focus::Set(set)
278 }
279 None => Focus::Below(*op, seeds),
280 }
281 }
282 other => Focus::Set(self.eval(other, depth + 1)?),
283 };
284 let limit = match &focus {
285 Focus::Set(set) => set.len(),
286 _ => self.store.ids.len(),
287 };
288 let n = self.store.ids.len();
289 let candidates = match (self.candidates(&prepared, limit)?, focus) {
290 (Some(bound), Focus::Everything) => {
291 self.tick(bound.len())?;
292 self.claim(bound.capacity())?;
293 bound
294 }
295 (Some(bound), Focus::Below(op, seeds)) if bound.len() <= n / 16 => {
296 let tested = self.below(op, &seeds, &bound)?;
297 self.release(seeds);
298 tested
299 }
300 (bound, Focus::Below(op, seeds)) => {
301 let set = self.hierarchy(op, &seeds)?;
302 self.release(seeds);
303 match bound {
304 Some(bound) => self.within(set, &bound)?,
305 None => set,
306 }
307 }
308 (Some(bound), Focus::Set(set)) => self.within(set, &bound)?,
309 (None, Focus::Set(set)) => set,
310 (None, Focus::Everything) => self.eval(&Expr::All, depth + 1)?,
311 };
312 let mut result = self.reserve(candidates.len())?;
313 for &source in &candidates {
314 if self.matches_refinement(&prepared, source, None)? {
315 result.push(source);
316 }
317 }
318 self.release(candidates);
319 self.release_prepared(prepared);
320 Ok(result)
321 }
322 Expr::Extremum { top: true, inner } => {
323 let candidates = self.eval(inner, depth + 1)?;
324 let result = self.top(&candidates)?;
325 self.release(candidates);
326 Ok(result)
327 }
328 Expr::Extremum { top, inner } => {
329 let candidates = self.eval(inner, depth + 1)?;
330 let excluded = self.hierarchy(
331 if *top {
332 Hierarchy::Descendant
333 } else {
334 Hierarchy::Ancestor
335 },
336 &candidates,
337 )?;
338 let result = self.merge(&candidates, &excluded, 2)?;
339 self.release(candidates);
340 self.release(excluded);
341 Ok(result)
342 }
343 Expr::Concept(code) => {
344 let ordinal = self.store.ordinal(*code);
345 let mut result = self.reserve(usize::from(ordinal.is_some()))?;
346 result.extend(ordinal);
347 Ok(result)
348 }
349 Expr::All => {
350 self.tick(self.store.ids.len())?;
351 let count = self.store.ids.len();
352 let mut result = self.reserve(count)?;
353 result.extend(0..count as u32);
354 Ok(result)
355 }
356 Expr::Hierarchy(op, inner) => {
357 let seeds = self.eval(inner, depth + 1)?;
358 let result = self.hierarchy(*op, &seeds)?;
359 self.release(seeds);
360 Ok(result)
361 }
362 Expr::Members(_)
363 | Expr::Dotted(_, _)
364 | Expr::And(_)
365 | Expr::Or(_)
366 | Expr::Minus(_, _) => Err(EvalError::InvalidAst),
367 }
368 }
369 fn hierarchy(&mut self, op: Hierarchy, seeds: &[u32]) -> Result<Vec<u32>> {
379 Ok(self.hierarchy_within(op, seeds, usize::MAX)?.unwrap())
380 }
381 fn hierarchy_within(
383 &mut self,
384 op: Hierarchy,
385 seeds: &[u32],
386 cap: usize,
387 ) -> Result<Option<Vec<u32>>> {
388 if seeds.is_empty() {
389 return self.reserve(0).map(Some);
390 }
391 let store = self.store;
392 let graph = if op.ancestors() {
393 &store.parents
394 } else {
395 &store.children
396 };
397 let n = store.ids.len();
398 let stamp = self.marks.next(n);
399 self.tick(seeds.len())?;
400 let large = n / 16;
403 let mut count = 0usize;
404 let mut stack = Vec::with_capacity(seeds.len());
405 let mut found = Vec::new();
406 for &seed in seeds {
407 let i = seed as usize;
408 if self.marks.seen[i] != stamp {
409 self.marks.seen[i] = stamp;
410 stack.push(seed);
411 }
412 if op.include_self() && self.marks.selected[i] != stamp {
413 self.marks.selected[i] = stamp;
414 count += 1;
415 if count <= large {
416 found.push(seed);
417 }
418 }
419 }
420 while let Some(node) = stack.pop() {
421 let next = graph.get(node);
422 self.tick(1 + next.len())?;
423 for &concept in next {
424 let i = concept as usize;
425 if self.marks.selected[i] != stamp {
426 self.marks.selected[i] = stamp;
427 count += 1;
428 if count <= large {
429 found.push(concept);
430 }
431 if count > cap {
432 return Ok(None);
433 }
434 }
435 if !op.direct() && self.marks.seen[i] != stamp {
436 self.marks.seen[i] = stamp;
437 stack.push(concept);
438 }
439 }
440 }
441 if count > large {
445 self.tick(n)?;
446 found = Vec::with_capacity(count);
447 found.extend(
448 self.marks
449 .selected
450 .iter()
451 .enumerate()
452 .filter(|&(_, &mark)| mark == stamp)
453 .map(|(i, _)| i as u32),
454 );
455 } else {
456 self.tick(found.len())?;
457 found.sort_unstable();
458 }
459 found.shrink_to_fit();
460 self.claim(found.capacity())?;
461 Ok(Some(found))
462 }
463 fn top(&mut self, set: &[u32]) -> Result<Vec<u32>> {
468 let stamp = self.mark_seeds(set)?;
469 let mut result = self.reserve(set.len())?;
470 let mut stack = Vec::new();
471 for &member in set {
472 if !self.has_seed_above(member, stamp, &mut stack)? {
473 result.push(member);
474 }
475 }
476 Ok(result)
477 }
478
479 fn below(&mut self, op: Hierarchy, seeds: &[u32], candidates: &[u32]) -> Result<Vec<u32>> {
484 debug_assert!(!op.ancestors());
485 let stamp = self.mark_seeds(seeds)?;
486 let mut result = self.reserve(candidates.len())?;
487 let mut stack = Vec::new();
488 for &candidate in candidates {
489 let is_seed = seeds.binary_search(&candidate).is_ok();
490 self.tick(1)?;
491 let inside = if op.include_self() && is_seed {
492 true
493 } else if op.direct() {
494 let parents = self.store.parents.get(candidate);
495 self.tick(parents.len())?;
496 parents.iter().any(|p| seeds.binary_search(p).is_ok())
497 } else {
498 self.has_seed_above(candidate, stamp, &mut stack)?
499 };
500 if inside {
501 result.push(candidate);
502 }
503 }
504 Ok(result)
505 }
506
507 fn mark_seeds(&mut self, seeds: &[u32]) -> Result<u8> {
510 let stamp = self.marks.next(self.store.ids.len());
511 self.tick(seeds.len())?;
512 for &seed in seeds {
513 self.marks.seen[seed as usize] = stamp;
514 self.marks.selected[seed as usize] = stamp;
515 }
516 Ok(stamp)
517 }
518
519 fn has_seed_above(
522 &mut self,
523 concept: u32,
524 stamp: u8,
525 stack: &mut Vec<(u32, usize)>,
526 ) -> Result<bool> {
527 let store = self.store;
528 let parents = store.parents.get(concept);
529 self.tick(1 + parents.len())?;
530 for &parent in parents {
531 if self.marks.seen[parent as usize] != stamp {
532 self.marks.seen[parent as usize] = stamp;
533 stack.push((parent, 0));
534 while let Some((node, next)) = stack.last_mut() {
535 let Some(&up) = store.parents.get(*node).get(*next) else {
536 stack.pop();
538 continue;
539 };
540 *next += 1;
541 self.tick(1)?;
542 if self.marks.seen[up as usize] != stamp {
543 self.marks.seen[up as usize] = stamp;
544 stack.push((up, 0));
545 } else if self.marks.selected[up as usize] == stamp {
546 for (node, _) in stack.drain(..) {
548 self.marks.selected[node as usize] = stamp;
549 }
550 }
551 }
552 }
553 if self.marks.selected[parent as usize] == stamp {
554 return Ok(true);
555 }
556 }
557 Ok(false)
558 }
559 fn within(&mut self, set: Vec<u32>, bound: &[u32]) -> Result<Vec<u32>> {
561 self.tick(set.len().min(bound.len()) + bound.len())?;
562 let tested = refinement::intersect(&set, bound);
563 self.release(set);
564 self.claim(tested.capacity())?;
565 Ok(tested)
566 }
567 fn merge(&mut self, left: &[u32], right: &[u32], mode: u8) -> Result<Vec<u32>> {
568 self.tick(left.len() + right.len())?;
569 let capacity = match mode {
570 0 => left.len().min(right.len()),
571 1 => (left.len() + right.len()).min(self.store.ids.len()),
572 _ => left.len(),
573 };
574 let mut result = self.reserve(capacity)?;
575 let (mut a, mut b) = (0, 0);
576 while a < left.len() || b < right.len() {
577 if b == right.len() || a < left.len() && left[a] < right[b] {
578 if mode != 0 {
579 result.push(left[a]);
580 }
581 a += 1;
582 } else if a == left.len() || right[b] < left[a] {
583 if mode == 1 {
584 result.push(right[b]);
585 }
586 b += 1;
587 } else {
588 if mode != 2 {
589 result.push(left[a]);
590 }
591 a += 1;
592 b += 1;
593 }
594 }
595 Ok(result)
596 }
597}