1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::fs::File;
9use std::io::{self, BufReader, Read};
10use std::path::Path;
11
12pub type UChar = u8;
14pub type UNumber = u32;
15pub type Catsize = u32;
16pub type Treesize = u32;
17pub type OffsetType = i64;
18
19pub const MASK_NONE: u8 = 0x00;
21pub const MASK_PACKAGE: u8 = 0x01;
22pub const MASK_PROFILE: u8 = 0x02;
23pub const MASK_HARD: u8 = MASK_PACKAGE | MASK_PROFILE;
24pub const MASK_SYSTEM: u8 = 0x04;
25pub const MASK_WORLD: u8 = 0x08;
26pub const MASK_WORLD_SETS: u8 = 0x10;
27pub const MASK_IN_PROFILE: u8 = 0x20;
28pub const MASK_MARKED: u8 = 0x40;
29
30pub const MAGICNUMCHAR: u8 = 0xFF;
32
33pub const DB_MAGIC: &[u8] = b"eix\n";
35
36pub const DB_VERSION_CURRENT: DBVersion = 39;
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct DBHeader {
55 pub version: DBVersion,
57
58 pub size: Catsize,
60
61 pub overlays: Vec<OverlayIdent>,
63
64 #[serde(skip)]
66 pub eapi_hash: StringHash,
67 #[serde(skip)]
68 pub license_hash: StringHash,
69 #[serde(skip)]
70 pub keywords_hash: StringHash,
71 #[serde(skip)]
72 pub iuse_hash: StringHash,
73 #[serde(skip)]
74 pub slot_hash: StringHash,
75 #[serde(skip)]
76 pub depend_hash: StringHash,
77
78 pub use_depend: bool, pub use_required_use: bool, pub use_src_uri: bool, pub world_sets: Vec<String>,
85}
86
87pub type DBVersion = u32;
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct OverlayIdent {
94 pub path: String, pub label: String, pub priority: i32, }
98
99#[derive(Debug, Clone, Default)]
103pub struct StringHash {
104 index_to_string: Vec<String>,
105 string_to_index: HashMap<String, usize>,
106}
107
108impl StringHash {
109 pub fn new() -> Self {
110 StringHash::default()
111 }
112
113 pub fn get_index(&self, s: &str) -> Option<usize> {
114 self.string_to_index.get(s).copied()
115 }
116
117 pub fn get_string(&self, index: usize) -> Option<&str> {
118 self.index_to_string.get(index).map(|s| s.as_str())
119 }
120
121 pub fn add(&mut self, s: String) -> usize {
122 if let Some(&idx) = self.string_to_index.get(&s) {
123 return idx;
124 }
125 let idx = self.index_to_string.len();
126 self.string_to_index.insert(s.clone(), idx);
127 self.index_to_string.push(s);
128 idx
129 }
130
131 pub fn len(&self) -> usize {
132 self.index_to_string.len()
133 }
134}
135
136pub type SaveBitmask = UNumber;
140
141pub const SAVE_BITMASK_DEP: SaveBitmask = 0x01;
142pub const SAVE_BITMASK_REQUIRED_USE: SaveBitmask = 0x02;
143pub const SAVE_BITMASK_SRC_URI: SaveBitmask = 0x04;
144
145#[derive(Debug, Clone, Serialize)]
149pub struct BasicPart {
150 pub part_type: PartType,
151 pub part_content: String,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
155pub enum PartType {
156 Garbage = 0,
157 Alpha = 1,
158 Beta = 2,
159 Pre = 3,
160 Rc = 4,
161 Revision = 5,
162 InterRev = 6,
163 Patch = 7,
164 Character = 8,
165 Primary = 9,
166 First = 10,
167}
168
169impl PartType {
170 pub fn from_u64(v: u64) -> Self {
171 match v {
172 1 => PartType::Alpha,
173 2 => PartType::Beta,
174 3 => PartType::Pre,
175 4 => PartType::Rc,
176 5 => PartType::Revision,
177 6 => PartType::InterRev,
178 7 => PartType::Patch,
179 8 => PartType::Character,
180 9 => PartType::Primary,
181 10 => PartType::First,
182 _ => PartType::Garbage,
183 }
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct Package {
192 pub category: String,
193 pub name: String,
194 pub description: String,
195 pub homepage: String,
196 pub licenses: String,
197 pub versions: Vec<Version>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct Version {
205 #[serde(rename = "version")]
206 pub version_string: String,
207 #[serde(skip)]
208 pub parts: Vec<BasicPart>,
209 pub eapi: String,
210 pub mask_flags: u8,
211 pub properties_flags: u8,
212 pub restrict_flags: u64,
213 pub keywords: Vec<String>,
214 pub slot: String,
215 pub overlay_key: u64,
216 pub reponame: String,
217 pub priority: i32,
218 pub iuse: Vec<String>,
219 pub required_use: Vec<String>,
220 pub depend: Option<Depend>,
221 pub src_uri: Option<String>,
222}
223
224impl Version {
225 pub fn get_full_version(&self) -> String {
226 let mut s = String::new();
227 for part in &self.parts {
228 match part.part_type {
229 PartType::First | PartType::Character | PartType::Garbage => {
230 s.push_str(&part.part_content);
231 }
232 PartType::Alpha => {
233 s.push_str("_alpha");
234 s.push_str(&part.part_content);
235 }
236 PartType::Beta => {
237 s.push_str("_beta");
238 s.push_str(&part.part_content);
239 }
240 PartType::Pre => {
241 s.push_str("_pre");
242 s.push_str(&part.part_content);
243 }
244 PartType::Rc => {
245 s.push_str("_rc");
246 s.push_str(&part.part_content);
247 }
248 PartType::Patch => {
249 s.push_str("_p");
250 s.push_str(&part.part_content);
251 }
252 PartType::Revision => {
253 s.push_str("-r");
254 s.push_str(&part.part_content);
255 }
256 PartType::InterRev | PartType::Primary => {
257 s.push('.');
258 s.push_str(&part.part_content);
259 }
260 }
261 }
262 s
263 }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct Depend {
271 pub depend: Vec<String>,
272 pub rdepend: Vec<String>,
273 pub pdepend: Vec<String>,
274 pub bdepend: Vec<String>,
275 pub idepend: Vec<String>,
276}
277
278pub struct Database {
282 reader: BufReader<File>,
283}
284
285impl Database {
286 pub fn open_read<P: AsRef<Path>>(path: P) -> io::Result<Self> {
288 let file = File::open(path)?;
289 let reader = BufReader::new(file);
290 Ok(Database { reader })
291 }
292
293 pub fn read_uchar(&mut self) -> io::Result<UChar> {
295 let mut buf = [0u8; 1];
296 self.reader.read_exact(&mut buf)?;
297 Ok(buf[0])
298 }
299
300 pub fn read_num(&mut self) -> io::Result<u64> {
309 let ch = self.read_uchar()?;
310
311 if ch != MAGICNUMCHAR {
313 return Ok(ch as u64);
314 }
315
316 let mut to_get = 1usize;
318 let mut result: u64;
319
320 loop {
322 let c = self.read_uchar()?;
323
324 if c == MAGICNUMCHAR {
325 to_get += 1;
326 continue;
327 }
328
329 if c != 0 {
330 result = c as u64;
331 } else {
332 result = MAGICNUMCHAR as u64;
334 to_get -= 1;
335 }
336 break;
337 }
338
339 for _ in 0..to_get {
341 let byte = self.read_uchar()?;
342 result = (result << 8) | (byte as u64);
343 }
344
345 Ok(result)
346 }
347
348 pub fn read_string(&mut self) -> io::Result<String> {
352 let len = self.read_num()? as usize;
353 if len == 0 {
354 return Ok(String::new());
355 }
356
357 let mut buf = vec![0u8; len];
358 self.reader.read_exact(&mut buf)?;
359
360 String::from_utf8(buf).map_err(|e| {
361 io::Error::new(
362 io::ErrorKind::InvalidData,
363 format!("Invalid UTF-8 in string: {}", e),
364 )
365 })
366 }
367
368 pub fn read_hash_string(&mut self, hash: &StringHash) -> io::Result<String> {
370 let index = self.read_num()? as usize;
371 hash.get_string(index)
372 .map(|s| s.to_string())
373 .ok_or_else(|| {
374 io::Error::new(
375 io::ErrorKind::InvalidData,
376 format!("Invalid hash index: {} (hash size: {})", index, hash.len()),
377 )
378 })
379 }
380
381 fn read_hash(&mut self) -> io::Result<StringHash> {
386 let count = self.read_num()? as usize;
387 let mut hash = StringHash::new();
388
389 for _ in 0..count {
390 let s = self.read_string()?;
391 hash.add(s);
392 }
393
394 Ok(hash)
395 }
396
397 pub fn read_hash_words(&mut self, hash: &StringHash) -> io::Result<Vec<String>> {
399 let count = self.read_num()? as usize;
400 let mut words = Vec::with_capacity(count);
401 for _ in 0..count {
402 words.push(self.read_hash_string(hash)?);
403 }
404 Ok(words)
405 }
406
407 pub fn read_part(&mut self) -> io::Result<BasicPart> {
409 let val = self.read_num()?;
410 let part_type = PartType::from_u64(val % 32);
411 let len = (val / 32) as usize;
412 let mut part_content = String::new();
413 if len > 0 {
414 let mut buf = vec![0u8; len];
415 self.reader.read_exact(&mut buf)?;
416 part_content = String::from_utf8(buf).map_err(|e| {
417 io::Error::new(
418 io::ErrorKind::InvalidData,
419 format!("Invalid UTF-8 in Part: {}", e),
420 )
421 })?;
422 }
423 Ok(BasicPart {
424 part_type,
425 part_content,
426 })
427 }
428
429 pub fn read_header(&mut self, min_version: DBVersion) -> io::Result<DBHeader> {
431 let mut magic = vec![0u8; DB_MAGIC.len()];
433 self.reader.read_exact(&mut magic)?;
434 if magic != DB_MAGIC {
435 return Err(io::Error::new(
436 io::ErrorKind::InvalidData,
437 format!("Invalid magic: expected {:?}, got {:?}", DB_MAGIC, magic),
438 ));
439 }
440
441 let version = self.read_num()? as DBVersion;
443 if version < min_version {
444 return Err(io::Error::new(
445 io::ErrorKind::InvalidData,
446 format!(
447 "Database version {} too old (minimum: {})",
448 version, min_version
449 ),
450 ));
451 }
452
453 let size = self.read_num()? as Catsize;
455
456 let overlay_count = self.read_num()? as usize;
458
459 let mut overlays = Vec::with_capacity(overlay_count);
461 for i in 0..overlay_count {
462 let path = self.read_string()?;
463 let label = self.read_string()?;
464 overlays.push(OverlayIdent {
465 path,
466 label,
467 priority: i as i32,
468 });
469 }
470
471 let eapi_hash = self.read_hash()?;
473 let license_hash = self.read_hash()?;
474 let keywords_hash = self.read_hash()?;
475 let iuse_hash = self.read_hash()?;
476 let slot_hash = self.read_hash()?;
477
478 let world_set_count = self.read_num()? as usize;
480 let mut world_sets = Vec::with_capacity(world_set_count);
481 for _ in 0..world_set_count {
482 world_sets.push(self.read_string()?);
483 }
484
485 let bitmask = self.read_num()? as SaveBitmask;
487 let use_depend = (bitmask & SAVE_BITMASK_DEP) != 0;
488 let use_required_use = (bitmask & SAVE_BITMASK_REQUIRED_USE) != 0;
489 let use_src_uri = (bitmask & SAVE_BITMASK_SRC_URI) != 0;
490
491 let depend_hash = if use_depend {
493 let _len = self.read_num()?;
495 self.read_hash()?
496 } else {
497 StringHash::new()
498 };
499
500 Ok(DBHeader {
501 version,
502 size,
503 overlays,
504 eapi_hash,
505 license_hash,
506 keywords_hash,
507 iuse_hash,
508 slot_hash,
509 depend_hash,
510 use_depend,
511 use_required_use,
512 use_src_uri,
513 world_sets,
514 })
515 }
516}
517
518pub struct PackageReader {
522 db: Database,
523 header: DBHeader,
524 frames: Treesize,
525 cat_size: Treesize,
526 cat_name: String,
527}
528
529impl Database {
530 pub fn read_version(&mut self, hdr: &DBHeader) -> io::Result<Version> {
531 let mut eapi = String::new();
532 if hdr.version >= 36 {
533 eapi = self.read_hash_string(&hdr.eapi_hash)?;
534 }
535
536 let mask_flags = self.read_uchar()?;
537 let properties_flags = self.read_uchar()?;
538 let restrict_flags = self.read_num()?;
539
540 let keywords = self.read_hash_words(&hdr.keywords_hash)?;
542
543 let part_count = self.read_num()? as usize;
545 let mut parts = Vec::with_capacity(part_count);
546 for _ in 0..part_count {
547 parts.push(self.read_part()?);
548 }
549
550 let slot = self.read_hash_string(&hdr.slot_hash)?;
552
553 let overlay_key = self.read_num()?;
555
556 let overlay = hdr.overlays.get(overlay_key as usize).ok_or_else(|| {
557 io::Error::new(
558 io::ErrorKind::InvalidData,
559 format!("Invalid overlay key: {}", overlay_key),
560 )
561 })?;
562 let reponame = overlay.label.clone();
563 let priority = overlay.priority;
564
565 let iuse = self.read_hash_words(&hdr.iuse_hash)?;
567
568 let mut required_use = Vec::new();
572 if hdr.use_required_use {
573 required_use = self.read_hash_words(&hdr.iuse_hash)?;
574 }
575
576 let mut depend = None;
579 if hdr.use_depend {
580 let _len = self.read_num()?; let mut dep = Depend {
583 depend: self.read_hash_words(&hdr.depend_hash)?,
584 rdepend: self.read_hash_words(&hdr.depend_hash)?,
585 pdepend: self.read_hash_words(&hdr.depend_hash)?,
586 bdepend: Vec::new(),
587 idepend: Vec::new(),
588 };
589 if hdr.version > 31 {
590 dep.bdepend = self.read_hash_words(&hdr.depend_hash)?;
591 }
592 if hdr.version > 38 {
593 dep.idepend = self.read_hash_words(&hdr.depend_hash)?;
594 }
595 depend = Some(dep);
596 }
597
598 let mut src_uri = None;
602 if hdr.use_src_uri {
603 src_uri = Some(self.read_string()?);
604 }
605
606 Ok(Version {
609 version_string: String::new(),
610 parts,
611 eapi,
612 mask_flags,
613 properties_flags,
614 restrict_flags,
615 keywords,
616 slot,
617 overlay_key,
618 reponame,
619 priority,
620 iuse,
621 required_use,
622 depend,
623 src_uri,
624 })
625 }
626}
627
628impl PackageReader {
629 pub fn new(db: Database, header: DBHeader) -> Self {
630 let frames = header.size;
631 PackageReader {
632 db,
633 header,
634 frames,
635 cat_size: 0,
636 cat_name: String::new(),
637 }
638 }
639
640 pub fn next_category(&mut self) -> io::Result<bool> {
642 if self.frames == 0 {
643 return Ok(false);
644 }
645
646 self.cat_name = self.db.read_string()?;
647 self.cat_size = self.db.read_num()? as Treesize;
648 self.frames -= 1;
649
650 Ok(true)
651 }
652
653 pub fn current_category(&self) -> &str {
654 &self.cat_name
655 }
656
657 pub fn read_package(&mut self) -> io::Result<Option<Package>> {
659 if self.cat_size == 0 {
660 return Ok(None);
661 }
662
663 let _pkg_len = self.db.read_num()?;
665
666 let name = self.db.read_string()?;
667 let description = self.db.read_string()?;
668 let homepage = self.db.read_string()?;
669 let licenses = self.db.read_hash_string(&self.header.license_hash)?;
670
671 let version_count = self.db.read_num()? as usize;
672 let mut versions = Vec::with_capacity(version_count);
673 for _ in 0..version_count {
674 let mut v = self.db.read_version(&self.header)?;
675 v.version_string = v.get_full_version();
676 versions.push(v);
677 }
678
679 self.cat_size -= 1;
680
681 Ok(Some(Package {
682 name,
683 description,
684 homepage,
685 licenses,
686 versions,
687 category: self.cat_name.clone(),
688 }))
689 }
690}
691
692#[cfg(test)]
694mod tests {
695 use super::*;
696
697 #[test]
698 fn test_magic_bytes() {
699 assert_eq!(DB_MAGIC, b"eix\n");
700 assert_eq!(DB_MAGIC.len(), 4);
701 }
702
703 #[test]
704 fn test_version() {
705 assert_eq!(DB_VERSION_CURRENT, 39);
706 }
707
708 #[test]
709 fn test_string_hash() {
710 let mut hash = StringHash::new();
711 let idx1 = hash.add("test".to_string());
712 let idx2 = hash.add("another".to_string());
713 let idx3 = hash.add("test".to_string());
714
715 assert_eq!(idx1, 0);
716 assert_eq!(idx2, 1);
717 assert_eq!(idx1, idx3);
718 assert_eq!(hash.len(), 2);
719
720 assert_eq!(hash.get_string(0), Some("test"));
721 assert_eq!(hash.get_string(1), Some("another"));
722 assert_eq!(hash.get_string(2), None);
723
724 assert_eq!(hash.get_index("test"), Some(0));
725 assert_eq!(hash.get_index("another"), Some(1));
726 assert_eq!(hash.get_index("nonexistent"), None);
727 }
728
729 #[test]
730 fn test_part_type_from_u64() {
731 assert_eq!(PartType::from_u64(1), PartType::Alpha);
732 assert_eq!(PartType::from_u64(5), PartType::Revision);
733 assert_eq!(PartType::from_u64(10), PartType::First);
734 assert_eq!(PartType::from_u64(0), PartType::Garbage);
735 assert_eq!(PartType::from_u64(99), PartType::Garbage);
736 }
737
738 struct MockDatabase {
740 data: Vec<u8>,
741 pos: usize,
742 }
743
744 impl MockDatabase {
745 fn new(data: Vec<u8>) -> Self {
746 MockDatabase { data, pos: 0 }
747 }
748
749 fn read_uchar(&mut self) -> io::Result<u8> {
750 if self.pos >= self.data.len() {
751 return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "EOF"));
752 }
753 let val = self.data[self.pos];
754 self.pos += 1;
755 Ok(val)
756 }
757
758 fn read_num(&mut self) -> io::Result<u64> {
760 let ch = self.read_uchar()?;
761 if ch != MAGICNUMCHAR {
762 return Ok(ch as u64);
763 }
764 let mut to_get = 1usize;
765 let mut result: u64;
766 loop {
767 let c = self.read_uchar()?;
768 if c == MAGICNUMCHAR {
769 to_get += 1;
770 continue;
771 }
772 if c != 0 {
773 result = c as u64;
774 } else {
775 result = MAGICNUMCHAR as u64;
776 to_get -= 1;
777 }
778 break;
779 }
780 for _ in 0..to_get {
781 let byte = self.read_uchar()?;
782 result = (result << 8) | (byte as u64);
783 }
784 Ok(result)
785 }
786 }
787
788 #[test]
789 fn test_read_num() {
790 let cases = vec![
791 (0x00, vec![0x00]),
792 (0xFE, vec![0xFE]),
793 (0xFF, vec![0xFF, 0x00]),
794 (0x0100, vec![0xFF, 0x01, 0x00]),
795 (0x01FF, vec![0xFF, 0x01, 0xFF]),
796 (0xFEFF, vec![0xFF, 0xFE, 0xFF]),
797 (0xFF00, vec![0xFF, 0xFF, 0x00, 0x00]),
798 (0xFF01, vec![0xFF, 0xFF, 0x00, 0x01]),
799 (0x010000, vec![0xFF, 0xFF, 0x01, 0x00, 0x00]),
800 (0xABCDEF, vec![0xFF, 0xFF, 0xAB, 0xCD, 0xEF]),
801 (0xFFABCD, vec![0xFF, 0xFF, 0xFF, 0x00, 0xAB, 0xCD]),
802 (0x01ABCDEF, vec![0xFF, 0xFF, 0xFF, 0x01, 0xAB, 0xCD, 0xEF]),
803 ];
804
805 for (expected, bytes) in cases {
806 let mut db = MockDatabase::new(bytes.clone());
807
808 let result = db.read_num().expect(&format!("Failed to read {:?}", bytes));
809 assert_eq!(
810 result, expected,
811 "Case {:?} failed: expected 0x{:X}, got 0x{:X}",
812 bytes, expected, result
813 );
814 }
815 }
816
817 #[test]
818 fn test_version_full_string() {
819 let v = Version {
820 version_string: "1.2.3".to_string(),
821 parts: vec![
822 BasicPart {
823 part_type: PartType::First,
824 part_content: "1".to_string(),
825 },
826 BasicPart {
827 part_type: PartType::Primary,
828 part_content: "2".to_string(),
829 },
830 BasicPart {
831 part_type: PartType::Primary,
832 part_content: "3".to_string(),
833 },
834 BasicPart {
835 part_type: PartType::Alpha,
836 part_content: "1".to_string(),
837 },
838 BasicPart {
839 part_type: PartType::Revision,
840 part_content: "1".to_string(),
841 },
842 ],
843 eapi: "8".to_string(),
844 mask_flags: 0,
845 properties_flags: 0,
846 restrict_flags: 0,
847 keywords: vec![],
848 slot: "0".to_string(),
849 overlay_key: 0,
850 reponame: "gentoo".to_string(),
851 priority: 0,
852 iuse: vec![],
853 required_use: vec![],
854 depend: None,
855 src_uri: None,
856 };
857 assert_eq!(v.get_full_version(), "1.2.3_alpha1-r1");
858 }
859}