1use std::collections::{
28 BTreeMap,
29 LinkedList,
30};
31use std::fmt;
32use std::io::{
33 Error,
34 ErrorKind,
35 Result,
36};
37
38use ::more_asserts::{
39 debug_assert_ge,
40 debug_assert_le,
41};
42#[allow(unused_imports)]
43use ::si_trace_print::{
44 de,
45 def1n,
46 def1o,
47 def1x,
48 def1ñ,
49 defn,
50 defo,
51 defx,
52 defñ,
53 den,
54 deo,
55 dex,
56 deñ,
57 pfn,
58 pfo,
59 pfx,
60};
61
62use crate::common::{
63 Count,
64 FPath,
65 FileOffset,
66 FileSz,
67 FileType,
68 FileTypeFixedStruct,
69 ResultFind,
70 debug_panic,
71 summary_stat,
72 summary_stats_enabled,
73};
74use crate::data::datetime::{
75 dt_after_or_before,
76 dt_pass_filters,
77 DateTimeL,
78 DateTimeLOpt,
79 FixedOffset,
80 Result_Filter_DateTime1,
81 Result_Filter_DateTime2,
82 SystemTime,
83};
84use crate::data::fixedstruct::{
85 buffer_to_fixedstructptr,
86 convert_datetime_tvpair,
87 filesz_to_types,
88 tv_pair_type,
89 FixedStruct,
90 FixedStructDynPtr,
91 FixedStructType,
92 FixedStructTypeSet,
93 Score,
94 ENTRY_SZ_MAX,
95 ENTRY_SZ_MIN,
96 TIMEVAL_SZ_MAX,
97};
98use crate::readers::blockreader::{
99 BlockIndex,
100 BlockOffset,
101 BlockReader,
102 BlockSz,
103 ResultReadDataToBuffer,
104};
105use crate::readers::summary::Summary;
106use crate::{
107 de_err,
108 de_wrn,
109 e_err,
110};
111
112pub type FoToEntry = BTreeMap<FileOffset, FixedStruct>;
123
124pub type FoToFo = BTreeMap<FileOffset, FileOffset>;
128
129pub type FoList = LinkedList<FileOffset>;
130
131type MapTvPairToFo = BTreeMap<tv_pair_type, FileOffset>;
132
133pub type ResultFindFixedStruct = ResultFind<(FileOffset, FixedStruct), (Option<FileOffset>, Error)>;
137
138pub type ResultFindFixedStructProcZeroBlock = ResultFind<(FixedStructType, Score, ListFileOffsetFixedStructPtr), Error>;
139
140pub type ResultTvFo = Result<(usize, usize, usize, usize, MapTvPairToFo)>;
141
142#[cfg(test)]
143pub type DroppedBlocks = LinkedList<BlockOffset>;
144
145type ListFileOffsetFixedStructPtr = LinkedList<(FileOffset, FixedStructDynPtr)>;
146
147#[derive(Debug)]
149pub enum ResultFixedStructReaderNew<E> {
150 FileOk(FixedStructReader),
153 FileErrEmpty,
154 FileErrTooSmall(String),
155 FileErrNoValidFixedStruct,
157 FileErrNoFixedStructWithinDtFilters,
159 FileErrIo(E),
162}
163
164pub type ResultFixedStructReaderNewError = ResultFixedStructReaderNew<Error>;
165
166#[derive(Debug)]
167pub enum ResultFixedStructReaderScoreFile<E> {
168 FileOk(FixedStructType, Score, ListFileOffsetFixedStructPtr),
171 FileErrEmpty,
172 FileErrNoValidFixedStruct,
174 FileErrNoHighScore,
176 FileErrIo(E),
179}
180
181pub type ResultFixedStructReaderScoreFileError = ResultFixedStructReaderScoreFile<Error>;
182
183pub struct FixedStructReader {
216 pub(crate) blockreader: BlockReader,
217 fixedstruct_type: FixedStructType,
218 filetype_fixedstruct: FileTypeFixedStruct,
219 fixedstruct_size: usize,
221 high_score: Score,
224 tz_offset: FixedOffset,
229 pub(crate) cache_entries: FoToEntry,
235 pub(crate) map_tvpair_fo: MapTvPairToFo,
241 pub(crate) block_use_count: BTreeMap<BlockOffset, usize>,
242 pub(crate) first_entry_fileoffset: FileOffset,
244 pub(crate) entries_stored_highest: usize,
246 pub(crate) entries_out_of_order: usize,
247 pub(super) entries_hits: Count,
249 pub(super) entries_miss: Count,
251 pub(super) entries_processed: Count,
255 pub(super) dt_first: DateTimeLOpt,
259 pub(super) dt_last: DateTimeLOpt,
263 pub(super) drop_entry_ok: Count,
266 pub(super) drop_entry_errors: Count,
269 #[cfg(test)]
271 pub(crate) dropped_blocks: DroppedBlocks,
272 pub(super) map_tvpair_fo_max_len: usize,
273 error: Option<String>,
282}
283
284impl fmt::Debug for FixedStructReader {
285 fn fmt(
286 &self,
287 f: &mut fmt::Formatter,
288 ) -> fmt::Result {
289 f.debug_struct("FixedStructReader")
290 .field("Path", &self.path())
291 .field("Entries", &self.cache_entries.len())
292 .field("tz_offset", &self.tz_offset)
293 .field("Error?", &self.error)
294 .finish()
295 }
296}
297
298#[allow(non_snake_case)]
303#[derive(Clone, Default, Eq, PartialEq, Debug)]
304pub struct SummaryFixedStructReader {
305 pub fixedstructreader_fixedstructtype_opt: Option<FixedStructType>,
306 pub fixedstructreader_filetypefixedstruct_opt: Option<FileTypeFixedStruct>,
307 pub fixedstructreader_fixedstruct_size: usize,
308 pub fixedstructreader_high_score: Score,
309 pub fixedstructreader_utmp_entries: Count,
310 pub fixedstructreader_first_entry_fileoffset: FileOffset,
311 pub fixedstructreader_entries_out_of_order: usize,
312 pub fixedstructreader_utmp_entries_max: Count,
313 pub fixedstructreader_utmp_entries_hit: Count,
314 pub fixedstructreader_utmp_entries_miss: Count,
315 pub fixedstructreader_drop_entry_ok: Count,
316 pub fixedstructreader_drop_entry_errors: Count,
317 pub fixedstructreader_datetime_first: DateTimeLOpt,
319 pub fixedstructreader_datetime_last: DateTimeLOpt,
321 pub fixedstructreader_map_tvpair_fo_max_len: usize,
322}
323
324impl FixedStructReader {
326 pub fn new(
337 path: FPath,
338 filetype: FileType,
339 blocksz: BlockSz,
340 tz_offset: FixedOffset,
341 dt_filter_after: DateTimeLOpt,
342 dt_filter_before: DateTimeLOpt,
343 ) -> ResultFixedStructReaderNewError {
344 def1n!(
345 "({:?}, filetype={:?}, blocksz={:?}, {:?}, {:?}, {:?})",
346 path, filetype, blocksz, tz_offset, dt_filter_after, dt_filter_before,
347 );
348 let mut blockreader = match BlockReader::new(path.clone(), filetype, blocksz) {
349 Ok(blockreader_) => blockreader_,
350 Err(err) => {
351 def1x!("return Err {}", err);
352 return ResultFixedStructReaderNew::FileErrIo(err);
354 }
355 };
356 let filetype_fixedstruct = match filetype {
357 FileType::FixedStruct { archival_type: _, fixedstruct_type: type_ } => type_,
358 _ => {
359 debug_panic!("Unexpected FileType: {:?}", filetype);
360 return ResultFixedStructReaderNew::FileErrIo(Error::new(
361 ErrorKind::InvalidData,
362 format!("Unexpected FileType {:?}", filetype),
363 ));
364 }
365 };
366
367 const ENTRY_SZ_MIN_FSZ: FileSz = ENTRY_SZ_MIN as FileSz;
368 if blockreader.filesz() == 0 {
369 def1x!("return FileErrEmpty");
370 return ResultFixedStructReaderNew::FileErrEmpty;
371 } else if blockreader.filesz() < ENTRY_SZ_MIN_FSZ {
372 def1x!(
373 "return FileErrTooSmall; {} < {} (ENTRY_SZ_MIN)",
374 blockreader.filesz(), ENTRY_SZ_MIN_FSZ
375 );
376 return ResultFixedStructReaderNew::FileErrTooSmall(
377 format!(
378 "file size {} < {} (ENTRY_SZ_MIN), file {:?}",
379 blockreader.filesz(), ENTRY_SZ_MIN_FSZ, path,
380 )
381 );
382 }
383
384 let (
389 fixedstruct_type,
390 high_score,
391 list_entries,
392 ) = match FixedStructReader::preprocess_fixedstructtype(
393 &mut blockreader, &filetype_fixedstruct, false,
394 ) {
395 ResultFixedStructReaderScoreFileError::FileOk(
396 fixedstruct_type_, high_score_, list_entries_,
397 ) => (fixedstruct_type_, high_score_, list_entries_),
398 ResultFixedStructReaderScoreFileError::FileErrEmpty => {
399 def1x!("return FileErrEmpty");
400 return ResultFixedStructReaderNew::FileErrEmpty;
401 }
402 ResultFixedStructReaderScoreFileError::FileErrNoHighScore => {
403 def1x!("return FileErrNoHighScore");
404 return ResultFixedStructReaderNew::FileErrNoValidFixedStruct;
405 }
406 ResultFixedStructReaderScoreFileError::FileErrNoValidFixedStruct => {
407 def1x!("return FileErrNoValidFixedStruct");
408 return ResultFixedStructReaderNew::FileErrNoValidFixedStruct;
409 }
410 ResultFixedStructReaderScoreFileError::FileErrIo(err) => {
411 de_err!("FixedStructReader::preprocess_fixedstructtype Error {}; file {:?}",
412 err, blockreader.path());
413 def1x!("return Err {:?}", err);
414 return ResultFixedStructReaderNew::FileErrIo(err);
415 }
416 };
417
418 let (total, invalid, valid_no_filter, out_of_order, map_tvpair_fo) =
419 match FixedStructReader::preprocess_timevalues(
420 &mut blockreader,
421 fixedstruct_type,
422 &dt_filter_after,
423 &dt_filter_before,
424 )
425 {
426 ResultTvFo::Err(err) => {
427 de_err!("FixedStructReader::preprocess_timevalues Error {}; file {:?}",
428 err, blockreader.path());
429 def1x!("return Err {:?}", err);
430 return ResultFixedStructReaderNew::FileErrIo(err);
431 }
432 ResultTvFo::Ok(
433 (total_, invalid_, valid_no_filter_, out_of_order_, map_tvpair_fo_)
434 ) =>
435 (total_, invalid_, valid_no_filter_, out_of_order_, map_tvpair_fo_),
436 };
437 def1o!("total: {}, invalid: {}, valid_no_filter: {}, out_of_order: {}",
438 total, invalid, valid_no_filter, out_of_order);
439 #[cfg(debug_assertions)]
440 {
441 def1o!("map_tvpair_fo has {} entries", map_tvpair_fo.len());
442 for (_tv_pair, _fo) in map_tvpair_fo.iter() {
443 def1o!("map_tvpair_fo: [tv_pair: {:?}] = fo: {}", _tv_pair, _fo);
444 }
445 }
446 debug_assert_ge!(total, invalid);
447 debug_assert_ge!(total, valid_no_filter);
448
449 if map_tvpair_fo.is_empty() {
450 if valid_no_filter > 0 {
451 def1x!("return FileErrNoFixedStructWithinDtFilters");
452 return ResultFixedStructReaderNew::FileErrNoFixedStructWithinDtFilters;
453 }
454 def1x!("return FileErrNoValidFixedStruct");
455 return ResultFixedStructReaderNew::FileErrNoValidFixedStruct;
456 }
457
458 let mut first_entry_fileoffset: FileOffset = blockreader.filesz();
460 for (_tv_pair, fo) in map_tvpair_fo.iter() {
461 if &first_entry_fileoffset > fo {
462 first_entry_fileoffset = *fo;
463 }
464 }
465 debug_assert_ne!(first_entry_fileoffset, blockreader.filesz(), "failed to update first_entry_fileoffset");
466
467 let mut block_use_count: BTreeMap<BlockOffset, usize> = BTreeMap::new();
470 def1o!("block_use_count create");
471 for (_tv_pair, fo) in map_tvpair_fo.iter() {
472 let bo_beg: BlockOffset = BlockReader::block_offset_at_file_offset(*fo, blocksz);
473 let fo_end: FileOffset = *fo + fixedstruct_type.size() as FileOffset;
474 let bo_end: BlockOffset = BlockReader::block_offset_at_file_offset(fo_end, blocksz);
475 def1o!("blocksz = {}", blocksz);
476 for bo in bo_beg..bo_end + 1 {
477 match block_use_count.get_mut(&bo) {
478 Some(count) => {
479 let count_ = *count + 1;
480 def1o!(
481 "block_use_count[{}] += 1 ({}); [{}‥{}]; total span [{}‥{})",
482 bo,
483 count_,
484 *fo,
485 fo_end,
486 BlockReader::file_offset_at_block_offset(bo_beg, blocksz),
487 BlockReader::file_offset_at_block_offset(bo_end + 1, blocksz),
488 );
489 *count = count_;
490 }
491 None => {
492 def1o!(
493 "block_use_count[{}] = 1; [{}‥{}]; total span [{}‥{})",
494 bo,
495 *fo,
496 fo_end,
497 BlockReader::file_offset_at_block_offset(bo_beg, blocksz),
498 BlockReader::file_offset_at_block_offset(bo_end + 1, blocksz),
499 );
500 block_use_count.insert(bo, 1);
501 }
502 }
503 }
504 }
505 #[cfg(debug_assertions)]
506 {
507 for (bo, count) in block_use_count.iter() {
508 def1o!(
509 "block_use_count[{}] = {}; total span [{}‥{}]",
510 bo,
511 count,
512 BlockReader::file_offset_at_block_offset(*bo, blocksz),
513 BlockReader::file_offset_at_block_offset(*bo + 1, blocksz),
514 );
515 }
516 }
517
518 let map_max_len = map_tvpair_fo.len();
519 let mut fixedstructreader = FixedStructReader {
521 blockreader,
522 fixedstruct_type,
523 filetype_fixedstruct,
524 fixedstruct_size: fixedstruct_type.size(),
525 high_score,
526 tz_offset,
527 cache_entries: FoToEntry::new(),
528 map_tvpair_fo,
529 block_use_count,
530 first_entry_fileoffset,
531 entries_stored_highest: 0,
532 entries_out_of_order: out_of_order,
533 entries_hits: 0,
534 entries_miss: 0,
535 entries_processed: 0,
536 dt_first: DateTimeLOpt::None,
537 dt_last: DateTimeLOpt::None,
538 drop_entry_ok: 0,
539 drop_entry_errors: 0,
540 #[cfg(test)]
541 dropped_blocks: DroppedBlocks::new(),
542 map_tvpair_fo_max_len: map_max_len,
543 error: None,
544 };
545
546 for (fo, fixedstructptr) in list_entries.into_iter() {
549 match FixedStruct::from_fixedstructptr(fo, &tz_offset, fixedstructptr) {
555 Ok(fixedstruct) => {
556 if fixedstructreader
557 .map_tvpair_fo
558 .iter()
559 .any(|(_tv_pair, fo2)| &fo == fo2)
560 {
561 def1o!("insert entry at fo {}", fo);
562 fixedstructreader.insert_cache_entry(fixedstruct);
563 } else {
564 def1o!("skip entry at fo {}; not in map_tvpair_fo", fo);
565 }
566 }
567 Err(err) => {
568 de_err!("FixedStruct::from_fixedstructptr Error {}; file {:?}", err, fixedstructreader.path());
569 fixedstructreader.set_error(&err);
570 }
571 }
572 }
573
574 def1x!("return FileOk(FixedStructReader)");
575
576 ResultFixedStructReaderNew::FileOk(fixedstructreader)
577 }
578
579 #[inline(always)]
583 pub const fn blocksz(&self) -> BlockSz {
584 self.blockreader.blocksz()
585 }
586
587 #[inline(always)]
591 pub const fn filesz(&self) -> FileSz {
592 self.blockreader.filesz()
593 }
594
595 #[inline(always)]
599 pub const fn filetype(&self) -> FileType {
600 self.blockreader.filetype()
601 }
602
603 #[inline(always)]
607 pub const fn path(&self) -> &FPath {
608 self.blockreader.path()
609 }
610
611 pub fn mtime(&self) -> SystemTime {
615 self.blockreader.mtime()
616 }
617
618 #[inline(always)]
621 pub fn count_entries_processed(&self) -> Count {
622 self.entries_processed
623 }
624
625 #[inline(always)]
627 pub fn entries_stored_highest(&self) -> usize {
628 self.entries_stored_highest
629 }
630
631 #[inline(always)]
635 pub const fn block_offset_at_file_offset(
636 &self,
637 fileoffset: FileOffset,
638 ) -> BlockOffset {
639 BlockReader::block_offset_at_file_offset(fileoffset, self.blocksz())
640 }
641
642 #[inline(always)]
646 pub const fn file_offset_at_block_offset(
647 &self,
648 blockoffset: BlockOffset,
649 ) -> FileOffset {
650 BlockReader::file_offset_at_block_offset(blockoffset, self.blocksz())
651 }
652
653 #[inline(always)]
657 pub const fn file_offset_at_block_offset_index(
658 &self,
659 blockoffset: BlockOffset,
660 blockindex: BlockIndex,
661 ) -> FileOffset {
662 BlockReader::file_offset_at_block_offset_index(blockoffset, self.blocksz(), blockindex)
663 }
664
665 #[inline(always)]
669 pub const fn block_index_at_file_offset(
670 &self,
671 fileoffset: FileOffset,
672 ) -> BlockIndex {
673 BlockReader::block_index_at_file_offset(fileoffset, self.blocksz())
674 }
675
676 #[inline(always)]
680 pub const fn count_blocks(&self) -> Count {
681 BlockReader::count_blocks(self.filesz(), self.blocksz()) as Count
682 }
683
684 pub const fn blockoffset_last(&self) -> BlockOffset {
688 self.blockreader
689 .blockoffset_last()
690 }
691
692 pub const fn fileoffset_last(&self) -> FileOffset {
696 self.blockreader
697 .fileoffset_last()
698 }
699
700 pub const fn is_fileoffset_last(
702 &self,
703 fileoffset: FileOffset,
704 ) -> bool {
705 self.fileoffset_last() == fileoffset
706 }
707
708 #[inline(always)]
710 pub fn is_last(
711 &self,
712 fixedstruct: &FixedStruct,
713 ) -> bool {
714 self.is_fileoffset_last(fixedstruct.fileoffset_end() - 1)
715 }
716
717 #[inline(always)]
720 pub const fn fileoffset_to_fixedstructoffset(
721 &self,
722 fileoffset: FileOffset,
723 ) -> FileOffset {
724 (fileoffset / self.fixedstruct_size_fo()) * self.fixedstruct_size_fo()
725 }
726
727 pub fn fileoffset_first(&self) -> Option<FileOffset> {
731 match self
732 .map_tvpair_fo
733 .iter()
734 .min_by_key(|(tv_pair, fo)| (*tv_pair, *fo))
735 {
736 Some((_tv_pair, fo_)) => Some(*fo_),
737 None => None,
738 }
739 }
740
741 #[inline(always)]
744 pub const fn fixedstruct_size(&self) -> usize {
745 self.fixedstruct_size
746 }
747
748 #[inline(always)]
752 pub const fn fixedstruct_size_fo(&self) -> FileOffset {
753 self.fixedstruct_size() as FileOffset
754 }
755
756 #[inline(always)]
758 pub const fn fixedstruct_type(&self) -> FixedStructType {
759 self.fixedstruct_type
760 }
761
762 #[cfg(test)]
766 pub fn get_fileoffsets(&self) -> Vec<FileOffset> {
767 self.cache_entries
768 .keys()
769 .cloned()
770 .collect()
771 }
772
773 fn set_error(
776 &mut self,
777 error: &Error,
778 ) {
779 def1ñ!("{:?}", error);
780 let mut error_string: String = error.kind().to_string();
781 error_string.push_str(": ");
782 error_string.push_str(error.kind().to_string().as_str());
783 match &self.error {
789 Some(err_s) => {
790 if err_s != &error_string {
791 e_err!("{}", error);
792 }
793 }
794 None => {
795 e_err!("{}", error);
796 }
797 }
798 if let Some(ref _err) = self.error {
799 de_wrn!("skip overwrite of previous Error ({:?}) with Error ({:?})", _err, error);
800 return;
801 }
802 self.error = Some(error_string);
803 }
804
805 fn insert_cache_entry(
811 &mut self,
812 entry: FixedStruct,
813 ) {
814 defn!("@{}", entry.fileoffset_begin());
815 let fo_beg: FileOffset = entry.fileoffset_begin();
816 debug_assert!(
817 !self.cache_entries.contains_key(&fo_beg),
818 "self.cache_entries already contains key {}",
819 fo_beg
820 );
821
822 self.cache_entries
824 .insert(fo_beg, entry);
825 self.entries_stored_highest = std::cmp::max(self.entries_stored_highest, self.cache_entries.len());
826 self.entries_processed += 1;
827 defo!("entries_processed = {}", self.entries_processed);
828
829 defx!();
830 }
831
832 fn dt_first_last_update(
835 &mut self,
836 datetime: &DateTimeL,
837 ) {
838 if !summary_stats_enabled() {
839 return;
840 }
841 defñ!("({:?})", datetime);
842 match self.dt_first {
843 Some(dt_first_) => {
844 if &dt_first_ > datetime {
845 self.dt_first = Some(*datetime);
846 }
847 }
848 None => {
849 self.dt_first = Some(*datetime);
850 }
851 }
852 match self.dt_last {
853 Some(dt_last_) => {
854 if &dt_last_ < datetime {
855 self.dt_last = Some(*datetime);
856 }
857 }
858 None => {
859 self.dt_last = Some(*datetime);
860 }
861 }
862 }
863
864 fn drop_entry(
873 &mut self,
874 fixedstruct: &FixedStruct,
875 ) -> usize {
876 let bsz: BlockSz = self.blocksz();
877 defn!(
878 "(fixedstruct@{}); offsets [{}‥{}), blocks [{}‥{}]",
879 fixedstruct.fileoffset_begin(),
880 fixedstruct.fileoffset_begin(),
881 fixedstruct.fileoffset_end(),
882 fixedstruct.blockoffset_begin(bsz),
883 fixedstruct.blockoffset_end(bsz),
884 );
885 let mut dropped_ok: usize = 0;
886 let mut dropped_err: usize = 0;
887 let mut bo_at: BlockOffset = fixedstruct.blockoffset_begin(bsz);
888 let bo_end: BlockOffset = fixedstruct.blockoffset_end(bsz);
889 debug_assert_le!(bo_at, bo_end);
890 while bo_at <= bo_end {
891 defo!("block_use_count.get_mut({})", bo_at);
892 match self.block_use_count.get_mut(&bo_at) {
893 Some(count) => {
894 if *count <= 1 {
895 defo!(
896 "block_use_count[{}] found; count=={}; total span [{}‥{})",
897 bo_at, count,
898 BlockReader::file_offset_at_block_offset(bo_at, bsz),
899 BlockReader::file_offset_at_block_offset(bo_end + 1, bsz),
900 );
901 if self
902 .blockreader
903 .drop_block(bo_at)
904 {
905 defo!(
906 "dropped block {}; total span [{}‥{})",
907 bo_at,
908 BlockReader::file_offset_at_block_offset(bo_at, bsz),
909 BlockReader::file_offset_at_block_offset(bo_end + 1, bsz),
910 );
911 self.block_use_count.remove(&bo_at);
912 #[cfg(test)]
913 self.dropped_blocks.push_back(bo_at);
914 dropped_ok += 1;
915 } else {
916 defo!("failed to drop block {}", bo_at);
917 dropped_err += 1;
918 }
919 } else {
920 *count -= 1;
921 defo!("block_use_count[{}] found; count-=1=={}", bo_at, *count);
922 }
923 }
924 None => {
925 defo!("block_use_count[{}] not found", bo_at);
926 }
927 }
928 bo_at += 1;
929 }
930 summary_stat!(
931 if dropped_ok > 0 {
932 self.drop_entry_ok += 1;
933 }
934 );
935 summary_stat!(
936 if dropped_err > 0 {
937 self.drop_entry_errors += 1;
938 }
939 );
940 defx!("return {}", dropped_ok);
941
942 dropped_ok
943 }
944
945 #[inline(always)]
948 fn remove_cache_entry(
949 &mut self,
950 fileoffset: FileOffset,
951 ) -> Option<FixedStruct> {
952 match self.cache_entries.remove(&fileoffset) {
953 Some(fixedstruct) => {
954 defñ!("({}): found in store", fileoffset);
955 self.entries_hits += 1;
956
957 Some(fixedstruct)
958 }
959 None => {
960 defñ!("({}): not found in store", fileoffset);
961 self.entries_miss += 1;
962
963 None
964 }
965 }
966 }
967
968 pub fn score_file(
980 blockreader: &mut BlockReader,
981 oneblock: bool,
982 types_to_bonus: FixedStructTypeSet,
983 ) -> ResultFixedStructReaderScoreFileError {
984 def1n!("(oneblock={}, types_to_bonus len {})", oneblock, types_to_bonus.len());
985 #[cfg(debug_assertions)]
986 {
987 for (fixedstructtype, bonus) in types_to_bonus.iter() {
988 def1o!("types_to_bonus: ({:<30?}, {:2}) size {}", fixedstructtype, bonus, fixedstructtype.size(),);
989 }
990 }
991 let mut buffer: [u8; ENTRY_SZ_MAX] = [0; ENTRY_SZ_MAX];
993 #[cfg(not(test))]
996 const COUNT_FOUND_ENTRIES_MAX: usize = 5;
997 #[cfg(test)]
998 const COUNT_FOUND_ENTRIES_MAX: usize = 2;
999 let mut _count_total: usize = 0;
1000 let mut highest_score: Score = 0;
1001 let mut highest_score_type: Option<FixedStructType> = None;
1002 let mut highest_score_entries = ListFileOffsetFixedStructPtr::new();
1003
1004 for (fixedstructtype, bonus) in types_to_bonus.into_iter() {
1005 let mut _count_loop: usize = 0;
1006 let mut count_found_entries: usize = 0;
1007 let mut high_score: Score = 0;
1008 let mut fo: FileOffset = 0;
1009 let mut found_entries = ListFileOffsetFixedStructPtr::new();
1010
1011 loop {
1012 if count_found_entries >= COUNT_FOUND_ENTRIES_MAX {
1013 def1o!("count_found_entries {} >= COUNT_FOUND_ENTRIES_MAX {}", count_found_entries, COUNT_FOUND_ENTRIES_MAX);
1014 break;
1015 }
1016 _count_total += 1;
1017 _count_loop += 1;
1018 let utmp_sz: usize = fixedstructtype.size();
1019 let fo_end = fo + utmp_sz as FileOffset;
1020 def1o!(
1021 "loop try {} (total {}), fixedstructtype {:?}, zero the buffer (size {}), looking at fileoffset {}‥{} (0x{:08X}‥0x{:08X})",
1022 _count_loop, _count_total, fixedstructtype, buffer.len(), fo, fo_end, fo, fo_end
1023 );
1024 buffer.iter_mut().for_each(|m| *m = 0);
1031 let buffer_read: usize = match blockreader.read_data_to_buffer(fo, fo_end, oneblock, &mut buffer) {
1033 ResultReadDataToBuffer::Found(buffer_read) => buffer_read,
1034 ResultReadDataToBuffer::Err(err) => {
1035 def1x!("return Err");
1036 return ResultFixedStructReaderScoreFileError::FileErrIo(err);
1037 }
1038 ResultReadDataToBuffer::Done => {
1039 break;
1041 }
1042 };
1043 if buffer_read < utmp_sz {
1044 def1o!(
1045 "read_data_to_buffer read bytes {} < {} requested fixedstruct size bytes; break",
1046 buffer_read,
1047 utmp_sz,
1048 );
1049 break;
1050 }
1051 let fo2 = fo;
1053 fo += utmp_sz as FileOffset;
1054 let slice_ = &buffer[..buffer_read];
1056 let fixedstructptr: FixedStructDynPtr = match buffer_to_fixedstructptr(slice_, fixedstructtype) {
1058 Some(val) => val,
1059 None => {
1060 def1o!(
1061 "buffer_to_fixedstructptr(buf len {}, {:?}) returned None; continue",
1062 buffer.len(),
1063 fixedstructtype,
1064 );
1065 continue;
1066 }
1067 };
1068 count_found_entries += 1;
1069 let score: Score = FixedStruct::score_fixedstruct(&fixedstructptr, bonus);
1071 def1o!("score {} for entry type {:?} @[{}‥{}]", score, fixedstructptr.fixedstruct_type(), fo2, fo_end);
1072 let _fs_type: FixedStructType = fixedstructptr.fixedstruct_type();
1074 found_entries.push_back((fo2, fixedstructptr));
1075 if score <= high_score {
1076 def1o!("score {} ({:?}) not higher than high score {}", score, _fs_type, high_score,);
1077 continue;
1079 }
1080 def1o!("new high score {} for entry type {:?} @[{}‥{}]", score, _fs_type, fo2, fo_end);
1082 high_score = score;
1083 }
1084 if high_score > highest_score {
1087 match highest_score_type {
1090 None => {
1091 def1o!(
1092 "new highest score {} entry type {:?} with {} entries",
1093 high_score, fixedstructtype, found_entries.len(),
1094 );
1095 }
1096 Some(_highest_score_type) => {
1097 def1o!(
1098 "new highest score {} entry type {:?} with {} entries; replaces old high score {} entry type {:?} with {} entries (entries dropped)",
1099 high_score, fixedstructtype, found_entries.len(),
1100 highest_score, _highest_score_type, highest_score_entries.len(),
1101 );
1102 }
1103 }
1104 highest_score = high_score;
1105 highest_score_type = Some(fixedstructtype);
1106 highest_score_entries = found_entries;
1107 } else {
1108 def1o!(
1109 "no new highest score: score {} entry type {:?} with {} entries. old high score remains: score {} entry type {:?} with {} entries",
1110 high_score, fixedstructtype, found_entries.len(),
1111 highest_score, highest_score_type, highest_score_entries.len(),
1112 );
1113 }
1114 }
1115
1116 match highest_score_type {
1117 None => {
1118 def1x!("return Err {:?}", ResultFixedStructReaderScoreFileError::FileErrNoHighScore);
1119 return ResultFixedStructReaderScoreFileError::FileErrNoHighScore;
1120 }
1121 Some(highest_score_type) => {
1122 def1x!("return Ok(({:?}, {}, found_entries))", highest_score_type, highest_score);
1123
1124 ResultFixedStructReaderScoreFileError::FileOk(highest_score_type, highest_score, highest_score_entries)
1125 }
1126 }
1127 }
1128
1129 pub(crate) fn preprocess_fixedstructtype(
1144 blockreader: &mut BlockReader,
1145 filetype_fixedstruct: &FileTypeFixedStruct,
1146 oneblock: bool,
1147 ) -> ResultFixedStructReaderScoreFileError {
1148 def1n!("({:?}, oneblock={})", filetype_fixedstruct, oneblock);
1149
1150 if blockreader.filesz() == 0 {
1152 def1x!("empty file; return FileErrEmpty");
1153 return ResultFixedStructReaderScoreFileError::FileErrEmpty;
1154 }
1155
1156 let types_to_bonus: FixedStructTypeSet = match filesz_to_types(
1157 blockreader.filesz(),
1158 filetype_fixedstruct,
1159 ) {
1160 Some(set) => set,
1161 None => {
1162 de_wrn!("FixedStructReader::filesz_to_types({}) failed; file {:?}",
1163 blockreader.filesz(), blockreader.path());
1164 def1x!("filesz_to_types returned None; return FileErrNoValidFixedStruct");
1165 return ResultFixedStructReaderScoreFileError::FileErrNoValidFixedStruct;
1166 }
1167 };
1168 def1o!("filesz_to_types returned {} types: {:?}", types_to_bonus.len(), types_to_bonus);
1169
1170 let ret = FixedStructReader::score_file(blockreader, oneblock, types_to_bonus);
1171 def1x!("score_file returned {:?}", ret);
1172
1173 ret
1174 }
1175
1176 pub(crate) fn preprocess_timevalues(
1183 blockreader: &mut BlockReader,
1184 fixedstruct_type: FixedStructType,
1185 dt_filter_after: &DateTimeLOpt,
1186 dt_filter_before: &DateTimeLOpt,
1187 ) -> ResultTvFo {
1188 defn!();
1189 let mut buffer: [u8; TIMEVAL_SZ_MAX] = [0; TIMEVAL_SZ_MAX];
1191 let mut map_tv_pair_fo: MapTvPairToFo = MapTvPairToFo::new();
1193 let mut out_of_order: usize = 0;
1195 let mut valid_no_pass_filter: usize = 0;
1197 let mut invalid: usize = 0;
1199 let mut total_entries: usize = 0;
1201
1202 let tv_filter_after: Option<tv_pair_type> = match dt_filter_after {
1203 Some(dt) => Some(convert_datetime_tvpair(dt)),
1204 None => None,
1205 };
1206 defo!("tv_filter_after: {:?}", tv_filter_after);
1207 let tv_filter_before: Option<tv_pair_type> = match dt_filter_before {
1208 Some(dt) => Some(convert_datetime_tvpair(dt)),
1209 None => None,
1210 };
1211 defo!("tv_filter_before: {:?}", tv_filter_before);
1212
1213 let entry_sz: FileOffset = fixedstruct_type.size() as FileOffset;
1215 debug_assert_eq!(blockreader.filesz() % entry_sz, 0, "file not a multiple of entry size {}", entry_sz);
1216 let tv_sz: usize = fixedstruct_type.size_tv();
1217 let tv_offset: usize = fixedstruct_type.offset_tv();
1218 let slice_: &mut [u8] = &mut buffer[..tv_sz];
1219 let mut fo: FileOffset = 0;
1220 let mut tv_pair_prev: Option<tv_pair_type> = None;
1221 loop {
1222 let beg: FileOffset = fo + tv_offset as FileOffset;
1224 let end: FileOffset = beg + tv_sz as FileOffset;
1225 match blockreader.read_data_to_buffer(beg, end, false, slice_) {
1226 ResultReadDataToBuffer::Found(_readn) => {
1227 defo!("read {} bytes at fileoffset {}", _readn, beg);
1228 debug_assert_eq!(
1229 _readn, tv_sz,
1230 "read {} bytes, expected {} bytes (size of a time value)",
1231 _readn, tv_sz,
1232 );
1233 }
1234 ResultReadDataToBuffer::Err(err) => {
1235 defx!("return Err");
1236 return ResultTvFo::Err(err);
1237 }
1238 ResultReadDataToBuffer::Done => {
1239 defo!("return Done");
1240 break;
1241 }
1242 }
1243 let tv_pair: tv_pair_type = match fixedstruct_type.tv_pair_from_buffer(slice_) {
1245 Some(pair) => pair,
1246 None => {
1247 de_err!("invalid entry at fileoffset {}", fo);
1248 defo!("invalid entry at fileoffset {}", fo);
1249 fo += entry_sz;
1250 invalid += 1;
1251 continue;
1252 }
1253 };
1254 defo!("tv_pair: {:?}", tv_pair);
1255 if tv_pair == tv_pair_type(0, 0) {
1256 defo!("tv_pair is (0, 0); continue");
1257 fo += entry_sz;
1258 continue;
1259 }
1260 match tv_pair_prev {
1261 Some(tv_pair_prev) => {
1262 if tv_pair < tv_pair_prev {
1263 out_of_order += 1;
1264 defo!(
1265 "out_of_order = {}; tv_pair = {:?}, tv_pair_prev = {:?}",
1266 out_of_order,
1267 tv_pair,
1268 tv_pair_prev,
1269 );
1270 }
1271 }
1272 None => {}
1273 }
1274 tv_pair_prev = Some(tv_pair);
1275 total_entries += 1;
1276 if let Some(tv_filter) = tv_filter_after {
1278 if tv_pair < tv_filter {
1279 defo!("tv_pair {:?} < {:?} tv_filter_after; continue", tv_pair, tv_filter);
1280 fo += entry_sz;
1281 valid_no_pass_filter += 1;
1282 continue;
1283 }
1284 }
1285 if let Some(tv_filter) = tv_filter_before {
1286 if tv_pair > tv_filter {
1287 defo!("tv_pair {:?} > {:?} tv_filter_before; continue", tv_pair, tv_filter);
1288 fo += entry_sz;
1289 valid_no_pass_filter += 1;
1290 continue;
1293 }
1294 }
1295 defo!("tv_pair {:?} @{} passes time value filters", tv_pair, fo);
1297 map_tv_pair_fo.insert(tv_pair, fo);
1298
1299 fo += entry_sz;
1300 }
1301 defx!("map_tv_pair_fo len {}", map_tv_pair_fo.len());
1303
1304 ResultTvFo::Ok((total_entries, invalid, valid_no_pass_filter, out_of_order, map_tv_pair_fo))
1305 }
1306
1307 pub fn process_entry_at(
1319 &mut self,
1320 fo: FileOffset,
1321 buffer: &mut [u8],
1322 ) -> ResultFindFixedStruct {
1323 defn!("({})", fo);
1324
1325 let sz: FileOffset = self.fixedstruct_size_fo();
1326 debug_assert_eq!(fo % sz, 0, "fileoffset {} not multiple of {}", fo, sz,);
1327 let fileoffset: FileOffset = fo - (fo % sz);
1328
1329 if fileoffset >= self.filesz() {
1330 defx!("return ResultFindFixedStruct::Done; fileoffset {} >= filesz {}", fileoffset, self.filesz());
1331 return ResultFindFixedStruct::Done;
1332 }
1333
1334 let fo_next: FileOffset = {
1342 let mut fo_next_: FileOffset = self.filesz();
1343 let mut next_pair: bool = false;
1344 let mut tv_pair_at_opt: Option<tv_pair_type> = None;
1345 for (tv_pair_at, fo_at) in self.map_tvpair_fo.iter() {
1348 if next_pair {
1349 defo!("set fo_next = {}", fo_at);
1350 fo_next_ = *fo_at;
1351 break;
1352 }
1353 if &fileoffset == fo_at {
1354 defo!("found fileoffset {} with key {:?} in map_tvpair_fo", fileoffset, tv_pair_at,);
1355 tv_pair_at_opt = Some(*tv_pair_at);
1356 next_pair = true;
1357 }
1358 }
1359 match tv_pair_at_opt {
1361 Some(tv_pair_at) => {
1362 self.map_tvpair_fo
1363 .remove(&tv_pair_at);
1364 defo!("remove tv_pair {:?}; map_tvpair_fo size {}", tv_pair_at, self.map_tvpair_fo.len());
1365 }
1366 None => {
1367 defo!("no map_tvpair_fo found!");
1368 }
1369 }
1370
1371 fo_next_
1372 };
1373 defo!("fo_next = {}", fo_next);
1374
1375 if let Some(fixedstruct) = self.remove_cache_entry(fileoffset) {
1377 summary_stat!(self.dt_first_last_update(fixedstruct.dt()));
1378 self.drop_entry(&fixedstruct);
1380 defx!("remove_cache_entry found fixedstruct at fileoffset {}; return Found({}, …)", fileoffset, fo_next,);
1381 return ResultFindFixedStruct::Found((fo_next, fixedstruct));
1382 }
1383
1384 if buffer.len() < sz as usize {
1389 defx!("return ResultFindFixedStruct::Err");
1390 return ResultFindFixedStruct::Err((
1391 None,
1392 Error::new(
1393 ErrorKind::InvalidData,
1394 format!(
1395 "buffer size {} less than fixedstruct size {} at fileoffset {}, file {:?}",
1396 buffer.len(), sz, fileoffset, self.path(),
1397 ),
1398 ),
1399 ));
1400 }
1401
1402 defo!("zero buffer[‥{}]", sz);
1404 let slice_: &mut [u8] = &mut buffer[..sz as usize];
1405 slice_.iter_mut().for_each(|m| *m = 0);
1406
1407 let _readn = match self
1409 .blockreader
1410 .read_data_to_buffer(fileoffset, fileoffset + sz, false, slice_)
1411 {
1412 ResultReadDataToBuffer::Found(val) => val,
1413 ResultReadDataToBuffer::Done => {
1414 defx!("return ResultFindFixedStruct::Done; read_data_to_buffer returned Done");
1415 return ResultFindFixedStruct::Done;
1416 }
1417 ResultReadDataToBuffer::Err(err) => {
1418 self.set_error(&err);
1419 defx!("return ResultFindFixedStruct::Err({:?})", err);
1420 return ResultFindFixedStruct::Err((None, err));
1423 }
1424 };
1425 debug_assert_eq!(_readn, sz as usize, "read {} bytes, expected {} bytes", _readn, sz);
1426
1427 let fs: FixedStruct = match FixedStruct::new(
1429 fileoffset,
1430 &self.tz_offset,
1431 slice_,
1432 self.fixedstruct_type(),
1433 ) {
1434 Ok(val) => val,
1435 Err(err) => {
1436 defx!("return ResultFindFixedStruct::Done; FixedStruct::new returned Err({:?})", err);
1437 return ResultFindFixedStruct::Err((Some(fo_next), err));
1438 }
1439 };
1440 self.entries_processed += 1;
1442 defo!("entries_processed = {}", self.entries_processed);
1443 summary_stat!(self.dt_first_last_update(fs.dt()));
1444 self.drop_entry(&fs);
1446
1447 defx!("return ResultFindFixedStruct::Found((fo_next={}, …))", fo_next);
1448
1449 ResultFindFixedStruct::Found((fo_next, fs))
1450 }
1451
1452 pub fn entry_dt_after_or_before(
1458 entry: &FixedStruct,
1459 dt_filter: &DateTimeLOpt,
1460 ) -> Result_Filter_DateTime1 {
1461 defñ!("({:?})", dt_filter);
1462
1463 dt_after_or_before(entry.dt(), dt_filter)
1464 }
1465
1466 #[inline(always)]
1472 pub fn entry_pass_filters(
1473 entry: &FixedStruct,
1474 dt_filter_after: &DateTimeLOpt,
1475 dt_filter_before: &DateTimeLOpt,
1476 ) -> Result_Filter_DateTime2 {
1477 defn!("({:?}, {:?})", dt_filter_after, dt_filter_before);
1478
1479 let result: Result_Filter_DateTime2 = dt_pass_filters(
1480 entry.dt(),
1481 dt_filter_after,
1482 dt_filter_before
1483 );
1484 defx!("(…) return {:?};", result);
1485
1486 result
1487 }
1488
1489 #[allow(non_snake_case)]
1494 pub fn summary(&self) -> SummaryFixedStructReader {
1495 let fixedstructreader_fixedstructtype_opt = Some(self.fixedstruct_type());
1496 let fixedstructreader_filetypefixedstruct_opt = Some(self.filetype_fixedstruct);
1497 let fixedstructreader_high_score: Score = self.high_score;
1498 let fixedstructreader_utmp_entries: Count = self.entries_processed;
1499 let fixedstructreader_first_entry_fileoffset: FileOffset = self.first_entry_fileoffset;
1500 let fixedstructreader_entries_out_of_order: usize = self.entries_out_of_order;
1501 let fixedstructreader_utmp_entries_max: Count = self.entries_stored_highest as Count;
1502 let fixedstructreader_utmp_entries_hit: Count = self.entries_hits as Count;
1503 let fixedstructreader_utmp_entries_miss: Count = self.entries_miss as Count;
1504 let fixedstructreader_drop_entry_ok: Count = self.drop_entry_ok;
1505 let fixedstructreader_drop_entry_errors: Count = self.drop_entry_errors;
1506 let fixedstructreader_datetime_first = self.dt_first;
1507 let fixedstructreader_datetime_last = self.dt_last;
1508 let fixedstructreader_map_tvpair_fo_max_len: usize = self.map_tvpair_fo_max_len;
1509
1510 SummaryFixedStructReader {
1511 fixedstructreader_fixedstructtype_opt,
1512 fixedstructreader_filetypefixedstruct_opt,
1513 fixedstructreader_fixedstruct_size: self.fixedstruct_size(),
1514 fixedstructreader_high_score,
1515 fixedstructreader_utmp_entries,
1516 fixedstructreader_first_entry_fileoffset,
1517 fixedstructreader_entries_out_of_order,
1518 fixedstructreader_utmp_entries_max,
1519 fixedstructreader_utmp_entries_hit,
1520 fixedstructreader_utmp_entries_miss,
1521 fixedstructreader_drop_entry_ok,
1522 fixedstructreader_drop_entry_errors,
1523 fixedstructreader_datetime_first,
1524 fixedstructreader_datetime_last,
1525 fixedstructreader_map_tvpair_fo_max_len,
1526 }
1527 }
1528
1529 pub fn summary_complete(&self) -> Summary {
1533 let path = self.path().clone();
1534 let path_ntf = None;
1535 let filetype = self.filetype();
1536 let logmessagetype = filetype.to_logmessagetype();
1537 let summaryblockreader = self.blockreader.summary();
1538 let summaryfixedstructreader = self.summary();
1539 let error: Option<String> = self.error.clone();
1540
1541 Summary::new(
1542 path,
1543 path_ntf,
1544 filetype,
1545 logmessagetype,
1546 Some(summaryblockreader),
1547 None,
1548 None,
1549 None,
1550 Some(summaryfixedstructreader),
1551 None,
1552 None,
1553 None,
1554 error,
1555 )
1556 }
1557}