1use crate::compression::{
4 AdvancedCompressionType, CompressedData, CompressionAlgorithm, CompressionMetadata,
5 MAX_DICTIONARY_SIZE,
6};
7use anyhow::{anyhow, Result};
8use std::collections::HashMap;
9use std::time::Instant;
10
11pub struct AdaptiveDictionary {
13 string_to_id: HashMap<Vec<u8>, u32>,
15 id_to_string: HashMap<u32, Vec<u8>>,
17 next_id: u32,
19}
20
21impl AdaptiveDictionary {
22 pub fn new() -> Self {
24 Self {
25 string_to_id: HashMap::new(),
26 id_to_string: HashMap::new(),
27 next_id: 0,
28 }
29 }
30
31 pub fn add_string(&mut self, data: &[u8]) -> Result<u32> {
33 if let Some(&id) = self.string_to_id.get(data) {
34 return Ok(id);
35 }
36
37 if self.string_to_id.len() >= MAX_DICTIONARY_SIZE {
38 return Err(anyhow!(
39 "Dictionary size limit exceeded: {} entries (max: {})",
40 self.string_to_id.len(),
41 MAX_DICTIONARY_SIZE
42 ));
43 }
44
45 let id = self.next_id;
46 self.next_id += 1;
47
48 self.string_to_id.insert(data.to_vec(), id);
49 self.id_to_string.insert(id, data.to_vec());
50
51 Ok(id)
52 }
53
54 pub fn get_string(&self, id: u32) -> Option<&[u8]> {
56 self.id_to_string.get(&id).map(|v| v.as_slice())
57 }
58
59 pub fn get_id(&self, data: &[u8]) -> Option<u32> {
61 self.string_to_id.get(data).copied()
62 }
63
64 pub fn compress_data(&mut self, data: &[u8]) -> Result<Vec<u8>> {
66 if data.is_empty() {
67 return Ok(Vec::new());
68 }
69
70 let mut compressed = Vec::new();
71
72 let mut current_word = Vec::new();
74
75 for &byte in data {
76 if byte.is_ascii_whitespace() {
77 if !current_word.is_empty() {
78 let id = self.add_string(¤t_word)?;
79 compressed.extend_from_slice(&id.to_le_bytes());
80 current_word.clear();
81 }
82 compressed.extend_from_slice(&(u32::MAX - byte as u32).to_le_bytes());
84 } else {
85 current_word.push(byte);
86 }
87 }
88
89 if !current_word.is_empty() {
91 let id = self.add_string(¤t_word)?;
92 compressed.extend_from_slice(&id.to_le_bytes());
93 }
94
95 Ok(compressed)
96 }
97
98 pub fn decompress_data(&self, compressed: &[u8]) -> Result<Vec<u8>> {
100 if compressed.is_empty() {
101 return Ok(Vec::new());
102 }
103
104 if compressed.len() % 4 != 0 {
105 return Err(anyhow!("Invalid compressed data length"));
106 }
107
108 let mut decompressed = Vec::new();
109
110 for chunk in compressed.chunks_exact(4) {
111 let id = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
112
113 if id > u32::MAX - 256 {
114 let byte = (u32::MAX - id) as u8;
116 decompressed.push(byte);
117 } else {
118 if let Some(data) = self.get_string(id) {
120 decompressed.extend_from_slice(data);
121 } else {
122 return Err(anyhow!("Invalid dictionary ID: {}", id));
123 }
124 }
125 }
126
127 Ok(decompressed)
128 }
129
130 pub fn serialize_dictionary(&self) -> Vec<u8> {
132 let mut serialized = Vec::new();
133
134 let size = self.id_to_string.len() as u32;
136 serialized.extend_from_slice(&size.to_le_bytes());
137
138 for (&id, data) in &self.id_to_string {
140 serialized.extend_from_slice(&id.to_le_bytes());
141 let len = data.len() as u32;
142 serialized.extend_from_slice(&len.to_le_bytes());
143 serialized.extend_from_slice(data);
144 }
145
146 serialized
147 }
148
149 pub fn deserialize_dictionary(&mut self, data: &[u8]) -> Result<()> {
151 if data.len() < 4 {
152 return Err(anyhow!("Invalid dictionary data"));
153 }
154
155 let size = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
156 let mut offset = 4;
157
158 self.string_to_id.clear();
159 self.id_to_string.clear();
160 self.next_id = 0;
161
162 for _ in 0..size {
163 if offset + 8 > data.len() {
164 return Err(anyhow!("Truncated dictionary data"));
165 }
166
167 let id = u32::from_le_bytes([
168 data[offset],
169 data[offset + 1],
170 data[offset + 2],
171 data[offset + 3],
172 ]);
173 offset += 4;
174
175 let len = u32::from_le_bytes([
176 data[offset],
177 data[offset + 1],
178 data[offset + 2],
179 data[offset + 3],
180 ]) as usize;
181 offset += 4;
182
183 if offset + len > data.len() {
184 return Err(anyhow!("Truncated dictionary entry"));
185 }
186
187 let string_data = data[offset..offset + len].to_vec();
188 offset += len;
189
190 self.string_to_id.insert(string_data.clone(), id);
191 self.id_to_string.insert(id, string_data);
192 self.next_id = self.next_id.max(id + 1);
193 }
194
195 Ok(())
196 }
197
198 pub fn statistics(&self) -> HashMap<String, String> {
200 let mut stats = HashMap::new();
201 stats.insert("entries".to_string(), self.string_to_id.len().to_string());
202 stats.insert("next_id".to_string(), self.next_id.to_string());
203
204 if !self.id_to_string.is_empty() {
205 let avg_length: f64 = self.id_to_string.values().map(|v| v.len()).sum::<usize>() as f64
206 / self.id_to_string.len() as f64;
207 stats.insert("avg_entry_length".to_string(), format!("{avg_length:.2}"));
208 }
209
210 stats
211 }
212}
213
214impl Default for AdaptiveDictionary {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220impl CompressionAlgorithm for AdaptiveDictionary {
221 fn compress(&self, data: &[u8]) -> Result<CompressedData> {
222 let mut dict = self.clone();
223 let start = Instant::now();
224 let compressed_data = dict.compress_data(data)?;
225 let compression_time = start.elapsed();
226
227 let dict_data = dict.serialize_dictionary();
229 let mut final_data = Vec::new();
230 final_data.extend_from_slice(&(dict_data.len() as u32).to_le_bytes());
231 final_data.extend_from_slice(&dict_data);
232 final_data.extend_from_slice(&compressed_data);
233
234 let metadata = CompressionMetadata {
235 algorithm: AdvancedCompressionType::AdaptiveDictionary,
236 original_size: data.len() as u64,
237 compressed_size: final_data.len() as u64,
238 compression_time_us: compression_time.as_micros() as u64,
239 metadata: dict.statistics(),
240 };
241
242 Ok(CompressedData {
243 data: final_data,
244 metadata,
245 })
246 }
247
248 fn decompress(&self, compressed: &CompressedData) -> Result<Vec<u8>> {
249 if compressed.metadata.algorithm != AdvancedCompressionType::AdaptiveDictionary {
250 return Err(anyhow!(
251 "Invalid compression algorithm: expected AdaptiveDictionary, got {}",
252 compressed.metadata.algorithm
253 ));
254 }
255
256 let data = &compressed.data;
257 if data.len() < 4 {
258 return Err(anyhow!("Invalid compressed data"));
259 }
260
261 let dict_len = u32::from_le_bytes([data[0], data[1], data[2], data[3]]) as usize;
263 if data.len() < 4 + dict_len {
264 return Err(anyhow!("Invalid compressed data"));
265 }
266
267 let mut dict = AdaptiveDictionary::new();
268 dict.deserialize_dictionary(&data[4..4 + dict_len])?;
269
270 let compressed_data = &data[4 + dict_len..];
272 dict.decompress_data(compressed_data)
273 }
274
275 fn algorithm_type(&self) -> AdvancedCompressionType {
276 AdvancedCompressionType::AdaptiveDictionary
277 }
278}
279
280impl Clone for AdaptiveDictionary {
282 fn clone(&self) -> Self {
283 Self {
284 string_to_id: self.string_to_id.clone(),
285 id_to_string: self.id_to_string.clone(),
286 next_id: self.next_id,
287 }
288 }
289}
290
291#[derive(Debug)]
320pub struct TripleDictionary {
321 subject_dict: HashMap<String, u64>,
323 predicate_dict: HashMap<String, u64>,
325 object_dict: HashMap<String, u64>,
327 reverse: HashMap<u64, String>,
330 next_id: u64,
332}
333
334impl TripleDictionary {
335 pub fn new() -> Self {
337 Self {
338 subject_dict: HashMap::new(),
339 predicate_dict: HashMap::new(),
340 object_dict: HashMap::new(),
341 reverse: HashMap::new(),
342 next_id: 0,
343 }
344 }
345
346 pub fn encode_triple(&mut self, s: &str, p: &str, o: &str) -> (u64, u64, u64) {
353 let s_id = self.intern_subject(s);
354 let p_id = self.intern_predicate(p);
355 let o_id = self.intern_object(o);
356 (s_id, p_id, o_id)
357 }
358
359 pub fn decode_triple(
363 &self,
364 s_id: u64,
365 p_id: u64,
366 o_id: u64,
367 ) -> Option<(String, String, String)> {
368 let s = self.reverse.get(&s_id)?.clone();
369 let p = self.reverse.get(&p_id)?.clone();
370 let o = self.reverse.get(&o_id)?.clone();
371 Some((s, p, o))
372 }
373
374 pub fn size(&self) -> usize {
377 self.reverse.len()
378 }
379
380 pub fn compression_ratio(&self) -> f64 {
389 if self.reverse.is_empty() {
390 return 1.0;
391 }
392 let total_string_bytes: usize = self.reverse.values().map(|s| s.len()).sum();
393 let id_bytes = self.reverse.len() * 8; if id_bytes == 0 {
395 return 1.0;
396 }
397 total_string_bytes as f64 / id_bytes as f64
398 }
399
400 pub fn get_subject_id(&self, s: &str) -> Option<u64> {
402 self.subject_dict.get(s).copied()
403 }
404
405 pub fn get_predicate_id(&self, p: &str) -> Option<u64> {
407 self.predicate_dict.get(p).copied()
408 }
409
410 pub fn get_object_id(&self, o: &str) -> Option<u64> {
412 self.object_dict.get(o).copied()
413 }
414
415 pub fn decode_id(&self, id: u64) -> Option<&str> {
419 self.reverse.get(&id).map(|s| s.as_str())
420 }
421
422 pub fn subject_count(&self) -> usize {
424 self.subject_dict.len()
425 }
426
427 pub fn predicate_count(&self) -> usize {
429 self.predicate_dict.len()
430 }
431
432 pub fn object_count(&self) -> usize {
434 self.object_dict.len()
435 }
436
437 fn intern_subject(&mut self, s: &str) -> u64 {
440 if let Some(&id) = self.subject_dict.get(s) {
441 return id;
442 }
443 let id = self.alloc_id();
444 self.subject_dict.insert(s.to_string(), id);
445 self.reverse.insert(id, s.to_string());
446 id
447 }
448
449 fn intern_predicate(&mut self, p: &str) -> u64 {
450 if let Some(&id) = self.predicate_dict.get(p) {
451 return id;
452 }
453 let id = self.alloc_id();
454 self.predicate_dict.insert(p.to_string(), id);
455 self.reverse.insert(id, p.to_string());
456 id
457 }
458
459 fn intern_object(&mut self, o: &str) -> u64 {
460 if let Some(&id) = self.object_dict.get(o) {
461 return id;
462 }
463 let id = self.alloc_id();
464 self.object_dict.insert(o.to_string(), id);
465 self.reverse.insert(id, o.to_string());
466 id
467 }
468
469 fn alloc_id(&mut self) -> u64 {
470 let id = self.next_id;
471 self.next_id = self.next_id.saturating_add(1);
472 id
473 }
474}
475
476impl Default for TripleDictionary {
477 fn default() -> Self {
478 Self::new()
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485
486 #[test]
487 fn test_dictionary_basic() {
488 let mut dict = AdaptiveDictionary::new();
489
490 let id1 = dict.add_string(b"hello").unwrap();
491 let id2 = dict.add_string(b"world").unwrap();
492 let id3 = dict.add_string(b"hello").unwrap(); assert_eq!(id1, id3);
495 assert_ne!(id1, id2);
496 assert_eq!(dict.get_string(id1), Some(&b"hello"[..]));
497 assert_eq!(dict.get_string(id2), Some(&b"world"[..]));
498 }
499
500 #[test]
501 fn test_compression_algorithm_trait() {
502 let dict = AdaptiveDictionary::new();
503 let data = b"hello world hello world";
504
505 let compressed = dict.compress(data).unwrap();
506 assert_eq!(
507 compressed.metadata.algorithm,
508 AdvancedCompressionType::AdaptiveDictionary
509 );
510
511 let decompressed = dict.decompress(&compressed).unwrap();
512 assert_eq!(data, decompressed.as_slice());
513 }
514
515 #[test]
516 fn test_dictionary_serialization() {
517 let mut dict = AdaptiveDictionary::new();
518 dict.add_string(b"test").unwrap();
519 dict.add_string(b"data").unwrap();
520
521 let serialized = dict.serialize_dictionary();
522
523 let mut new_dict = AdaptiveDictionary::new();
524 new_dict.deserialize_dictionary(&serialized).unwrap();
525
526 assert_eq!(dict.get_id(b"test"), new_dict.get_id(b"test"));
527 assert_eq!(dict.get_id(b"data"), new_dict.get_id(b"data"));
528 }
529
530 #[test]
533 fn test_triple_dictionary_encode_decode_roundtrip() {
534 let mut dict = TripleDictionary::new();
535
536 let s = "http://example.org/Alice";
537 let p = "http://schema.org/knows";
538 let o = "http://example.org/Bob";
539
540 let (s_id, p_id, o_id) = dict.encode_triple(s, p, o);
541 let decoded = dict
542 .decode_triple(s_id, p_id, o_id)
543 .expect("decode must succeed");
544 assert_eq!(decoded.0, s);
545 assert_eq!(decoded.1, p);
546 assert_eq!(decoded.2, o);
547 }
548
549 #[test]
550 fn test_triple_dictionary_same_uri_same_id() {
551 let mut dict = TripleDictionary::new();
552
553 let rdf_type = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
554 let class_a = "http://example.org/ClassA";
555 let class_b = "http://example.org/ClassB";
556
557 let (_, p1, _) = dict.encode_triple("http://example.org/s1", rdf_type, class_a);
558 let (_, p2, _) = dict.encode_triple("http://example.org/s2", rdf_type, class_b);
559 assert_eq!(p1, p2, "same predicate URI must map to same ID");
561 }
562
563 #[test]
564 fn test_triple_dictionary_size() {
565 let mut dict = TripleDictionary::new();
566 assert_eq!(dict.size(), 0);
567
568 dict.encode_triple(
570 "http://example.org/s",
571 "http://example.org/p",
572 "http://example.org/o",
573 );
574 assert_eq!(dict.size(), 3, "three unique URIs should give size 3");
575
576 dict.encode_triple(
578 "http://example.org/s",
579 "http://example.org/p",
580 "http://example.org/o",
581 );
582 assert_eq!(
583 dict.size(),
584 3,
585 "duplicate triple must not grow the dictionary"
586 );
587 }
588
589 #[test]
590 fn test_triple_dictionary_compression_ratio() {
591 let mut dict = TripleDictionary::new();
592 for i in 0..100 {
594 dict.encode_triple(
595 &format!("http://very-long-namespace.example.org/subject/{}", i),
596 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
597 "http://very-long-namespace.example.org/SomeClass",
598 );
599 }
600 let ratio = dict.compression_ratio();
601 assert!(
602 ratio > 1.0,
603 "long URIs should have compression_ratio > 1.0, got {}",
604 ratio
605 );
606 }
607
608 #[test]
609 fn test_triple_dictionary_decode_unknown_id_returns_none() {
610 let dict = TripleDictionary::new();
611 let result = dict.decode_triple(9999, 9998, 9997);
612 assert!(result.is_none(), "unknown IDs should return None");
613 }
614
615 #[test]
616 fn test_triple_dictionary_component_counts() {
617 let mut dict = TripleDictionary::new();
618
619 dict.encode_triple(
620 "http://example.org/s1",
621 "http://schema.org/name",
622 "\"Alice\"",
623 );
624 dict.encode_triple("http://example.org/s2", "http://schema.org/name", "\"Bob\"");
625 dict.encode_triple("http://example.org/s1", "http://schema.org/age", "\"30\"");
626
627 assert_eq!(dict.subject_count(), 2, "two distinct subjects");
628 assert_eq!(dict.predicate_count(), 2, "two distinct predicates");
629 assert_eq!(dict.object_count(), 3, "three distinct objects");
630 }
631
632 #[test]
633 fn test_triple_dictionary_large_graph() {
634 let mut dict = TripleDictionary::new();
635
636 let predicates = [
638 "http://schema.org/name",
639 "http://schema.org/knows",
640 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
641 ];
642
643 for i in 0..1000 {
644 let s = format!("http://example.org/person/{}", i);
645 let p = predicates[i % predicates.len()];
646 let o = format!("http://example.org/person/{}", (i + 1) % 1000);
647 dict.encode_triple(&s, p, &o);
648 }
649
650 assert_eq!(dict.subject_count(), 1000);
652 assert_eq!(dict.predicate_count(), 3);
653 assert!(dict.object_count() <= 1000);
657
658 let ratio = dict.compression_ratio();
659 assert!(
661 ratio > 2.0,
662 "large graph should achieve > 2x compression ratio, got {}",
663 ratio
664 );
665 }
666
667 #[test]
668 fn test_triple_dictionary_get_id_helpers() {
669 let mut dict = TripleDictionary::new();
670 let s = "http://example.org/s";
671 let p = "http://schema.org/p";
672 let o = "http://example.org/o";
673
674 let (s_id, p_id, o_id) = dict.encode_triple(s, p, o);
675
676 assert_eq!(dict.get_subject_id(s), Some(s_id));
677 assert_eq!(dict.get_predicate_id(p), Some(p_id));
678 assert_eq!(dict.get_object_id(o), Some(o_id));
679 assert_eq!(dict.get_subject_id("unknown"), None);
680 }
681
682 #[test]
683 fn test_triple_dictionary_decode_id() {
684 let mut dict = TripleDictionary::new();
685 let (s_id, p_id, o_id) = dict.encode_triple(
686 "http://example.org/s",
687 "http://schema.org/p",
688 "http://example.org/o",
689 );
690 assert_eq!(dict.decode_id(s_id), Some("http://example.org/s"));
691 assert_eq!(dict.decode_id(p_id), Some("http://schema.org/p"));
692 assert_eq!(dict.decode_id(o_id), Some("http://example.org/o"));
693 assert_eq!(dict.decode_id(99999), None);
694 }
695}