1use crate::{
7 jsonld::JsonLdParseError,
8 model::{NamedNode, Object, Predicate, Quad, Subject, Triple},
9 optimization::{SimdJsonProcessor, TermInterner, TermInternerExt, ZeroCopyBuffer},
10};
11use dashmap::DashMap;
13use parking_lot::Mutex;
15#[cfg(feature = "parallel")]
16use rayon::prelude::*;
17use serde_json::{Map, Value};
18use std::{
19 collections::VecDeque,
20 error::Error as StdError,
21 sync::{
22 atomic::{AtomicUsize, Ordering},
23 Arc,
24 },
25};
26use tokio::{
27 io::{AsyncRead, AsyncReadExt, BufReader},
28 sync::{mpsc, RwLock, Semaphore},
29 time::{Duration, Instant},
30};
31
32pub struct UltraStreamingJsonLdParser {
34 config: StreamingConfig,
35 context_cache: Arc<DashMap<String, Arc<Value>>>,
36 term_interner: Arc<TermInterner>,
37 performance_monitor: Arc<PerformanceMonitor>,
38 simd_processor: SimdJsonProcessor,
39 buffer_pool: Arc<BufferPool>,
40}
41
42#[derive(Debug, Clone)]
44pub struct StreamingConfig {
45 pub chunk_size: usize,
47 pub max_concurrent_threads: usize,
49 pub buffer_size: usize,
51 pub enable_simd: bool,
53 pub context_cache_size: usize,
55 pub adaptive_threshold: f64,
57 pub memory_pressure_threshold: usize,
59 pub zero_copy_level: ZeroCopyLevel,
61 pub enable_profiling: bool,
63}
64
65#[derive(Debug, Clone, Copy, PartialEq)]
67pub enum ZeroCopyLevel {
68 None,
70 Basic,
72 Advanced,
74 Maximum,
76}
77
78pub struct PerformanceMonitor {
80 total_bytes_processed: AtomicUsize,
81 total_triples_parsed: AtomicUsize,
82 parse_errors: AtomicUsize,
83 context_cache_hits: AtomicUsize,
84 context_cache_misses: AtomicUsize,
85 simd_operations: AtomicUsize,
86 zero_copy_operations: AtomicUsize,
87 start_time: Instant,
88 chunk_processing_times: Arc<Mutex<VecDeque<Duration>>>,
89}
90
91pub struct BufferPool {
93 available_buffers: Arc<Mutex<Vec<ZeroCopyBuffer>>>,
94 buffer_size: usize,
95 max_buffers: usize,
96 current_buffers: AtomicUsize,
97}
98
99#[async_trait::async_trait]
101pub trait StreamingSink: Send + Sync {
102 type Error: Send + Sync + std::error::Error + 'static;
103
104 async fn process_triple_batch(&mut self, triples: Vec<Triple>) -> Result<(), Self::Error>;
105 async fn process_quad_batch(&mut self, quads: Vec<Quad>) -> Result<(), Self::Error>;
106 async fn flush(&mut self) -> Result<(), Self::Error>;
107 fn performance_statistics(&self) -> SinkStatistics;
108}
109
110#[derive(Debug, Clone)]
112pub struct SinkStatistics {
113 pub total_triples_processed: usize,
114 pub total_quads_processed: usize,
115 pub average_batch_size: f64,
116 pub processing_rate_per_second: f64,
117 pub memory_usage_bytes: usize,
118}
119
120impl Default for StreamingConfig {
121 fn default() -> Self {
122 Self {
123 chunk_size: 64 * 1024, max_concurrent_threads: std::thread::available_parallelism()
125 .map(|n| n.get())
126 .unwrap_or(1)
127 * 2,
128 buffer_size: 1024 * 1024, enable_simd: true,
130 context_cache_size: 10000,
131 adaptive_threshold: 0.8,
132 memory_pressure_threshold: 8 * 1024 * 1024 * 1024, zero_copy_level: ZeroCopyLevel::Advanced,
134 enable_profiling: true,
135 }
136 }
137}
138
139impl UltraStreamingJsonLdParser {
140 pub fn new(config: StreamingConfig) -> Self {
142 Self {
143 context_cache: Arc::new(DashMap::with_capacity(config.context_cache_size)),
144 term_interner: Arc::new(TermInterner::new()),
145 performance_monitor: Arc::new(PerformanceMonitor::new()),
146 simd_processor: SimdJsonProcessor::new(),
147 buffer_pool: Arc::new(BufferPool::new(config.buffer_size, 100)),
148 config,
149 }
150 }
151
152 pub async fn stream_parse<R, S>(
154 &mut self,
155 reader: R,
156 mut sink: S,
157 ) -> Result<StreamingStatistics, JsonLdParseError>
158 where
159 R: AsyncRead + Unpin + Send + 'static,
160 S: StreamingSink + Send + 'static,
161 S::Error: 'static,
162 {
163 let mut buf_reader = BufReader::with_capacity(self.config.chunk_size, reader);
164 let (tx, mut rx) = mpsc::channel::<ProcessingChunk>(self.config.buffer_size);
165 let (triple_tx, mut triple_rx) = mpsc::channel::<Vec<Triple>>(100);
166 let semaphore = Arc::new(Semaphore::new(self.config.max_concurrent_threads));
167
168 let sink_handle = tokio::spawn(async move {
170 while let Some(batch) = triple_rx.recv().await {
171 sink.process_triple_batch(batch)
172 .await
173 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))?;
174 }
175
176 sink.flush()
177 .await
178 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))?;
179
180 Ok::<(), JsonLdParseError>(())
181 });
182
183 let processing_handle = tokio::spawn({
185 let config = self.config.clone();
186 let context_cache = Arc::clone(&self.context_cache);
187 let term_interner = Arc::clone(&self.term_interner);
188 let performance_monitor = Arc::clone(&self.performance_monitor);
189 let simd_processor = self.simd_processor.clone();
190 let triple_tx = triple_tx.clone();
191
192 async move {
193 let mut batch_buffer = Vec::with_capacity(config.buffer_size);
194
195 while let Some(chunk) = rx.recv().await {
196 let _permit = semaphore
197 .acquire()
198 .await
199 .expect("semaphore should not be closed");
200
201 let processed_triples = if config.enable_simd {
203 Self::process_chunk_simd(
204 chunk,
205 &context_cache,
206 &term_interner,
207 &simd_processor,
208 )
209 .await?
210 } else {
211 Self::process_chunk_standard(chunk, &context_cache, &term_interner).await?
212 };
213
214 performance_monitor.record_triples_parsed(processed_triples.len());
215
216 batch_buffer.extend(processed_triples);
217
218 if batch_buffer.len() >= config.buffer_size
220 || performance_monitor.should_flush_batch()
221 {
222 triple_tx
223 .send(std::mem::take(&mut batch_buffer))
224 .await
225 .map_err(|_| {
226 JsonLdParseError::ProcessingError(
227 "Triple channel send failed".to_string(),
228 )
229 })?;
230 }
231 }
232
233 if !batch_buffer.is_empty() {
235 triple_tx.send(batch_buffer).await.map_err(|_| {
236 JsonLdParseError::ProcessingError("Triple channel send failed".to_string())
237 })?;
238 }
239
240 Ok::<(), JsonLdParseError>(())
241 }
242 });
243
244 let mut buffer = self.buffer_pool.get_buffer().await;
246 let mut total_bytes = 0;
247
248 loop {
249 match buf_reader.read(buffer.as_mut_slice()).await {
250 Ok(0) => break, Ok(n) => {
252 buffer.set_len(n);
253 total_bytes += n;
254 self.performance_monitor.record_bytes_processed(n);
255
256 if self.should_adjust_chunk_size(n) {
258 self.adjust_chunk_size_adaptive().await;
259 }
260
261 let chunk = ProcessingChunk {
262 data: buffer.as_slice().to_vec(),
263 timestamp: Instant::now(),
264 sequence_id: total_bytes,
265 };
266
267 tx.send(chunk).await.map_err(|_| {
268 JsonLdParseError::ProcessingError("Channel send failed".to_string())
269 })?;
270
271 buffer = self.buffer_pool.get_buffer().await;
272 }
273 Err(e) => return Err(JsonLdParseError::Io(e)),
274 }
275 }
276
277 drop(tx); processing_handle
279 .await
280 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))??;
281
282 drop(triple_tx); sink_handle
284 .await
285 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))??;
286
287 Ok(self.performance_monitor.get_statistics())
288 }
289
290 async fn process_chunk_simd(
292 chunk: ProcessingChunk,
293 context_cache: &DashMap<String, Arc<Value>>,
294 term_interner: &TermInterner,
295 simd_processor: &SimdJsonProcessor,
296 ) -> Result<Vec<Triple>, JsonLdParseError> {
297 let start = Instant::now();
298
299 let json_value = simd_processor
301 .parse_json(&chunk.data)
302 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))?;
303
304 let context = Self::resolve_context_zero_copy(&json_value, context_cache).await?;
306
307 #[cfg(feature = "parallel")]
309 let triples = Self::extract_triples_parallel(&json_value, &context, term_interner).await?;
310 #[cfg(not(feature = "parallel"))]
311 let triples = Self::extract_triples_standard(&json_value, &context, term_interner).await?;
312
313 let _processing_time = start.elapsed();
315 Ok(triples)
318 }
319
320 async fn process_chunk_standard(
322 chunk: ProcessingChunk,
323 context_cache: &DashMap<String, Arc<Value>>,
324 term_interner: &TermInterner,
325 ) -> Result<Vec<Triple>, JsonLdParseError> {
326 let json_value: Value = serde_json::from_slice(&chunk.data)
328 .map_err(|e| JsonLdParseError::ProcessingError(e.to_string()))?;
329
330 let context = Self::resolve_context_cached(&json_value, context_cache).await?;
332
333 let triples = Self::extract_triples_standard(&json_value, &context, term_interner).await?;
335
336 Ok(triples)
337 }
338
339 async fn resolve_context_zero_copy(
341 json_value: &Value,
342 context_cache: &DashMap<String, Arc<Value>>,
343 ) -> Result<Arc<Value>, JsonLdParseError> {
344 if let Some(context_ref) = json_value.get("@context") {
345 if let Some(context_str) = context_ref.as_str() {
346 if let Some(cached_context) = context_cache.get(context_str) {
347 return Ok(Arc::clone(&cached_context));
348 }
349
350 let resolved_context = Self::resolve_remote_context(context_str).await?;
352 let context_arc = Arc::new(resolved_context);
353 context_cache.insert(context_str.to_string(), Arc::clone(&context_arc));
354 return Ok(context_arc);
355 }
356 }
357
358 Ok(Arc::new(Value::Object(Map::new())))
360 }
361
362 async fn resolve_context_cached(
364 json_value: &Value,
365 context_cache: &DashMap<String, Arc<Value>>,
366 ) -> Result<Arc<Value>, JsonLdParseError> {
367 Self::resolve_context_zero_copy(json_value, context_cache).await
369 }
370
371 #[cfg(feature = "parallel")]
373 async fn extract_triples_parallel(
374 json_value: &Value,
375 context: &Value,
376 term_interner: &TermInterner,
377 ) -> Result<Vec<Triple>, JsonLdParseError> {
378 if let Value::Array(objects) = json_value {
379 let triples: Result<Vec<Vec<Triple>>, JsonLdParseError> = objects
381 .par_iter()
382 .map(|obj| Self::extract_triples_from_object(obj, context, term_interner))
383 .collect();
384
385 Ok(triples?.into_iter().flatten().collect())
386 } else {
387 Self::extract_triples_from_object(json_value, context, term_interner)
388 }
389 }
390
391 async fn extract_triples_standard(
393 json_value: &Value,
394 context: &Value,
395 term_interner: &TermInterner,
396 ) -> Result<Vec<Triple>, JsonLdParseError> {
397 Self::extract_triples_from_object(json_value, context, term_interner)
398 }
399
400 fn extract_triples_from_object(
402 obj: &Value,
403 context: &Value,
404 term_interner: &TermInterner,
405 ) -> Result<Vec<Triple>, JsonLdParseError> {
406 let mut triples = Vec::new();
407
408 if let Value::Object(map) = obj {
409 let subject: Subject = if let Some(id) = map.get("@id") {
411 Subject::NamedNode(term_interner.intern_named_node(id.as_str().ok_or_else(
412 || JsonLdParseError::ProcessingError("Invalid @id".to_string()),
413 )?)?)
414 } else {
415 Subject::BlankNode(term_interner.intern_blank_node())
417 };
418
419 for (key, value) in map {
421 if key.starts_with('@') {
422 continue; }
424
425 let predicate_iri = Self::expand_property(key, context)?;
427 let predicate = term_interner.intern_named_node(&predicate_iri)?;
428
429 match value {
431 Value::Array(values) => {
432 for val in values {
433 if let Some(triple) = Self::create_triple_from_value(
434 subject.clone(),
435 predicate.clone(),
436 val,
437 context,
438 term_interner,
439 )? {
440 triples.push(triple);
441 }
442 }
443 }
444 _ => {
445 if let Some(triple) = Self::create_triple_from_value(
446 subject.clone(),
447 predicate,
448 value,
449 context,
450 term_interner,
451 )? {
452 triples.push(triple);
453 }
454 }
455 }
456 }
457 }
458
459 Ok(triples)
460 }
461
462 fn create_triple_from_value(
464 subject: Subject,
465 predicate: NamedNode,
466 value: &Value,
467 _context: &Value,
468 term_interner: &TermInterner,
469 ) -> Result<Option<Triple>, JsonLdParseError> {
470 let object: Object = match value {
471 Value::String(s) => {
472 if s.starts_with("http://") || s.starts_with("https://") {
474 Object::NamedNode(term_interner.intern_named_node(s)?)
475 } else {
476 Object::Literal(term_interner.intern_literal(s)?)
477 }
478 }
479 Value::Object(obj) => {
480 if let Some(id) = obj.get("@id") {
481 Object::NamedNode(term_interner.intern_named_node(id.as_str().ok_or_else(
483 || JsonLdParseError::ProcessingError("Invalid @id in object".to_string()),
484 )?)?)
485 } else if let Some(val) = obj.get("@value") {
486 let literal_value = val.as_str().ok_or_else(|| {
488 JsonLdParseError::ProcessingError("Invalid @value".to_string())
489 })?;
490
491 if let Some(datatype) = obj.get("@type") {
492 let datatype_iri = datatype.as_str().ok_or_else(|| {
493 JsonLdParseError::ProcessingError("Invalid @type".to_string())
494 })?;
495 Object::Literal(
496 term_interner
497 .intern_literal_with_datatype(literal_value, datatype_iri)?,
498 )
499 } else if let Some(lang) = obj.get("@language") {
500 let language = lang.as_str().ok_or_else(|| {
501 JsonLdParseError::ProcessingError("Invalid @language".to_string())
502 })?;
503 Object::Literal(
504 term_interner.intern_literal_with_language(literal_value, language)?,
505 )
506 } else {
507 Object::Literal(term_interner.intern_literal(literal_value)?)
508 }
509 } else {
510 return Ok(None); }
512 }
513 Value::Number(n) => Object::Literal(term_interner.intern_literal(&n.to_string())?),
514 Value::Bool(b) => Object::Literal(term_interner.intern_literal(&b.to_string())?),
515 _ => return Ok(None),
516 };
517
518 Ok(Some(Triple::new(
519 subject,
520 Predicate::NamedNode(predicate),
521 object,
522 )))
523 }
524
525 fn expand_property(property: &str, context: &Value) -> Result<String, JsonLdParseError> {
527 if property.contains(':') {
529 Ok(property.to_string())
530 } else if let Value::Object(ctx) = context {
531 if let Some(expanded) = ctx.get(property) {
532 if let Some(iri) = expanded.as_str() {
533 Ok(iri.to_string())
534 } else {
535 Ok(format!("http://example.org/{property}"))
536 }
537 } else {
538 Ok(format!("http://example.org/{property}"))
539 }
540 } else {
541 Ok(format!("http://example.org/{property}"))
542 }
543 }
544
545 async fn resolve_remote_context(_context_iri: &str) -> Result<Value, JsonLdParseError> {
547 Ok(Value::Object(Map::new()))
550 }
551
552 fn should_adjust_chunk_size(&self, bytes_read: usize) -> bool {
554 let target_size = self.config.chunk_size;
555 let threshold = (target_size as f64 * self.config.adaptive_threshold) as usize;
556 bytes_read < threshold || bytes_read > target_size * 2
557 }
558
559 async fn adjust_chunk_size_adaptive(&mut self) {
561 let avg_processing_time = self.performance_monitor.average_chunk_processing_time();
562 let memory_pressure = self.performance_monitor.memory_pressure_detected();
563
564 if memory_pressure {
565 self.config.chunk_size = (self.config.chunk_size / 2).max(1024);
566 } else if avg_processing_time < Duration::from_millis(10) {
567 self.config.chunk_size = (self.config.chunk_size * 2).min(1024 * 1024);
568 }
569 }
570}
571
572#[derive(Debug)]
574struct ProcessingChunk {
575 data: Vec<u8>,
576 #[allow(dead_code)]
577 timestamp: Instant,
578 #[allow(dead_code)]
579 sequence_id: usize,
580}
581
582#[derive(Debug, Clone)]
584pub struct StreamingStatistics {
585 pub total_bytes_processed: usize,
586 pub total_triples_parsed: usize,
587 pub processing_time: Duration,
588 pub average_throughput_mbps: f64,
589 pub parse_errors: usize,
590 pub context_cache_hit_ratio: f64,
591 pub simd_operations_count: usize,
592 pub zero_copy_operations_count: usize,
593}
594
595impl PerformanceMonitor {
596 fn new() -> Self {
597 Self {
598 total_bytes_processed: AtomicUsize::new(0),
599 total_triples_parsed: AtomicUsize::new(0),
600 parse_errors: AtomicUsize::new(0),
601 context_cache_hits: AtomicUsize::new(0),
602 context_cache_misses: AtomicUsize::new(0),
603 simd_operations: AtomicUsize::new(0),
604 zero_copy_operations: AtomicUsize::new(0),
605 start_time: Instant::now(),
606 chunk_processing_times: Arc::new(Mutex::new(VecDeque::with_capacity(1000))),
607 }
608 }
609
610 fn record_bytes_processed(&self, bytes: usize) {
611 self.total_bytes_processed
612 .fetch_add(bytes, Ordering::Relaxed);
613 }
614
615 fn record_triples_parsed(&self, count: usize) {
616 self.total_triples_parsed
617 .fetch_add(count, Ordering::Relaxed);
618 }
619
620 fn should_flush_batch(&self) -> bool {
621 self.average_chunk_processing_time() > Duration::from_millis(100)
623 }
624
625 fn average_chunk_processing_time(&self) -> Duration {
626 let times = self.chunk_processing_times.lock();
627 if times.is_empty() {
628 return Duration::from_millis(1);
629 }
630
631 let total: Duration = times.iter().sum();
632 total / times.len() as u32
633 }
634
635 fn memory_pressure_detected(&self) -> bool {
636 false }
639
640 fn get_statistics(&self) -> StreamingStatistics {
641 let elapsed = self.start_time.elapsed();
642 let bytes = self.total_bytes_processed.load(Ordering::Relaxed);
643 let triples = self.total_triples_parsed.load(Ordering::Relaxed);
644 let errors = self.parse_errors.load(Ordering::Relaxed);
645 let cache_hits = self.context_cache_hits.load(Ordering::Relaxed);
646 let cache_misses = self.context_cache_misses.load(Ordering::Relaxed);
647 let simd_ops = self.simd_operations.load(Ordering::Relaxed);
648 let zero_copy_ops = self.zero_copy_operations.load(Ordering::Relaxed);
649
650 let throughput_mbps = if elapsed.as_secs() > 0 {
651 (bytes as f64) / (1024.0 * 1024.0) / elapsed.as_secs_f64()
652 } else {
653 0.0
654 };
655
656 let cache_hit_ratio = if cache_hits + cache_misses > 0 {
657 cache_hits as f64 / (cache_hits + cache_misses) as f64
658 } else {
659 0.0
660 };
661
662 StreamingStatistics {
663 total_bytes_processed: bytes,
664 total_triples_parsed: triples,
665 processing_time: elapsed,
666 average_throughput_mbps: throughput_mbps,
667 parse_errors: errors,
668 context_cache_hit_ratio: cache_hit_ratio,
669 simd_operations_count: simd_ops,
670 zero_copy_operations_count: zero_copy_ops,
671 }
672 }
673}
674
675impl BufferPool {
676 fn new(buffer_size: usize, max_buffers: usize) -> Self {
677 Self {
678 available_buffers: Arc::new(Mutex::new(Vec::with_capacity(max_buffers))),
679 buffer_size,
680 max_buffers,
681 current_buffers: AtomicUsize::new(0),
682 }
683 }
684
685 async fn get_buffer(&self) -> ZeroCopyBuffer {
686 loop {
687 {
689 let mut buffers = self.available_buffers.lock();
690 if let Some(buffer) = buffers.pop() {
691 return buffer;
692 }
693 } if self.current_buffers.load(Ordering::Relaxed) < self.max_buffers {
696 self.current_buffers.fetch_add(1, Ordering::Relaxed);
697 return ZeroCopyBuffer::new(self.buffer_size);
698 } else {
699 tokio::time::sleep(Duration::from_millis(1)).await;
701 }
702 }
703 }
704
705 #[allow(dead_code)]
706 fn return_buffer(&self, mut buffer: ZeroCopyBuffer) {
707 buffer.reset();
708 let mut buffers = self.available_buffers.lock();
709 if buffers.len() < self.max_buffers {
710 buffers.push(buffer);
711 } else {
712 self.current_buffers.fetch_sub(1, Ordering::Relaxed);
713 }
714 }
715}
716
717pub struct MemoryStreamingSink {
719 triples: Arc<RwLock<Vec<Triple>>>,
720 quads: Arc<RwLock<Vec<Quad>>>,
721 statistics: Arc<RwLock<SinkStatistics>>,
722}
723
724impl Default for MemoryStreamingSink {
725 fn default() -> Self {
726 Self::new()
727 }
728}
729
730impl MemoryStreamingSink {
731 pub fn new() -> Self {
732 Self {
733 triples: Arc::new(RwLock::new(Vec::new())),
734 quads: Arc::new(RwLock::new(Vec::new())),
735 statistics: Arc::new(RwLock::new(SinkStatistics {
736 total_triples_processed: 0,
737 total_quads_processed: 0,
738 average_batch_size: 0.0,
739 processing_rate_per_second: 0.0,
740 memory_usage_bytes: 0,
741 })),
742 }
743 }
744
745 pub fn into_triples(self) -> Arc<RwLock<Vec<Triple>>> {
746 self.triples
747 }
748
749 pub async fn get_triples(&self) -> Vec<Triple> {
750 self.triples.read().await.clone()
751 }
752
753 pub async fn get_quads(&self) -> Vec<Quad> {
754 self.quads.read().await.clone()
755 }
756}
757
758#[derive(Debug)]
760pub struct StreamingError(Box<dyn StdError + Send + Sync>);
761
762impl std::fmt::Display for StreamingError {
763 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
764 write!(f, "Streaming error: {}", self.0)
765 }
766}
767
768impl StdError for StreamingError {
769 fn source(&self) -> Option<&(dyn StdError + 'static)> {
770 Some(&*self.0)
771 }
772}
773
774impl From<Box<dyn StdError + Send + Sync>> for StreamingError {
775 fn from(err: Box<dyn StdError + Send + Sync>) -> Self {
776 StreamingError(err)
777 }
778}
779
780#[async_trait::async_trait]
781impl StreamingSink for MemoryStreamingSink {
782 type Error = StreamingError;
783
784 async fn process_triple_batch(&mut self, triples: Vec<Triple>) -> Result<(), Self::Error> {
785 let batch_size = triples.len();
786 self.triples.write().await.extend(triples);
787
788 let mut stats = self.statistics.write().await;
789 stats.total_triples_processed += batch_size;
790 stats.average_batch_size = (stats.average_batch_size + batch_size as f64) / 2.0;
791
792 Ok(())
793 }
794
795 async fn process_quad_batch(&mut self, quads: Vec<Quad>) -> Result<(), Self::Error> {
796 let batch_size = quads.len();
797 self.quads.write().await.extend(quads);
798
799 let mut stats = self.statistics.write().await;
800 stats.total_quads_processed += batch_size;
801
802 Ok(())
803 }
804
805 async fn flush(&mut self) -> Result<(), Self::Error> {
806 Ok(())
808 }
809
810 fn performance_statistics(&self) -> SinkStatistics {
811 SinkStatistics {
813 total_triples_processed: 0,
814 total_quads_processed: 0,
815 average_batch_size: 0.0,
816 processing_rate_per_second: 0.0,
817 memory_usage_bytes: 0,
818 }
819 }
820}
821
822#[cfg(test)]
823mod tests {
824 use super::*;
825 use std::io::Cursor;
826
827 #[tokio::test]
828 async fn test_ultra_streaming_parser() {
829 let json_ld_data = r#"[
830 {
831 "@id": "http://example.org/person/1",
832 "name": "Alice",
833 "age": 30
834 },
835 {
836 "@id": "http://example.org/person/2",
837 "name": "Bob",
838 "age": 25
839 }
840 ]"#;
841
842 let config = StreamingConfig::default();
843 let mut parser = UltraStreamingJsonLdParser::new(config);
844 let reader = Cursor::new(json_ld_data.as_bytes());
845 let sink = MemoryStreamingSink::new();
846
847 let _sink_data = Arc::clone(&sink.triples);
849
850 let stats = parser
851 .stream_parse(reader, sink)
852 .await
853 .expect("async operation should succeed");
854
855 assert!(stats.total_bytes_processed > 0);
856 }
860}