1use crate::interning::{InternedString, StringInterner};
8use crate::model::*;
9use bumpalo::Bump;
10use crossbeam::epoch::{self, Atomic, Owned};
11use crossbeam::queue::SegQueue;
12use dashmap::DashMap;
13use parking_lot::RwLock;
14#[cfg(feature = "parallel")]
15use rayon::iter::IntoParallelRefIterator;
16#[cfg(feature = "parallel")]
17use rayon::iter::ParallelIterator;
18use simd_json;
19use std::collections::{BTreeSet, HashMap};
20use std::pin::Pin;
21use std::sync::atomic::Ordering;
22use std::sync::Arc;
23
24pub type TermInterner = StringInterner;
26
27pub trait TermInternerExt {
29 fn intern_named_node(&self, iri: &str) -> Result<NamedNode, crate::OxirsError>;
31
32 fn intern_blank_node(&self) -> BlankNode;
34
35 fn intern_literal(&self, value: &str) -> Result<Literal, crate::OxirsError>;
37
38 fn intern_literal_with_datatype(
40 &self,
41 value: &str,
42 datatype_iri: &str,
43 ) -> Result<Literal, crate::OxirsError>;
44
45 fn intern_literal_with_language(
47 &self,
48 value: &str,
49 language: &str,
50 ) -> Result<Literal, crate::OxirsError>;
51}
52
53impl TermInternerExt for TermInterner {
54 fn intern_named_node(&self, iri: &str) -> Result<NamedNode, crate::OxirsError> {
55 let interned = self.intern(iri);
57 NamedNode::new(interned.as_ref())
59 }
60
61 fn intern_blank_node(&self) -> BlankNode {
62 BlankNode::new_unique()
64 }
65
66 fn intern_literal(&self, value: &str) -> Result<Literal, crate::OxirsError> {
67 let interned = self.intern(value);
69 Ok(Literal::new_simple_literal(interned.as_ref()))
71 }
72
73 fn intern_literal_with_datatype(
74 &self,
75 value: &str,
76 datatype_iri: &str,
77 ) -> Result<Literal, crate::OxirsError> {
78 let value_interned = self.intern(value);
80 let datatype_interned = self.intern(datatype_iri);
81 let datatype_node = NamedNode::new(datatype_interned.as_ref())?;
83 Ok(Literal::new_typed_literal(
84 value_interned.as_ref(),
85 datatype_node,
86 ))
87 }
88
89 fn intern_literal_with_language(
90 &self,
91 value: &str,
92 language: &str,
93 ) -> Result<Literal, crate::OxirsError> {
94 let value_interned = self.intern(value);
96 let language_interned = self.intern(language);
97 let literal = Literal::new_language_tagged_literal(
99 value_interned.as_ref(),
100 language_interned.as_ref(),
101 )?;
102 Ok(literal)
103 }
104}
105
106#[derive(Debug)]
110pub struct RdfArena {
111 arena: std::sync::Mutex<Bump>,
113 interner: StringInterner,
115 allocated_bytes: std::sync::atomic::AtomicUsize,
117 allocation_count: std::sync::atomic::AtomicUsize,
118}
119
120impl RdfArena {
121 pub fn new() -> Self {
123 RdfArena {
124 arena: std::sync::Mutex::new(Bump::new()),
125 interner: StringInterner::new(),
126 allocated_bytes: std::sync::atomic::AtomicUsize::new(0),
127 allocation_count: std::sync::atomic::AtomicUsize::new(0),
128 }
129 }
130
131 pub fn with_capacity(capacity: usize) -> Self {
133 RdfArena {
134 arena: std::sync::Mutex::new(Bump::with_capacity(capacity)),
135 interner: StringInterner::new(),
136 allocated_bytes: std::sync::atomic::AtomicUsize::new(0),
137 allocation_count: std::sync::atomic::AtomicUsize::new(0),
138 }
139 }
140
141 pub fn alloc_str(&self, s: &str) -> String {
143 self.allocated_bytes
146 .fetch_add(s.len(), std::sync::atomic::Ordering::Relaxed);
147 self.allocation_count
148 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
149 s.to_string()
150 }
151
152 pub fn intern_str(&self, s: &str) -> InternedString {
154 InternedString::new_with_interner(s, &self.interner)
155 }
156
157 pub fn reset(&self) {
159 if let Ok(mut arena) = self.arena.lock() {
160 arena.reset();
161 self.allocated_bytes
162 .store(0, std::sync::atomic::Ordering::Relaxed);
163 self.allocation_count
164 .store(0, std::sync::atomic::Ordering::Relaxed);
165 }
166 }
167
168 pub fn allocated_bytes(&self) -> usize {
170 self.allocated_bytes
171 .load(std::sync::atomic::Ordering::Relaxed)
172 }
173
174 pub fn allocation_count(&self) -> usize {
176 self.allocation_count
177 .load(std::sync::atomic::Ordering::Relaxed)
178 }
179}
180
181impl Default for RdfArena {
182 fn default() -> Self {
183 Self::new()
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum TermRef<'a> {
192 NamedNode(&'a str),
193 BlankNode(&'a str),
194 Literal(&'a str, Option<&'a str>, Option<&'a str>), Variable(&'a str),
196}
197
198impl<'a> TermRef<'a> {
199 pub fn from_named_node(node: &'a NamedNode) -> Self {
201 TermRef::NamedNode(node.as_str())
202 }
203
204 pub fn from_blank_node(node: &'a BlankNode) -> Self {
206 TermRef::BlankNode(node.as_str())
207 }
208
209 pub fn from_literal(literal: &'a Literal) -> Self {
211 let language = literal.language();
212 TermRef::Literal(literal.value(), None, language)
215 }
216
217 pub fn as_str(&self) -> &'a str {
219 match self {
220 TermRef::NamedNode(s) => s,
221 TermRef::BlankNode(s) => s,
222 TermRef::Literal(s, _, _) => s,
223 TermRef::Variable(s) => s,
224 }
225 }
226
227 pub fn to_owned(&self) -> Result<Term, crate::OxirsError> {
229 match self {
230 TermRef::NamedNode(iri) => NamedNode::new(*iri).map(Term::NamedNode),
231 TermRef::BlankNode(id) => BlankNode::new(*id).map(Term::BlankNode),
232 TermRef::Literal(value, datatype, language) => {
233 let literal = if let Some(lang) = language {
234 Literal::new_lang(*value, *lang)?
235 } else if let Some(dt) = datatype {
236 let dt_node = NamedNode::new(*dt)?;
237 Literal::new_typed(*value, dt_node)
238 } else {
239 Literal::new(*value)
240 };
241 Ok(Term::Literal(literal))
242 }
243 TermRef::Variable(name) => Variable::new(*name).map(Term::Variable),
244 }
245 }
246
247 pub fn is_named_node(&self) -> bool {
249 matches!(self, TermRef::NamedNode(_))
250 }
251
252 pub fn is_blank_node(&self) -> bool {
254 matches!(self, TermRef::BlankNode(_))
255 }
256
257 pub fn is_literal(&self) -> bool {
259 matches!(self, TermRef::Literal(_, _, _))
260 }
261
262 pub fn is_variable(&self) -> bool {
264 matches!(self, TermRef::Variable(_))
265 }
266}
267
268impl<'a> std::fmt::Display for TermRef<'a> {
269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
270 match self {
271 TermRef::NamedNode(iri) => write!(f, "<{iri}>"),
272 TermRef::BlankNode(id) => write!(f, "{id}"),
273 TermRef::Literal(value, datatype, language) => {
274 write!(f, "\"{value}\"")?;
275 if let Some(lang) = language {
276 write!(f, "@{lang}")?;
277 } else if let Some(dt) = datatype {
278 write!(f, "^^<{dt}>")?;
279 }
280 Ok(())
281 }
282 TermRef::Variable(name) => write!(f, "?{name}"),
283 }
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
289pub struct TripleRef<'a> {
290 pub subject: TermRef<'a>,
291 pub predicate: TermRef<'a>,
292 pub object: TermRef<'a>,
293}
294
295impl<'a> TripleRef<'a> {
296 pub fn new(subject: TermRef<'a>, predicate: TermRef<'a>, object: TermRef<'a>) -> Self {
298 TripleRef {
299 subject,
300 predicate,
301 object,
302 }
303 }
304
305 pub fn from_triple(triple: &'a Triple) -> Self {
321 TripleRef {
322 subject: match triple.subject() {
323 Subject::NamedNode(n) => TermRef::NamedNode(n.as_str()),
324 Subject::BlankNode(b) => TermRef::BlankNode(b.as_str()),
325 Subject::Variable(v) => TermRef::Variable(v.as_str()),
326 Subject::QuotedTriple(_) => TermRef::NamedNode("<<quoted-triple>>"),
327 },
328 predicate: match triple.predicate() {
329 Predicate::NamedNode(n) => TermRef::NamedNode(n.as_str()),
330 Predicate::Variable(v) => TermRef::Variable(v.as_str()),
331 },
332 object: match triple.object() {
333 Object::NamedNode(n) => TermRef::NamedNode(n.as_str()),
334 Object::BlankNode(b) => TermRef::BlankNode(b.as_str()),
335 Object::Literal(l) => TermRef::from_literal(l),
336 Object::Variable(v) => TermRef::Variable(v.as_str()),
337 Object::QuotedTriple(_) => TermRef::NamedNode("<<quoted-triple>>"),
338 },
339 }
340 }
341
342 pub fn to_owned(&self) -> Result<Triple, crate::OxirsError> {
344 let subject = match self.subject.to_owned()? {
345 Term::NamedNode(n) => Subject::NamedNode(n),
346 Term::BlankNode(b) => Subject::BlankNode(b),
347 _ => return Err(crate::OxirsError::Parse("Invalid subject term".to_string())),
348 };
349
350 let predicate = match self.predicate.to_owned()? {
351 Term::NamedNode(n) => Predicate::NamedNode(n),
352 _ => {
353 return Err(crate::OxirsError::Parse(
354 "Invalid predicate term".to_string(),
355 ))
356 }
357 };
358
359 let object = match self.object.to_owned()? {
360 Term::NamedNode(n) => Object::NamedNode(n),
361 Term::BlankNode(b) => Object::BlankNode(b),
362 Term::Literal(l) => Object::Literal(l),
363 _ => return Err(crate::OxirsError::Parse("Invalid object term".to_string())),
364 };
365
366 Ok(Triple::new(subject, predicate, object))
367 }
368}
369
370impl<'a> std::fmt::Display for TripleRef<'a> {
371 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
372 write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
373 }
374}
375
376#[derive(Debug)]
378pub struct LockFreeGraph {
379 data: Atomic<GraphData>,
381 epoch: epoch::Guard,
383}
384
385#[derive(Debug)]
387struct GraphData {
388 triples: BTreeSet<Triple>,
390 version: u64,
392}
393
394impl LockFreeGraph {
395 pub fn new() -> Self {
397 let initial_data = GraphData {
398 triples: BTreeSet::new(),
399 version: 0,
400 };
401
402 LockFreeGraph {
403 data: Atomic::new(initial_data),
404 epoch: epoch::pin(),
405 }
406 }
407
408 pub fn insert(&self, triple: Triple) -> bool {
410 loop {
411 let current = self.data.load(Ordering::Acquire, &self.epoch);
412 let current_ref = unsafe { current.deref() };
413
414 if current_ref.triples.contains(&triple) {
416 return false;
417 }
418
419 let mut new_triples = current_ref.triples.clone();
421 new_triples.insert(triple.clone());
422
423 let new_data = GraphData {
424 triples: new_triples,
425 version: current_ref.version + 1,
426 };
427
428 match self.data.compare_exchange_weak(
430 current,
431 Owned::new(new_data),
432 Ordering::Release,
433 Ordering::Relaxed,
434 &self.epoch,
435 ) {
436 Ok(_) => {
437 unsafe {
439 self.epoch.defer_destroy(current);
440 }
441 return true;
442 }
443 Err(_) => {
444 continue;
446 }
447 }
448 }
449 }
450
451 pub fn len(&self) -> usize {
453 let current = self.data.load(Ordering::Acquire, &self.epoch);
454 unsafe { current.deref().triples.len() }
455 }
456
457 pub fn is_empty(&self) -> bool {
459 self.len() == 0
460 }
461
462 pub fn contains(&self, triple: &Triple) -> bool {
464 let current = self.data.load(Ordering::Acquire, &self.epoch);
465 unsafe { current.deref().triples.contains(triple) }
466 }
467}
468
469impl Default for LockFreeGraph {
470 fn default() -> Self {
471 Self::new()
472 }
473}
474
475#[derive(Debug)]
477pub struct OptimizedGraph {
478 spo: DashMap<InternedString, DashMap<InternedString, BTreeSet<InternedString>>>,
480 pos: DashMap<InternedString, DashMap<InternedString, BTreeSet<InternedString>>>,
482 osp: DashMap<InternedString, DashMap<InternedString, BTreeSet<InternedString>>>,
484 interner: Arc<StringInterner>,
486 stats: Arc<RwLock<GraphStats>>,
488}
489
490#[derive(Debug, Clone, Default)]
492pub struct GraphStats {
493 pub triple_count: usize,
494 pub unique_subjects: usize,
495 pub unique_predicates: usize,
496 pub unique_objects: usize,
497 pub index_memory_usage: usize,
498 pub intern_hit_ratio: f64,
499}
500
501impl OptimizedGraph {
502 pub fn new() -> Self {
504 OptimizedGraph {
505 spo: DashMap::new(),
506 pos: DashMap::new(),
507 osp: DashMap::new(),
508 interner: Arc::new(StringInterner::new()),
509 stats: Arc::new(RwLock::new(GraphStats::default())),
510 }
511 }
512
513 pub fn insert(&self, triple: &Triple) -> bool {
515 let subject = self.intern_subject(triple.subject());
516 let predicate = self.intern_predicate(triple.predicate());
517 let object = self.intern_object(triple.object());
518
519 let spo_entry = self.spo.entry(subject.clone()).or_default();
521 let mut po_entry = spo_entry.entry(predicate.clone()).or_default();
522 let was_new = po_entry.insert(object.clone());
523
524 if was_new {
525 let pos_entry = self.pos.entry(predicate.clone()).or_default();
527 let mut os_entry = pos_entry.entry(object.clone()).or_default();
528 os_entry.insert(subject.clone());
529
530 let osp_entry = self.osp.entry(object.clone()).or_default();
532 let mut sp_entry = osp_entry.entry(subject.clone()).or_default();
533 sp_entry.insert(predicate);
534
535 {
537 let mut stats = self.stats.write();
538 stats.triple_count += 1;
539 stats.intern_hit_ratio = self.interner.stats().hit_ratio();
540 }
541 }
542
543 was_new
544 }
545
546 pub fn query(
548 &self,
549 subject: Option<&Subject>,
550 predicate: Option<&Predicate>,
551 object: Option<&Object>,
552 ) -> Vec<Triple> {
553 let mut results = Vec::new();
554
555 match (subject.is_some(), predicate.is_some(), object.is_some()) {
557 (true, true, true) => {
558 if let (Some(s), Some(p), Some(o)) = (subject, predicate, object) {
560 let s_intern = self.intern_subject(s);
561 let p_intern = self.intern_predicate(p);
562 let o_intern = self.intern_object(o);
563
564 if let Some(po_map) = self.spo.get(&s_intern) {
565 if let Some(o_set) = po_map.get(&p_intern) {
566 if o_set.contains(&o_intern) {
567 let triple = Triple::new(s.clone(), p.clone(), o.clone());
568 results.push(triple);
569 }
570 }
571 }
572 }
573 }
574 (true, true, false) => {
575 if let (Some(s), Some(p)) = (subject, predicate) {
577 let s_intern = self.intern_subject(s);
578 let p_intern = self.intern_predicate(p);
579
580 if let Some(po_map) = self.spo.get(&s_intern) {
581 if let Some(o_set) = po_map.get(&p_intern) {
582 for o_intern in o_set.iter() {
583 if let Ok(object) = self.unintern_object(o_intern) {
584 let triple = Triple::new(s.clone(), p.clone(), object);
585 results.push(triple);
586 }
587 }
588 }
589 }
590 }
591 }
592 (false, true, true) => {
593 if let (Some(p), Some(o)) = (predicate, object) {
595 let p_intern = self.intern_predicate(p);
596 let o_intern = self.intern_object(o);
597
598 if let Some(os_map) = self.pos.get(&p_intern) {
599 if let Some(s_set) = os_map.get(&o_intern) {
600 for s_intern in s_set.iter() {
601 if let Ok(subject) = self.unintern_subject(s_intern) {
602 let triple = Triple::new(subject, p.clone(), o.clone());
603 results.push(triple);
604 }
605 }
606 }
607 }
608 }
609 }
610 _ => {
611 for s_entry in &self.spo {
613 let s_intern = s_entry.key();
614 if let Ok(s) = self.unintern_subject(s_intern) {
615 if let Some(subj) = subject {
616 if subj != &s {
617 continue;
618 }
619 }
620
621 for po_entry in s_entry.value().iter() {
622 let p_intern = po_entry.key();
623 if let Ok(p) = self.unintern_predicate(p_intern) {
624 if let Some(pred) = predicate {
625 if pred != &p {
626 continue;
627 }
628 }
629
630 for o_intern in po_entry.value().iter() {
631 if let Ok(o) = self.unintern_object(o_intern) {
632 if let Some(obj) = object {
633 if obj != &o {
634 continue;
635 }
636 }
637
638 let triple = Triple::new(s.clone(), p.clone(), o);
639 results.push(triple);
640 }
641 }
642 }
643 }
644 }
645 }
646 }
647 }
648
649 results
650 }
651
652 pub fn stats(&self) -> GraphStats {
654 self.stats.read().clone()
655 }
656
657 fn intern_subject(&self, subject: &Subject) -> InternedString {
659 match subject {
660 Subject::NamedNode(n) => InternedString::new_with_interner(n.as_str(), &self.interner),
661 Subject::BlankNode(b) => InternedString::new_with_interner(b.as_str(), &self.interner),
662 Subject::Variable(v) => InternedString::new_with_interner(v.as_str(), &self.interner),
663 Subject::QuotedTriple(qt) => {
664 let serialized = format!("{qt}");
675 InternedString::new_with_interner(&serialized, &self.interner)
676 }
677 }
678 }
679
680 fn intern_predicate(&self, predicate: &Predicate) -> InternedString {
682 match predicate {
683 Predicate::NamedNode(n) => {
684 InternedString::new_with_interner(n.as_str(), &self.interner)
685 }
686 Predicate::Variable(v) => InternedString::new_with_interner(v.as_str(), &self.interner),
687 }
688 }
689
690 fn intern_object(&self, object: &Object) -> InternedString {
692 match object {
693 Object::NamedNode(n) => InternedString::new_with_interner(n.as_str(), &self.interner),
694 Object::BlankNode(b) => InternedString::new_with_interner(b.as_str(), &self.interner),
695 Object::Literal(l) => {
696 let serialized = format!("{l}");
698 InternedString::new_with_interner(&serialized, &self.interner)
699 }
700 Object::Variable(v) => InternedString::new_with_interner(v.as_str(), &self.interner),
701 Object::QuotedTriple(qt) => {
702 let serialized = format!("{qt}");
707 InternedString::new_with_interner(&serialized, &self.interner)
708 }
709 }
710 }
711
712 fn unintern_subject(&self, interned: &InternedString) -> Result<Subject, crate::OxirsError> {
714 let s = interned.as_str();
715 if s.starts_with("?") || s.starts_with("$") {
716 Variable::new(&s[1..]).map(Subject::Variable)
717 } else if s.starts_with("_:") {
718 BlankNode::new(s).map(Subject::BlankNode)
719 } else {
720 NamedNode::new(s).map(Subject::NamedNode)
721 }
722 }
723
724 fn unintern_predicate(
726 &self,
727 interned: &InternedString,
728 ) -> Result<Predicate, crate::OxirsError> {
729 let s = interned.as_str();
730 if s.starts_with("?") || s.starts_with("$") {
731 Variable::new(&s[1..]).map(Predicate::Variable)
732 } else {
733 NamedNode::new(s).map(Predicate::NamedNode)
734 }
735 }
736
737 fn unintern_object(&self, interned: &InternedString) -> Result<Object, crate::OxirsError> {
739 let s = interned.as_str();
740 if s.starts_with("?") || s.starts_with("$") {
741 return Variable::new(&s[1..]).map(Object::Variable);
742 } else if let Some(stripped) = s.strip_prefix("\"") {
743 if let Some(end_quote) = stripped.find('"') {
745 let value = &stripped[..end_quote];
746 return Ok(Object::Literal(Literal::new(value)));
747 }
748 return Ok(Object::Literal(Literal::new(s)));
750 }
751
752 if s.starts_with("_:") {
753 BlankNode::new(s).map(Object::BlankNode)
754 } else {
755 NamedNode::new(s).map(Object::NamedNode)
756 }
757 }
758}
759
760impl Default for OptimizedGraph {
761 fn default() -> Self {
762 Self::new()
763 }
764}
765
766#[cfg(feature = "parallel")]
768#[derive(Debug)]
769pub struct BatchProcessor {
770 operation_queue: SegQueue<BatchOperation>,
772 processing_pool: rayon::ThreadPool,
774 stats: Arc<RwLock<BatchStats>>,
776}
777
778#[derive(Debug, Clone)]
780pub enum BatchOperation {
781 Insert(Quad),
782 Delete(Quad),
783 Update { old: Quad, new: Quad },
784 Compact,
785}
786
787#[derive(Debug, Default, Clone)]
789pub struct BatchStats {
790 pub operations_processed: usize,
791 pub batch_size: usize,
792 pub processing_time_ms: u64,
793 pub throughput_ops_per_sec: f64,
794}
795
796#[cfg(feature = "parallel")]
797impl BatchProcessor {
798 pub fn new(num_threads: usize) -> Self {
800 let pool = rayon::ThreadPoolBuilder::new()
801 .num_threads(num_threads)
802 .build()
803 .expect("thread pool builder should succeed");
804
805 BatchProcessor {
806 operation_queue: SegQueue::new(),
807 processing_pool: pool,
808 stats: Arc::new(RwLock::new(BatchStats::default())),
809 }
810 }
811
812 pub fn push(&self, operation: BatchOperation) {
814 self.operation_queue.push(operation);
815 }
816
817 pub fn process_batch(&self, batch_size: usize) -> Result<usize, crate::OxirsError> {
819 let start_time = std::time::Instant::now();
820 let mut operations = Vec::with_capacity(batch_size);
821
822 for _ in 0..batch_size {
824 if let Some(op) = self.operation_queue.pop() {
825 operations.push(op);
826 } else {
827 break;
828 }
829 }
830
831 if operations.is_empty() {
832 return Ok(0);
833 }
834
835 let operations_count = operations.len();
836
837 self.processing_pool.install(|| {
839 operations.par_iter().for_each(|operation| {
840 match operation {
841 BatchOperation::Insert(_quad) => {
842 }
844 BatchOperation::Delete(_quad) => {
845 }
847 BatchOperation::Update {
848 old: _old,
849 new: _new,
850 } => {
851 }
853 BatchOperation::Compact => {
854 }
856 }
857 });
858 });
859
860 let processing_time = start_time.elapsed();
862 {
863 let mut stats = self.stats.write();
864 stats.operations_processed += operations_count;
865 stats.batch_size = batch_size;
866 stats.processing_time_ms = processing_time.as_millis() as u64;
867 if processing_time.as_secs_f64() > 0.0 {
868 stats.throughput_ops_per_sec =
869 operations_count as f64 / processing_time.as_secs_f64();
870 }
871 }
872
873 Ok(operations_count)
874 }
875
876 pub fn stats(&self) -> BatchStats {
878 self.stats.read().clone()
879 }
880
881 pub fn pending_operations(&self) -> usize {
883 self.operation_queue.len()
884 }
885}
886
887#[cfg(feature = "parallel")]
888impl Default for BatchProcessor {
889 fn default() -> Self {
890 Self::new(
891 std::thread::available_parallelism()
892 .map(|n| n.get())
893 .unwrap_or(1),
894 )
895 }
896}
897
898pub mod simd {
900 #[cfg(feature = "simd")]
901 use wide::u8x32;
902
903 #[cfg(feature = "simd")]
905 pub fn validate_iri_fast(iri: &str) -> bool {
906 if iri.is_empty() {
907 return false;
908 }
909
910 let bytes = iri.as_bytes();
911 let len = bytes.len();
912
913 let chunks = len / 32;
915 let _remainder = len % 32;
916
917 for i in 0..chunks {
918 let start = i * 32;
919 let chunk = &bytes[start..start + 32];
920
921 let data = u8x32::from([
923 chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
924 chunk[8], chunk[9], chunk[10], chunk[11], chunk[12], chunk[13], chunk[14],
925 chunk[15], chunk[16], chunk[17], chunk[18], chunk[19], chunk[20], chunk[21],
926 chunk[22], chunk[23], chunk[24], chunk[25], chunk[26], chunk[27], chunk[28],
927 chunk[29], chunk[30], chunk[31],
928 ]);
929
930 let forbidden_chars = [b'<', b'>', b'"', b'{', b'}', b'|', b'\\', b'^', b'`', b' '];
932
933 for &forbidden in &forbidden_chars {
934 let forbidden_vec = u8x32::splat(forbidden);
935 let matches = data.simd_eq(forbidden_vec);
936 if matches.any() {
937 return false;
938 }
939 }
940
941 for &byte in chunk {
943 if matches!(byte, 0..=31 | 127..=159) {
944 return false;
945 }
946 }
947 }
948
949 for &byte in &bytes[chunks * 32..] {
951 if matches!(byte,
952 0..=31 | 127..=159 | b'<' | b'>' | b'"' | b'{' | b'}' | b'|' | b'\\' | b'^' | b'`' | b' ' ) {
955 return false;
956 }
957 }
958
959 true
960 }
961
962 #[cfg(not(feature = "simd"))]
964 pub fn validate_iri_fast(iri: &str) -> bool {
965 if iri.is_empty() {
966 return false;
967 }
968
969 for byte in iri.bytes() {
970 if matches!(
971 byte,
972 b'<' | b'>' | b'"' | b'{' | b'}' | b'|' | b'\\' | b'^' | b'`' | b' ' ) {
974 return false;
975 }
976 }
977
978 true
979 }
980
981 pub fn compare_strings_fast(a: &str, b: &str) -> std::cmp::Ordering {
983 if a.len() != b.len() {
984 return a.len().cmp(&b.len());
985 }
986
987 let a_bytes = a.as_bytes();
988 let b_bytes = b.as_bytes();
989 let len = a_bytes.len();
990
991 let chunks = len / 32;
993
994 for i in 0..chunks {
995 let start = i * 32;
996 let a_chunk = &a_bytes[start..start + 32];
997 let b_chunk = &b_bytes[start..start + 32];
998
999 for j in 0..32 {
1001 match a_chunk[j].cmp(&b_chunk[j]) {
1002 std::cmp::Ordering::Equal => continue,
1003 other => return other,
1004 }
1005 }
1006 }
1007
1008 for i in chunks * 32..len {
1010 match a_bytes[i].cmp(&b_bytes[i]) {
1011 std::cmp::Ordering::Equal => continue,
1012 other => return other,
1013 }
1014 }
1015
1016 std::cmp::Ordering::Equal
1017 }
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use super::*;
1023
1024 #[test]
1025 fn test_rdf_arena() {
1026 let arena = RdfArena::new();
1027
1028 let s1 = arena.alloc_str("test string 1");
1029 let s2 = arena.alloc_str("test string 2");
1030
1031 assert_eq!(s1, "test string 1");
1032 assert_eq!(s2, "test string 2");
1033 assert!(arena.allocated_bytes() > 0);
1034 assert_eq!(arena.allocation_count(), 2);
1035 }
1036
1037 #[test]
1038 fn test_term_ref() {
1039 let node = NamedNode::new("http://example.org/test").expect("valid IRI");
1040 let term_ref = TermRef::from_named_node(&node);
1041
1042 assert!(term_ref.is_named_node());
1043 assert_eq!(term_ref.as_str(), "http://example.org/test");
1044
1045 let owned = term_ref.to_owned().expect("operation should succeed");
1046 assert!(owned.is_named_node());
1047 }
1048
1049 #[test]
1050 fn test_triple_ref() {
1051 let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
1052 let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
1053 let object = Literal::new("test object");
1054 let triple = Triple::new(subject, predicate, object);
1055
1056 let triple_ref = TripleRef::from_triple(&triple);
1057 assert!(triple_ref.subject.is_named_node());
1058 assert!(triple_ref.predicate.is_named_node());
1059 assert!(triple_ref.object.is_literal());
1060
1061 let owned = triple_ref.to_owned().expect("operation should succeed");
1062 assert_eq!(owned, triple);
1063 }
1064
1065 #[test]
1066 fn test_lock_free_graph() {
1067 let graph = LockFreeGraph::new();
1068 assert!(graph.is_empty());
1069
1070 let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
1071 let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
1072 let object = Literal::new("test object");
1073 let triple = Triple::new(subject, predicate, object);
1074
1075 assert!(graph.insert(triple.clone()));
1076 assert!(!graph.insert(triple.clone())); assert_eq!(graph.len(), 1);
1078 assert!(graph.contains(&triple));
1079 }
1080
1081 #[test]
1082 fn test_optimized_graph() {
1083 let graph = OptimizedGraph::new();
1084
1085 let subject = NamedNode::new("http://example.org/s").expect("valid IRI");
1086 let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
1087 let object = Literal::new("test object");
1088 let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
1089
1090 assert!(graph.insert(&triple));
1091 assert!(!graph.insert(&triple)); let results = graph.query(
1095 Some(&Subject::NamedNode(subject.clone())),
1096 Some(&Predicate::NamedNode(predicate.clone())),
1097 Some(&Object::Literal(object.clone())),
1098 );
1099 assert_eq!(results.len(), 1);
1100 assert_eq!(results[0], triple);
1101
1102 let results = graph.query(Some(&Subject::NamedNode(subject)), None, None);
1104 assert_eq!(results.len(), 1);
1105
1106 let stats = graph.stats();
1107 assert_eq!(stats.triple_count, 1);
1108 }
1109
1110 #[test]
1117 fn regression_optimized_graph_distinguishes_quoted_triple_subjects() {
1118 let graph = OptimizedGraph::new();
1119
1120 let inner1 = Triple::new(
1121 NamedNode::new("http://example.org/a1").expect("valid IRI"),
1122 NamedNode::new("http://example.org/rel").expect("valid IRI"),
1123 NamedNode::new("http://example.org/b1").expect("valid IRI"),
1124 );
1125 let inner2 = Triple::new(
1126 NamedNode::new("http://example.org/a2").expect("valid IRI"),
1127 NamedNode::new("http://example.org/rel").expect("valid IRI"),
1128 NamedNode::new("http://example.org/b2").expect("valid IRI"),
1129 );
1130 assert_ne!(inner1, inner2, "test fixture triples must differ");
1131
1132 let predicate = NamedNode::new("http://example.org/certainty").expect("valid IRI");
1133 let object = Literal::new("high");
1134
1135 let triple1 = Triple::new(
1136 Subject::QuotedTriple(Box::new(QuotedTriple::new(inner1))),
1137 predicate.clone(),
1138 object.clone(),
1139 );
1140 let triple2 = Triple::new(
1141 Subject::QuotedTriple(Box::new(QuotedTriple::new(inner2))),
1142 predicate,
1143 object,
1144 );
1145
1146 assert!(graph.insert(&triple1));
1150 assert!(
1151 graph.insert(&triple2),
1152 "a second, distinct quoted-triple subject must not be treated as a duplicate"
1153 );
1154
1155 let stats = graph.stats();
1156 assert_eq!(stats.triple_count, 2);
1157 }
1158
1159 #[test]
1160 fn test_simd_iri_validation() {
1161 assert!(simd::validate_iri_fast("http://example.org/test"));
1162 assert!(!simd::validate_iri_fast("http://example.org/<invalid>"));
1163 assert!(!simd::validate_iri_fast(""));
1164 assert!(!simd::validate_iri_fast(
1165 "http://example.org/test with spaces"
1166 ));
1167 }
1168
1169 #[test]
1170 fn test_simd_string_comparison() {
1171 assert_eq!(
1172 simd::compare_strings_fast("abc", "abc"),
1173 std::cmp::Ordering::Equal
1174 );
1175 assert_eq!(
1176 simd::compare_strings_fast("abc", "def"),
1177 std::cmp::Ordering::Less
1178 );
1179 assert_eq!(
1180 simd::compare_strings_fast("def", "abc"),
1181 std::cmp::Ordering::Greater
1182 );
1183 assert_eq!(
1184 simd::compare_strings_fast("short", "longer"),
1185 std::cmp::Ordering::Less
1186 );
1187 }
1188
1189 #[test]
1190 fn test_arena_reset() {
1191 let arena = RdfArena::new();
1192
1193 arena.alloc_str("test");
1194 assert!(arena.allocated_bytes() > 0);
1195
1196 arena.reset();
1197 assert_eq!(arena.allocated_bytes(), 0);
1198 assert_eq!(arena.allocation_count(), 0);
1199 }
1200
1201 #[test]
1202 fn test_concurrent_optimized_graph() {
1203 use std::sync::Arc;
1204 use std::thread;
1205
1206 let graph = Arc::new(OptimizedGraph::new());
1207 let handles: Vec<_> = (0..10)
1208 .map(|i| {
1209 let graph = Arc::clone(&graph);
1210 thread::spawn(move || {
1211 let subject = NamedNode::new(format!("http://example.org/s{i}"))
1212 .expect("valid IRI from format");
1213 let predicate = NamedNode::new("http://example.org/p").expect("valid IRI");
1214 let object = Literal::new(format!("object{i}"));
1215 let triple = Triple::new(subject, predicate, object);
1216
1217 graph.insert(&triple)
1218 })
1219 })
1220 .collect();
1221
1222 let results: Vec<bool> = handles
1223 .into_iter()
1224 .map(|h| h.join().expect("thread should not panic"))
1225 .collect();
1226 assert!(results.iter().all(|&inserted| inserted));
1227
1228 let stats = graph.stats();
1229 assert_eq!(stats.triple_count, 10);
1230 }
1231}
1232
1233pub struct ZeroCopyBuffer {
1235 data: Pin<Box<[u8]>>,
1236 len: usize,
1237}
1238
1239impl ZeroCopyBuffer {
1240 pub fn new(capacity: usize) -> Self {
1242 Self::with_capacity(capacity)
1243 }
1244
1245 pub fn with_capacity(capacity: usize) -> Self {
1247 let vec = vec![0; capacity];
1248 let data = vec.into_boxed_slice();
1249
1250 ZeroCopyBuffer {
1251 data: Pin::new(data),
1252 len: 0,
1253 }
1254 }
1255
1256 pub fn as_slice(&self) -> &[u8] {
1258 &self.data[..self.len]
1259 }
1260
1261 pub fn as_mut_slice(&mut self) -> &mut [u8] {
1263 &mut self.data[..]
1264 }
1265
1266 pub fn capacity(&self) -> usize {
1268 self.data.len()
1269 }
1270
1271 pub fn len(&self) -> usize {
1273 self.len
1274 }
1275
1276 pub fn is_empty(&self) -> bool {
1278 self.len == 0
1279 }
1280
1281 pub fn clear(&mut self) {
1283 self.len = 0;
1284 }
1285
1286 pub fn reset(&mut self) {
1288 self.clear();
1289 }
1290
1291 pub fn set_len(&mut self, len: usize) {
1293 assert!(len <= self.capacity());
1294 self.len = len;
1295 }
1296
1297 pub fn write(&mut self, data: &[u8]) -> Result<usize, std::io::Error> {
1299 let available = self.capacity() - self.len;
1300 let to_write = data.len().min(available);
1301
1302 if to_write == 0 {
1303 return Err(std::io::Error::new(
1304 std::io::ErrorKind::WriteZero,
1305 "Buffer is full",
1306 ));
1307 }
1308
1309 unsafe {
1311 let dst = self.data.as_mut_ptr().add(self.len);
1312 std::ptr::copy_nonoverlapping(data.as_ptr(), dst, to_write);
1313 }
1314
1315 self.len += to_write;
1316 Ok(to_write)
1317 }
1318}
1319
1320#[derive(Clone)]
1322pub struct SimdJsonProcessor;
1323
1324impl SimdJsonProcessor {
1325 pub fn new() -> Self {
1327 SimdJsonProcessor
1328 }
1329
1330 pub fn parse<'a>(
1332 &mut self,
1333 json: &'a mut [u8],
1334 ) -> Result<simd_json::BorrowedValue<'a>, simd_json::Error> {
1335 simd_json::to_borrowed_value(json)
1336 }
1337
1338 pub fn parse_str<'a>(
1340 &mut self,
1341 json: &'a mut str,
1342 ) -> Result<simd_json::BorrowedValue<'a>, simd_json::Error> {
1343 let bytes = unsafe { json.as_bytes_mut() };
1344 simd_json::to_borrowed_value(bytes)
1345 }
1346
1347 pub fn parse_owned(
1349 &mut self,
1350 json: &mut [u8],
1351 ) -> Result<simd_json::OwnedValue, simd_json::Error> {
1352 simd_json::to_owned_value(json)
1353 }
1354
1355 pub fn parse_json(&self, json: &[u8]) -> Result<serde_json::Value, serde_json::Error> {
1357 serde_json::from_slice(json)
1358 }
1359}
1360
1361impl Default for SimdJsonProcessor {
1362 fn default() -> Self {
1363 Self::new()
1364 }
1365}
1366
1367#[derive(Clone, Debug)]
1372pub struct SimdXmlProcessor {
1373 scan_buffer: Vec<u8>,
1375}
1376
1377impl SimdXmlProcessor {
1378 pub fn new() -> Self {
1380 SimdXmlProcessor {
1381 scan_buffer: Vec::with_capacity(4096),
1382 }
1383 }
1384
1385 #[cfg(target_arch = "x86_64")]
1388 pub fn find_special_char(&self, data: &[u8]) -> Option<usize> {
1389 use std::arch::x86_64::*;
1390
1391 const CHUNK_SIZE: usize = 16;
1393 let mut offset = 0;
1394
1395 if data.len() >= CHUNK_SIZE {
1396 unsafe {
1397 let lt = _mm_set1_epi8(b'<' as i8);
1399 let gt = _mm_set1_epi8(b'>' as i8);
1400 let amp = _mm_set1_epi8(b'&' as i8);
1401 let quot = _mm_set1_epi8(b'"' as i8);
1402 let apos = _mm_set1_epi8(b'\'' as i8);
1403
1404 while offset + CHUNK_SIZE <= data.len() {
1405 let chunk = _mm_loadu_si128(data.as_ptr().add(offset) as *const __m128i);
1406
1407 let eq_lt = _mm_cmpeq_epi8(chunk, lt);
1409 let eq_gt = _mm_cmpeq_epi8(chunk, gt);
1410 let eq_amp = _mm_cmpeq_epi8(chunk, amp);
1411 let eq_quot = _mm_cmpeq_epi8(chunk, quot);
1412 let eq_apos = _mm_cmpeq_epi8(chunk, apos);
1413
1414 let any_match = _mm_or_si128(
1416 _mm_or_si128(_mm_or_si128(eq_lt, eq_gt), eq_amp),
1417 _mm_or_si128(eq_quot, eq_apos),
1418 );
1419
1420 let mask = _mm_movemask_epi8(any_match);
1421 if mask != 0 {
1422 return Some(offset + mask.trailing_zeros() as usize);
1423 }
1424
1425 offset += CHUNK_SIZE;
1426 }
1427 }
1428 }
1429
1430 data[offset..]
1432 .iter()
1433 .position(|&b| matches!(b, b'<' | b'>' | b'&' | b'"' | b'\''))
1434 .map(|i| i + offset)
1435 }
1436
1437 #[cfg(not(target_arch = "x86_64"))]
1439 pub fn find_special_char(&self, data: &[u8]) -> Option<usize> {
1440 data.iter()
1441 .position(|&b| matches!(b, b'<' | b'>' | b'&' | b'"' | b'\''))
1442 }
1443
1444 pub fn is_valid_utf8(&self, data: &[u8]) -> bool {
1447 std::str::from_utf8(data).is_ok()
1448 }
1449
1450 pub fn trim_whitespace<'a>(&self, data: &'a [u8]) -> &'a [u8] {
1453 let start = data
1454 .iter()
1455 .position(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
1456 .unwrap_or(data.len());
1457 let end = data
1458 .iter()
1459 .rposition(|&b| !matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
1460 .map(|i| i + 1)
1461 .unwrap_or(0);
1462
1463 if start >= end {
1464 &[]
1465 } else {
1466 &data[start..end]
1467 }
1468 }
1469
1470 #[cfg(target_arch = "x86_64")]
1472 pub fn find_colon(&self, data: &[u8]) -> Option<usize> {
1473 use std::arch::x86_64::*;
1474
1475 const CHUNK_SIZE: usize = 16;
1476 let mut offset = 0;
1477
1478 if data.len() >= CHUNK_SIZE {
1479 unsafe {
1480 let colon = _mm_set1_epi8(b':' as i8);
1481
1482 while offset + CHUNK_SIZE <= data.len() {
1483 let chunk = _mm_loadu_si128(data.as_ptr().add(offset) as *const __m128i);
1484 let eq = _mm_cmpeq_epi8(chunk, colon);
1485 let mask = _mm_movemask_epi8(eq);
1486
1487 if mask != 0 {
1488 return Some(offset + mask.trailing_zeros() as usize);
1489 }
1490
1491 offset += CHUNK_SIZE;
1492 }
1493 }
1494 }
1495
1496 data[offset..]
1497 .iter()
1498 .position(|&b| b == b':')
1499 .map(|i| i + offset)
1500 }
1501
1502 #[cfg(not(target_arch = "x86_64"))]
1504 pub fn find_colon(&self, data: &[u8]) -> Option<usize> {
1505 data.iter().position(|&b| b == b':')
1506 }
1507
1508 pub fn parse_qname<'a>(&self, qname: &'a [u8]) -> (&'a [u8], &'a [u8]) {
1510 match self.find_colon(qname) {
1511 Some(pos) => (&qname[..pos], &qname[pos + 1..]),
1512 None => (&[], qname),
1513 }
1514 }
1515
1516 pub fn expand_name<'a>(
1518 &self,
1519 prefix: &'a [u8],
1520 local: &'a [u8],
1521 namespaces: &HashMap<String, String>,
1522 ) -> Option<String> {
1523 let prefix_str = std::str::from_utf8(prefix).ok()?;
1524 let local_str = std::str::from_utf8(local).ok()?;
1525
1526 namespaces
1527 .get(prefix_str)
1528 .map(|ns| format!("{}{}", ns, local_str))
1529 }
1530
1531 pub fn ensure_buffer_capacity(&mut self, capacity: usize) {
1533 if self.scan_buffer.capacity() < capacity {
1534 self.scan_buffer
1535 .reserve(capacity - self.scan_buffer.capacity());
1536 }
1537 }
1538}
1539
1540impl Default for SimdXmlProcessor {
1541 fn default() -> Self {
1542 Self::new()
1543 }
1544}