1use crate::{e_err, de_err, de_wrn};
28use crate::common::{
29 debug_panic,
30 Count,
31 FileOffset,
32 FileSz,
33 FileType,
34 FileTypeFixedStruct,
35 FPath,
36 ResultS3,
37};
38use crate::data::datetime::{
39 DateTimeL,
40 DateTimeLOpt,
41 FixedOffset,
42 SystemTime,
43 Result_Filter_DateTime1,
44 Result_Filter_DateTime2,
45 dt_after_or_before,
46 dt_pass_filters,
47};
48use crate::data::fixedstruct::{
49 buffer_to_fixedstructptr,
50 convert_datetime_tvpair,
51 filesz_to_types,
52 FixedStruct,
53 FixedStructDynPtr,
54 FixedStructType,
55 FixedStructTypeSet,
56 ENTRY_SZ_MAX,
57 ENTRY_SZ_MIN,
58 Score,
59 TIMEVAL_SZ_MAX,
60 tv_pair_type,
61};
62use crate::readers::blockreader::{
63 BlockIndex,
64 BlockOffset,
65 BlockReader,
66 BlockSz,
67 ResultReadDataToBuffer,
68};
69use crate::readers::summary::Summary;
70
71use std::collections::{BTreeMap, LinkedList};
72use std::fmt;
73use std::io::{Error, ErrorKind, Result};
74
75use ::more_asserts::{debug_assert_ge, debug_assert_le};
76#[allow(unused_imports)]
77use ::si_trace_print::{
78 de,
79 defn,
80 defo,
81 defx,
82 defñ,
83 def1ñ,
84 def1n,
85 def1o,
86 def1x,
87 den,
88 deo,
89 dex,
90 deñ,
91 pfo,
92 pfn,
93 pfx,
94};
95
96
97pub type FoToEntry = BTreeMap<FileOffset, FixedStruct>;
108
109pub type FoToFo = BTreeMap<FileOffset, FileOffset>;
113
114pub type FoList = LinkedList<FileOffset>;
115
116type MapTvPairToFo = BTreeMap<tv_pair_type, FileOffset>;
117
118pub type ResultS3FixedStructFind = ResultS3<(FileOffset, FixedStruct), (Option<FileOffset>, Error)>;
122
123pub type ResultS3FixedStructProcZeroBlock = ResultS3<(FixedStructType, Score, ListFileOffsetFixedStructPtr), Error>;
124
125pub type ResultTvFo = Result<(usize, usize, usize, usize, MapTvPairToFo)>;
126
127#[cfg(test)]
128pub type DroppedBlocks = LinkedList<BlockOffset>;
129
130type ListFileOffsetFixedStructPtr = LinkedList<(FileOffset, FixedStructDynPtr)>;
131
132#[derive(Debug)]
134pub enum ResultFixedStructReaderNew<E> {
135 FileOk(FixedStructReader),
138 FileErrEmpty,
139 FileErrTooSmall(String),
140 FileErrNoValidFixedStruct,
142 FileErrNoFixedStructWithinDtFilters,
144 FileErrIo(E),
147}
148
149pub type ResultFixedStructReaderNewError = ResultFixedStructReaderNew<Error>;
150
151#[derive(Debug)]
152pub enum ResultFixedStructReaderScoreFile<E> {
153 FileOk(FixedStructType, Score, ListFileOffsetFixedStructPtr),
156 FileErrEmpty,
157 FileErrNoValidFixedStruct,
159 FileErrNoHighScore,
161 FileErrIo(E),
164}
165
166pub type ResultFixedStructReaderScoreFileError = ResultFixedStructReaderScoreFile<Error>;
167
168pub struct FixedStructReader
201{
202 pub(crate) blockreader: BlockReader,
203 fixedstruct_type: FixedStructType,
204 filetype_fixedstruct: FileTypeFixedStruct,
205 fixedstruct_size: usize,
207 high_score: Score,
210 tz_offset: FixedOffset,
215 pub(crate) cache_entries: FoToEntry,
221 pub(crate) map_tvpair_fo: MapTvPairToFo,
227 pub(crate) block_use_count: BTreeMap<BlockOffset, usize>,
228 pub(crate) first_entry_fileoffset: FileOffset,
230 pub(crate) entries_stored_highest: usize,
232 pub(crate) entries_out_of_order: usize,
233 pub(super) entries_hits: Count,
235 pub(super) entries_miss: Count,
237 pub(super) entries_processed: Count,
241 pub(super) dt_first: DateTimeLOpt,
248 pub(super) dt_last: DateTimeLOpt,
255 pub(super) drop_entry_ok: Count,
257 pub(super) drop_entry_errors: Count,
259 pub(super) blockoffset_drop_last: BlockOffset,
261 #[cfg(test)]
263 pub(crate) dropped_blocks: DroppedBlocks,
264 pub(super) map_tvpair_fo_max_len: usize,
265 error: Option<String>,
274}
275
276impl fmt::Debug for FixedStructReader
277{
278 fn fmt(
279 &self,
280 f: &mut fmt::Formatter,
281 ) -> fmt::Result {
282 f.debug_struct("FixedStructReader")
283 .field("Path", &self.path())
284 .field("Entries", &self.cache_entries.len())
285 .field("tz_offset", &self.tz_offset)
286 .field("dt_first", &self.dt_first)
287 .field("dt_last", &self.dt_last)
288 .field("Error?", &self.error)
289 .finish()
290 }
291}
292
293#[allow(non_snake_case)]
298#[derive(Clone, Default, Eq, PartialEq, Debug)]
299pub struct SummaryFixedStructReader {
300 pub fixedstructreader_fixedstructtype_opt: Option<FixedStructType>,
301 pub fixedstructreader_filetypefixedstruct_opt: Option<FileTypeFixedStruct>,
302 pub fixedstructreader_fixedstruct_size: usize,
303 pub fixedstructreader_high_score: Score,
304 pub fixedstructreader_utmp_entries: Count,
305 pub fixedstructreader_first_entry_fileoffset: FileOffset,
306 pub fixedstructreader_entries_out_of_order: usize,
307 pub fixedstructreader_utmp_entries_max: Count,
308 pub fixedstructreader_utmp_entries_hit: Count,
309 pub fixedstructreader_utmp_entries_miss: Count,
310 pub fixedstructreader_drop_entry_ok: Count,
311 pub fixedstructreader_drop_entry_errors: Count,
312 pub fixedstructreader_datetime_first: DateTimeLOpt,
314 pub fixedstructreader_datetime_last: DateTimeLOpt,
316 pub fixedstructreader_map_tvpair_fo_max_len: usize,
317}
318
319impl FixedStructReader
321{
322 pub fn new(
333 path: FPath,
334 filetype: FileType,
335 blocksz: BlockSz,
336 tz_offset: FixedOffset,
337 dt_filter_after: DateTimeLOpt,
338 dt_filter_before: DateTimeLOpt,
339 ) -> ResultFixedStructReaderNewError {
340 def1n!(
341 "({:?}, filetype={:?}, blocksz={:?}, {:?}, {:?}, {:?})",
342 path, filetype, blocksz, tz_offset, dt_filter_after, dt_filter_before,
343 );
344 let mut blockreader = match BlockReader::new(
345 path.clone(), filetype, blocksz
346 ) {
347 Ok(blockreader_) => blockreader_,
348 Err(err) => {
349 def1x!("return Err {}", err);
350 return ResultFixedStructReaderNew::FileErrIo(err);
352 }
353 };
354 let filetype_fixedstruct = match filetype {
355 FileType::FixedStruct { archival_type: _, fixedstruct_type: type_ } => type_,
356 _ => {
357 debug_panic!("Unexpected FileType: {:?}", filetype);
358 return ResultFixedStructReaderNew::FileErrIo(
359 Error::new(
360 ErrorKind::InvalidData,
361 format!("Unexpected FileType {:?}", filetype),
362 )
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!(
466 first_entry_fileoffset, blockreader.filesz(),
467 "failed to update first_entry_fileoffset"
468 );
469
470 let mut block_use_count: BTreeMap<BlockOffset, usize> = BTreeMap::new();
473 def1o!("block_use_count create");
474 for (_tv_pair, fo) in map_tvpair_fo.iter() {
475 let bo_beg: BlockOffset = BlockReader::block_offset_at_file_offset(*fo, blocksz);
476 let fo_end: FileOffset = *fo + fixedstruct_type.size() as FileOffset;
477 let bo_end: BlockOffset = BlockReader::block_offset_at_file_offset(fo_end, blocksz);
478 def1o!("blocksz = {}", blocksz);
479 for bo in bo_beg..bo_end+1 {
480 match block_use_count.get_mut(&bo) {
481 Some(count) => {
482 let count_ = *count + 1;
483 def1o!(
484 "block_use_count[{}] += 1 ({}); [{}‥{}]; total span [{}‥{})",
485 bo, count_, *fo, 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, *fo, fo_end,
495 BlockReader::file_offset_at_block_offset(bo_beg, blocksz),
496 BlockReader::file_offset_at_block_offset(bo_end + 1, blocksz),
497 );
498 block_use_count.insert(bo, 1);
499 }
500 }
501 }
502 }
503 #[cfg(debug_assertions)]
504 {
505 for (bo, count) in block_use_count.iter() {
506 def1o!(
507 "block_use_count[{}] = {}; total span [{}‥{}]",
508 bo, count,
509 BlockReader::file_offset_at_block_offset(*bo, blocksz),
510 BlockReader::file_offset_at_block_offset(*bo + 1, blocksz),
511 );
512 }
513 }
514
515 let map_max_len = map_tvpair_fo.len();
516 let mut fixedstructreader = FixedStructReader
518 {
519 blockreader,
520 fixedstruct_type,
521 filetype_fixedstruct,
522 fixedstruct_size: fixedstruct_type.size(),
523 high_score,
524 tz_offset,
525 cache_entries: FoToEntry::new(),
526 map_tvpair_fo,
527 block_use_count,
528 first_entry_fileoffset,
529 entries_stored_highest: 0,
530 entries_out_of_order: out_of_order,
531 entries_hits: 0,
532 entries_miss: 0,
533 entries_processed: 0,
534 dt_first: DateTimeLOpt::None,
535 dt_last: DateTimeLOpt::None,
536 drop_entry_ok: 0,
537 drop_entry_errors: 0,
538 blockoffset_drop_last: 0,
539 #[cfg(test)]
540 dropped_blocks: DroppedBlocks::new(),
541 map_tvpair_fo_max_len: map_max_len,
542 error: None,
543 };
544
545 for (fo, fixedstructptr) in list_entries.into_iter() {
548 match FixedStruct::from_fixedstructptr(
554 fo, &tz_offset, fixedstructptr
555 ) {
556 Ok(fixedstruct) => {
557 if fixedstructreader.map_tvpair_fo.iter().find(|(_tv_pair, fo2)| &fo == *fo2).is_some() {
558 def1o!("insert entry at fo {}", fo);
559 fixedstructreader.insert_cache_entry(fixedstruct);
560 } else {
561 def1o!("skip entry at fo {}; not in map_tvpair_fo", fo);
562 }
563 }
564 Err(err) => {
565 de_err!("FixedStruct::from_fixedstructptr Error {}; file {:?}",
566 err, fixedstructreader.path());
567 fixedstructreader.set_error(&err);
568 }
569 }
570 }
571
572 def1x!("return FileOk(FixedStructReader)");
573
574 ResultFixedStructReaderNew::FileOk(fixedstructreader)
575 }
576
577 #[inline(always)]
581 pub const fn blocksz(&self) -> BlockSz {
582 self.blockreader.blocksz()
583 }
584
585 #[inline(always)]
589 pub const fn filesz(&self) -> FileSz {
590 self.blockreader.filesz()
591 }
592
593 #[inline(always)]
597 pub const fn filetype(&self) -> FileType {
598 self.blockreader.filetype()
599 }
600
601 #[inline(always)]
605 pub const fn path(&self) -> &FPath {
606 self.blockreader.path()
607 }
608
609 pub fn mtime(&self) -> SystemTime {
613 self.blockreader.mtime()
614 }
615
616 #[inline(always)]
619 pub fn count_entries_processed(&self) -> Count {
620 self.entries_processed
621 }
622
623 #[inline(always)]
625 pub fn entries_stored_highest(&self) -> usize {
626 self.entries_stored_highest
627 }
628
629 #[inline(always)]
633 pub const fn block_offset_at_file_offset(
634 &self,
635 fileoffset: FileOffset,
636 ) -> BlockOffset {
637 BlockReader::block_offset_at_file_offset(fileoffset, self.blocksz())
638 }
639
640 #[inline(always)]
644 pub const fn file_offset_at_block_offset(
645 &self,
646 blockoffset: BlockOffset,
647 ) -> FileOffset {
648 BlockReader::file_offset_at_block_offset(blockoffset, self.blocksz())
649 }
650
651 #[inline(always)]
655 pub const fn file_offset_at_block_offset_index(
656 &self,
657 blockoffset: BlockOffset,
658 blockindex: BlockIndex,
659 ) -> FileOffset {
660 BlockReader::file_offset_at_block_offset_index(blockoffset, self.blocksz(), blockindex)
661 }
662
663 #[inline(always)]
667 pub const fn block_index_at_file_offset(
668 &self,
669 fileoffset: FileOffset,
670 ) -> BlockIndex {
671 BlockReader::block_index_at_file_offset(fileoffset, self.blocksz())
672 }
673
674 #[inline(always)]
678 pub const fn count_blocks(&self) -> Count {
679 BlockReader::count_blocks(self.filesz(), self.blocksz()) as Count
680 }
681
682 pub const fn blockoffset_last(&self) -> BlockOffset {
686 self.blockreader
687 .blockoffset_last()
688 }
689
690 pub const fn fileoffset_last(&self) -> FileOffset {
694 self.blockreader
695 .fileoffset_last()
696 }
697
698 pub const fn is_fileoffset_last(
700 &self,
701 fileoffset: FileOffset,
702 ) -> bool {
703 self.fileoffset_last() == fileoffset
704 }
705
706 #[inline(always)]
708 pub fn is_last(
709 &self,
710 fixedstruct: &FixedStruct,
711 ) -> bool {
712 self.is_fileoffset_last(fixedstruct.fileoffset_end() - 1)
713 }
714
715 #[inline(always)]
718 pub const fn fileoffset_to_fixedstructoffset (
719 &self,
720 fileoffset: FileOffset,
721 ) -> FileOffset {
722 (fileoffset / self.fixedstruct_size_fo()) * self.fixedstruct_size_fo()
723 }
724
725 pub fn fileoffset_first(&self) -> Option<FileOffset> {
729 match self.map_tvpair_fo.iter().min_by_key(|(tv_pair, fo)| (*tv_pair, *fo)) {
730 Some((_tv_pair, fo_)) => Some(*fo_),
731 None => None,
732 }
733 }
734
735 #[inline(always)]
738 pub const fn fixedstruct_size(&self) -> usize {
739 self.fixedstruct_size
740 }
741
742 #[inline(always)]
746 pub const fn fixedstruct_size_fo(&self) -> FileOffset {
747 self.fixedstruct_size() as FileOffset
748 }
749
750 #[inline(always)]
752 pub const fn fixedstruct_type(&self) -> FixedStructType {
753 self.fixedstruct_type
754 }
755
756 #[cfg(test)]
760 pub fn get_fileoffsets(&self) -> Vec<FileOffset> {
761 self.cache_entries
762 .keys()
763 .cloned()
764 .collect()
765 }
766
767 fn set_error(
770 &mut self,
771 error: &Error,
772 ) {
773 def1ñ!("{:?}", error);
774 let mut error_string: String = error.kind().to_string();
775 error_string.push_str(": ");
776 error_string.push_str(error.kind().to_string().as_str());
777 match &self.error {
783 Some(err_s) => {
784 if err_s != &error_string {
785 e_err!("{}", error);
786 }
787 }
788 None => {
789 e_err!("{}", error);
790 }
791 }
792 if let Some(ref _err) = self.error {
793 de_wrn!("skip overwrite of previous Error ({:?}) with Error ({:?})", _err, error);
794 return;
795 }
796 self.error = Some(error_string);
797 }
798
799 fn insert_cache_entry(
805 &mut self,
806 entry: FixedStruct,
807 ) {
808 defn!("@{}", entry.fileoffset_begin());
809 let fo_beg: FileOffset = entry.fileoffset_begin();
810 debug_assert!(
811 !self.cache_entries.contains_key(&fo_beg),
812 "self.cache_entries already contains key {}",
813 fo_beg
814 );
815
816 self.cache_entries
818 .insert(fo_beg, entry);
819 self.entries_stored_highest = std::cmp::max(
820 self.entries_stored_highest, self.cache_entries.len()
821 );
822 self.entries_processed += 1;
823 defo!("entries_processed = {}", self.entries_processed);
824
825 defx!();
826 }
827
828 fn dt_first_last_update(
831 &mut self,
832 datetime: &DateTimeL,
833 ) {
834 defñ!("({:?})", datetime);
835 match self.dt_first {
839 Some(dt_first_) => {
840 if &dt_first_ > datetime {
841 self.dt_first = Some(*datetime);
842 }
843 }
844 None => {
845 self.dt_first = Some(*datetime);
846 }
847 }
848 match self.dt_last {
849 Some(dt_last_) => {
850 if &dt_last_ < datetime {
851 self.dt_last = Some(*datetime);
852 }
853 }
854 None => {
855 self.dt_last = Some(*datetime);
856 }
857 }
858 }
859
860 fn drop_entry(
869 &mut self,
870 fixedstruct: &FixedStruct,
871 ) -> usize {
872 let bsz: BlockSz = self.blocksz();
873 defn!(
874 "(fixedstruct@{}); offsets [{}‥{}), blocks [{}‥{}]",
875 fixedstruct.fileoffset_begin(),
876 fixedstruct.fileoffset_begin(),
877 fixedstruct.fileoffset_end(),
878 fixedstruct.blockoffset_begin(bsz),
879 fixedstruct.blockoffset_end(bsz),
880 );
881 let mut dropped_ok: usize = 0;
882 let mut dropped_err: usize = 0;
883 let mut bo_at: BlockOffset = fixedstruct.blockoffset_begin(bsz);
884 let bo_end: BlockOffset = fixedstruct.blockoffset_end(bsz);
885 debug_assert_le!(bo_at, bo_end);
886 while bo_at <= bo_end {
887 defo!("block_use_count.get_mut({})", bo_at);
888 match self.block_use_count.get_mut(&bo_at) {
889 Some(count) => {
890 if *count <= 1 {
891 defo!(
892 "block_use_count[{}] found; count=={}; total span [{}‥{})",
893 bo_at, count,
894 BlockReader::file_offset_at_block_offset(bo_at, bsz),
895 BlockReader::file_offset_at_block_offset(bo_end + 1, bsz),
896 );
897 if self
898 .blockreader
899 .drop_block(bo_at)
900 {
901 defo!(
902 "dropped block {}; total span [{}‥{})",
903 bo_at,
904 BlockReader::file_offset_at_block_offset(bo_at, bsz),
905 BlockReader::file_offset_at_block_offset(bo_end + 1, bsz),
906 );
907 self.blockoffset_drop_last = std::cmp::max(bo_at, self.blockoffset_drop_last);
910 self.block_use_count.remove(&bo_at);
911 #[cfg(test)]
912 self.dropped_blocks.push_back(bo_at);
913 dropped_ok += 1;
914 } else {
915 defo!("failed to drop block {}", bo_at);
916 dropped_err += 1;
917 }
918 } else {
919 *count -= 1;
920 defo!("block_use_count[{}] found; count-=1=={}", bo_at, *count);
921 }
922 }
923 None => {
924 defo!("block_use_count[{}] not found", bo_at);
925 }
926 }
927 bo_at += 1;
928 }
929 if dropped_ok > 0 {
930 self.drop_entry_ok += 1;
931 }
932 if dropped_err > 0 {
933 self.drop_entry_errors += 1;
934 }
935 defx!("return {}", dropped_ok);
936
937 dropped_ok
938 }
939
940 #[inline(always)]
943 fn remove_cache_entry(
944 &mut self,
945 fileoffset: FileOffset,
946 ) -> Option<FixedStruct> {
947 match self.cache_entries.remove(&fileoffset) {
948 Some(fixedstruct) => {
949 defñ!("({}): found in store", fileoffset);
950 self.entries_hits += 1;
951
952 Some(fixedstruct)
953 }
954 None => {
955 defñ!("({}): not found in store", fileoffset);
956 self.entries_miss += 1;
957
958 None
959 }
960 }
961 }
962
963 pub fn score_file(
975 blockreader: &mut BlockReader,
976 oneblock: bool,
977 types_to_bonus: FixedStructTypeSet,
978 ) -> ResultFixedStructReaderScoreFileError {
979 def1n!("(oneblock={}, types_to_bonus len {})", oneblock, types_to_bonus.len());
980 #[cfg(debug_assertions)]
981 {
982 for (fixedstructtype, bonus) in types_to_bonus.iter() {
983 def1o!(
984 "types_to_bonus: ({:<30?}, {:2}) size {}",
985 fixedstructtype, bonus, fixedstructtype.size(),
986 );
987 }
988 }
989 let mut buffer: [u8; ENTRY_SZ_MAX] = [0; ENTRY_SZ_MAX];
991 #[cfg(not(test))]
994 const COUNT_FOUND_ENTRIES_MAX: usize = 5;
995 #[cfg(test)]
996 const COUNT_FOUND_ENTRIES_MAX: usize = 2;
997 let mut _count_total: usize = 0;
998 let mut highest_score: Score = 0;
999 let mut highest_score_type: Option<FixedStructType> = None;
1000 let mut highest_score_entries = ListFileOffsetFixedStructPtr::new();
1001
1002 for (fixedstructtype, bonus) in types_to_bonus.into_iter() {
1003 let mut _count_loop: usize = 0;
1004 let mut count_found_entries: usize = 0;
1005 let mut high_score: Score = 0;
1006 let mut fo: FileOffset = 0;
1007 let mut found_entries = ListFileOffsetFixedStructPtr::new();
1008
1009 loop {
1010 if count_found_entries >= COUNT_FOUND_ENTRIES_MAX {
1011 def1o!("count_found_entries {} >= COUNT_FOUND_ENTRIES_MAX {}", count_found_entries, COUNT_FOUND_ENTRIES_MAX);
1012 break;
1013 }
1014 _count_total += 1;
1015 _count_loop += 1;
1016 let utmp_sz: usize = fixedstructtype.size();
1017 let fo_end = fo + utmp_sz as FileOffset;
1018 def1o!(
1019 "loop try {} (total {}), fixedstructtype {:?}, zero the buffer (size {}), looking at fileoffset {}‥{} (0x{:08X}‥0x{:08X})",
1020 _count_loop, _count_total, fixedstructtype, buffer.len(), fo, fo_end, fo, fo_end
1021 );
1022 buffer.iter_mut().for_each(|m| *m = 0);
1029 let buffer_read: usize = match blockreader.read_data_to_buffer(
1031 fo,
1032 fo_end,
1033 oneblock,
1034 &mut buffer,
1035 ) {
1036 ResultReadDataToBuffer::Found(buffer_read) => buffer_read,
1037 ResultReadDataToBuffer::Err(err) => {
1038 def1x!("return Err");
1039 return ResultFixedStructReaderScoreFileError::FileErrIo(err);
1040 }
1041 ResultReadDataToBuffer::Done => {
1042 break;
1044 }
1045 };
1046 if buffer_read < utmp_sz {
1047 def1o!(
1048 "read_data_to_buffer read bytes {} < {} requested fixedstruct size bytes; break",
1049 buffer_read, utmp_sz,
1050 );
1051 break;
1052 }
1053 let fo2 = fo;
1055 fo += utmp_sz as FileOffset;
1056 let slice_ = &buffer[..buffer_read];
1058 let fixedstructptr: FixedStructDynPtr = match buffer_to_fixedstructptr(slice_, fixedstructtype) {
1060 Some(val) => val,
1061 None => {
1062 def1o!(
1063 "buffer_to_fixedstructptr(buf len {}, {:?}) returned None; continue",
1064 buffer.len(), fixedstructtype,
1065 );
1066 continue;
1067 }
1068 };
1069 count_found_entries += 1;
1070 let score: Score = FixedStruct::score_fixedstruct(&fixedstructptr, bonus);
1072 def1o!("score {} for entry type {:?} @[{}‥{}]",
1073 score, fixedstructptr.fixedstruct_type(), fo2, fo_end);
1074 let _fs_type: FixedStructType = fixedstructptr.fixedstruct_type();
1076 found_entries.push_back((fo2, fixedstructptr));
1077 if score <= high_score {
1078 def1o!(
1079 "score {} ({:?}) not higher than high score {}",
1080 score,
1081 _fs_type,
1082 high_score,
1083 );
1084 continue;
1086 }
1087 def1o!("new high score {} for entry type {:?} @[{}‥{}]",
1089 score, _fs_type, fo2, fo_end);
1090 high_score = score;
1091 }
1092 if high_score > highest_score {
1095 match highest_score_type {
1098 None => {
1099 def1o!(
1100 "new highest score {} entry type {:?} with {} entries",
1101 high_score, fixedstructtype, found_entries.len(),
1102 );
1103 }
1104 Some(_highest_score_type) => {
1105 def1o!(
1106 "new highest score {} entry type {:?} with {} entries; replaces old high score {} entry type {:?} with {} entries (entries dropped)",
1107 high_score, fixedstructtype, found_entries.len(),
1108 highest_score, _highest_score_type, highest_score_entries.len(),
1109 );
1110 }
1111 }
1112 highest_score = high_score;
1113 highest_score_type = Some(fixedstructtype);
1114 highest_score_entries = found_entries;
1115 } else {
1116 def1o!(
1117 "no new highest score: score {} entry type {:?} with {} entries. old high score remains: score {} entry type {:?} with {} entries",
1118 high_score, fixedstructtype, found_entries.len(),
1119 highest_score, highest_score_type, highest_score_entries.len(),
1120 );
1121 }
1122 }
1123
1124 match highest_score_type {
1125 None => {
1126 def1x!("return Err {:?}", ResultFixedStructReaderScoreFileError::FileErrNoHighScore);
1127 return ResultFixedStructReaderScoreFileError::FileErrNoHighScore;
1128 }
1129 Some(highest_score_type) => {
1130 def1x!("return Ok(({:?}, {}, found_entries))", highest_score_type, highest_score);
1131
1132 ResultFixedStructReaderScoreFileError::FileOk(highest_score_type, highest_score, highest_score_entries)
1133 }
1134 }
1135 }
1136
1137 pub(crate) fn preprocess_fixedstructtype(
1152 blockreader: &mut BlockReader,
1153 filetype_fixedstruct: &FileTypeFixedStruct,
1154 oneblock: bool,
1155 ) -> ResultFixedStructReaderScoreFileError
1156 {
1157 def1n!("({:?}, oneblock={})", filetype_fixedstruct, oneblock);
1158
1159 if blockreader.filesz() == 0 {
1161 def1x!("empty file; return FileErrEmpty");
1162 return ResultFixedStructReaderScoreFileError::FileErrEmpty;
1163 }
1164
1165 let types_to_bonus: FixedStructTypeSet = match filesz_to_types(
1166 blockreader.filesz(),
1167 filetype_fixedstruct,
1168 ) {
1169 Some(set) => set,
1170 None => {
1171 de_wrn!("FixedStructReader::filesz_to_types({}) failed; file {:?}",
1172 blockreader.filesz(), blockreader.path());
1173 def1x!("filesz_to_types returned None; return FileErrNoValidFixedStruct");
1174 return ResultFixedStructReaderScoreFileError::FileErrNoValidFixedStruct;
1175 }
1176 };
1177 def1o!("filesz_to_types returned {} types: {:?}", types_to_bonus.len(), types_to_bonus);
1178
1179 match FixedStructReader::score_file(blockreader, oneblock, types_to_bonus) {
1180 ret => {
1181 def1x!("score_file returned {:?}", ret);
1182
1183 ret
1184 }
1185 }
1186 }
1187
1188 pub(crate) fn preprocess_timevalues(
1195 blockreader: &mut BlockReader,
1196 fixedstruct_type: FixedStructType,
1197 dt_filter_after: &DateTimeLOpt,
1198 dt_filter_before: &DateTimeLOpt,
1199 ) -> ResultTvFo
1200 {
1201 defn!();
1202 let mut buffer: [u8; TIMEVAL_SZ_MAX] = [0; TIMEVAL_SZ_MAX];
1204 let mut map_tv_pair_fo: MapTvPairToFo = MapTvPairToFo::new();
1206 let mut out_of_order: usize = 0;
1208 let mut valid_no_pass_filter: usize = 0;
1210 let mut invalid: usize = 0;
1212 let mut total_entries: usize = 0;
1214
1215 let tv_filter_after: Option<tv_pair_type> = match dt_filter_after {
1216 Some(dt) => Some(convert_datetime_tvpair(dt)),
1217 None => None,
1218 };
1219 defo!("tv_filter_after: {:?}", tv_filter_after);
1220 let tv_filter_before: Option<tv_pair_type> = match dt_filter_before {
1221 Some(dt) => Some(convert_datetime_tvpair(dt)),
1222 None => None,
1223 };
1224 defo!("tv_filter_before: {:?}", tv_filter_before);
1225
1226 let entry_sz: FileOffset = fixedstruct_type.size() as FileOffset;
1228 debug_assert_eq!(blockreader.filesz() % entry_sz, 0, "file not a multiple of entry size {}", entry_sz);
1229 let tv_sz: usize = fixedstruct_type.size_tv();
1230 let tv_offset: usize = fixedstruct_type.offset_tv();
1231 let slice_: &mut [u8] = &mut buffer[..tv_sz];
1232 let mut fo: FileOffset = 0;
1233 let mut tv_pair_prev: Option<tv_pair_type> = None;
1234 loop {
1235 let beg: FileOffset = fo + tv_offset as FileOffset;
1237 let end: FileOffset = beg + tv_sz as FileOffset;
1238 match blockreader.read_data_to_buffer(
1239 beg,
1240 end,
1241 false,
1242 slice_,
1243 ) {
1244 ResultReadDataToBuffer::Found(_readn) => {
1245 defo!("read {} bytes at fileoffset {}", _readn, beg);
1246 debug_assert_eq!(
1247 _readn, tv_sz as usize,
1248 "read {} bytes, expected {} bytes (size of a time value)",
1249 _readn, tv_sz,
1250 );
1251 }
1252 ResultReadDataToBuffer::Err(err) => {
1253 defx!("return Err");
1254 return ResultTvFo::Err(err);
1255 }
1256 ResultReadDataToBuffer::Done => {
1257 defo!("return Done");
1258 break;
1259 }
1260 }
1261 let tv_pair: tv_pair_type = match fixedstruct_type.tv_pair_from_buffer(
1263 slice_,
1264 ) {
1265 Some(pair) => pair,
1266 None => {
1267 de_err!("invalid entry at fileoffset {}", fo);
1268 defo!("invalid entry at fileoffset {}", fo);
1269 fo += entry_sz;
1270 invalid += 1;
1271 continue;
1272 }
1273 };
1274 defo!("tv_pair: {:?}", tv_pair);
1275 if tv_pair == tv_pair_type(0, 0) {
1276 defo!("tv_pair is (0, 0); continue");
1277 fo += entry_sz;
1278 continue;
1279 }
1280 match tv_pair_prev {
1281 Some(tv_pair_prev) => {
1282 if tv_pair < tv_pair_prev {
1283 out_of_order += 1;
1284 defo!(
1285 "out_of_order = {}; tv_pair = {:?}, tv_pair_prev = {:?}",
1286 out_of_order, tv_pair, tv_pair_prev,
1287 );
1288 }
1289 }
1290 None => {}
1291 }
1292 tv_pair_prev = Some(tv_pair);
1293 total_entries += 1;
1294 if let Some(tv_filter) = tv_filter_after {
1296 if tv_pair < tv_filter {
1297 defo!("tv_pair {:?} < {:?} tv_filter_after; continue", tv_pair, tv_filter);
1298 fo += entry_sz;
1299 valid_no_pass_filter += 1;
1300 continue;
1301 }
1302 }
1303 if let Some(tv_filter) = tv_filter_before {
1304 if tv_pair > tv_filter {
1305 defo!("tv_pair {:?} > {:?} tv_filter_before; continue", tv_pair, tv_filter);
1306 fo += entry_sz;
1307 valid_no_pass_filter += 1;
1308 continue;
1311 }
1312 }
1313 defo!("tv_pair {:?} @{} passes time value filters", tv_pair, fo);
1315 map_tv_pair_fo.insert(tv_pair, fo);
1316
1317 fo += entry_sz;
1318 }
1319 defx!("map_tv_pair_fo len {}", map_tv_pair_fo.len());
1321
1322 ResultTvFo::Ok(
1323 (total_entries, invalid, valid_no_pass_filter, out_of_order, map_tv_pair_fo)
1324 )
1325 }
1326
1327 pub fn process_entry_at(&mut self, fo: FileOffset, buffer: &mut [u8])
1339 -> ResultS3FixedStructFind
1340 {
1341 defn!("({})", fo);
1342
1343 let sz: FileOffset = self.fixedstruct_size_fo();
1344 debug_assert_eq!(
1345 fo % sz, 0,
1346 "fileoffset {} not multiple of {}", fo, sz,
1347 );
1348 let fileoffset: FileOffset = fo - (fo % sz);
1349
1350 if fileoffset >= self.filesz() {
1351 defx!(
1352 "return ResultS3FixedStructFind::Done; fileoffset {} >= filesz {}",
1353 fileoffset, self.filesz()
1354 );
1355 return ResultS3FixedStructFind::Done;
1356 }
1357
1358 let fo_next: FileOffset = {
1366 let mut fo_next_: FileOffset = self.filesz();
1367 let mut next_pair: bool = false;
1368 let mut tv_pair_at_opt: Option<tv_pair_type> = None;
1369 for (tv_pair_at, fo_at) in self.map_tvpair_fo.iter() {
1372 if next_pair {
1373 defo!("set fo_next = {}", fo_at);
1374 fo_next_ = *fo_at;
1375 break;
1376 }
1377 if &fileoffset == fo_at {
1378 defo!(
1379 "found fileoffset {} with key {:?} in map_tvpair_fo",
1380 fileoffset, tv_pair_at,
1381 );
1382 tv_pair_at_opt = Some(*tv_pair_at);
1383 next_pair = true;
1384 }
1385 }
1386 match tv_pair_at_opt {
1388 Some(tv_pair_at) => {
1389 self.map_tvpair_fo.remove(&tv_pair_at);
1390 defo!(
1391 "remove tv_pair {:?}; map_tvpair_fo size {}",
1392 tv_pair_at, self.map_tvpair_fo.len()
1393 );
1394 }
1395 None => {
1396 defo!("no map_tvpair_fo found!");
1397 }
1398 }
1399
1400 fo_next_
1401 };
1402 defo!("fo_next = {}", fo_next);
1403
1404 if let Some(fixedstruct) = self.remove_cache_entry(fileoffset) {
1406 self.dt_first_last_update(fixedstruct.dt());
1407 self.drop_entry(&fixedstruct);
1409 defx!(
1410 "remove_cache_entry found fixedstruct at fileoffset {}; return Found({}, …)",
1411 fileoffset, fo_next,
1412 );
1413 return ResultS3FixedStructFind::Found((fo_next, fixedstruct));
1414 }
1415
1416 if buffer.len() < sz as usize {
1421 defx!("return ResultS3FixedStructFind::Err");
1422 return ResultS3FixedStructFind::Err((
1423 None,
1424 Error::new(
1425 ErrorKind::InvalidData,
1426 format!(
1427 "buffer size {} less than fixedstruct size {} at fileoffset {}, file {:?}",
1428 buffer.len(), sz, fileoffset, self.path(),
1429 ),
1430 )
1431 ));
1432 }
1433
1434 defo!("zero buffer[‥{}]", sz);
1436 let slice_: &mut [u8] = &mut buffer[..sz as usize];
1437 slice_.iter_mut().for_each(|m| *m = 0);
1438
1439 let _readn = match self.blockreader.read_data_to_buffer(
1441 fileoffset,
1442 fileoffset + sz,
1443 false,
1444 slice_,
1445 ) {
1446 ResultReadDataToBuffer::Found(val) => val,
1447 ResultReadDataToBuffer::Done => {
1448 defx!("return ResultS3FixedStructFind::Done; read_data_to_buffer returned Done");
1449 return ResultS3FixedStructFind::Done;
1450 }
1451 ResultReadDataToBuffer::Err(err) => {
1452 self.set_error(&err);
1453 defx!("return ResultS3FixedStructFind::Err({:?})", err);
1454 return ResultS3FixedStructFind::Err((None, err));
1457 }
1458 };
1459 debug_assert_eq!(_readn, sz as usize, "read {} bytes, expected {} bytes", _readn, sz);
1460
1461 let fs: FixedStruct = match FixedStruct::new(
1463 fileoffset,
1464 &self.tz_offset,
1465 &slice_,
1466 self.fixedstruct_type(),
1467 ) {
1468 Ok(val) => val,
1469 Err(err) => {
1470 defx!("return ResultS3FixedStructFind::Done; FixedStruct::new returned Err({:?})", err);
1471 return ResultS3FixedStructFind::Err((Some(fo_next), err));
1472 }
1473 };
1474 self.entries_processed += 1;
1476 defo!("entries_processed = {}", self.entries_processed);
1477 self.dt_first_last_update(fs.dt());
1478 self.drop_entry(&fs);
1480
1481 defx!("return ResultS3FixedStructFind::Found((fo_next={}, …))", fo_next);
1482
1483 ResultS3FixedStructFind::Found((fo_next, fs))
1484 }
1485
1486 pub fn entry_dt_after_or_before(
1492 entry: &FixedStruct,
1493 dt_filter: &DateTimeLOpt,
1494 ) -> Result_Filter_DateTime1 {
1495 defñ!("({:?})", dt_filter);
1496
1497 dt_after_or_before(entry.dt(), dt_filter)
1498 }
1499
1500 #[inline(always)]
1506 pub fn entry_pass_filters(
1507 entry: &FixedStruct,
1508 dt_filter_after: &DateTimeLOpt,
1509 dt_filter_before: &DateTimeLOpt,
1510 ) -> Result_Filter_DateTime2 {
1511 defn!("({:?}, {:?})", dt_filter_after, dt_filter_before);
1512
1513 let result: Result_Filter_DateTime2 = dt_pass_filters(
1514 entry.dt(),
1515 dt_filter_after,
1516 dt_filter_before
1517 );
1518 defx!("(…) return {:?};", result);
1519
1520 result
1521 }
1522
1523 #[allow(non_snake_case)]
1528 pub fn summary(&self) -> SummaryFixedStructReader {
1529 let fixedstructreader_fixedstructtype_opt = Some(self.fixedstruct_type());
1530 let fixedstructreader_filetypefixedstruct_opt = Some(self.filetype_fixedstruct);
1531 let fixedstructreader_high_score: Score = self.high_score;
1532 let fixedstructreader_utmp_entries: Count = self.entries_processed;
1533 let fixedstructreader_first_entry_fileoffset: FileOffset = self.first_entry_fileoffset;
1534 let fixedstructreader_entries_out_of_order: usize = self.entries_out_of_order;
1535 let fixedstructreader_utmp_entries_max: Count = self.entries_stored_highest as Count;
1536 let fixedstructreader_utmp_entries_hit: Count = self.entries_hits as Count;
1537 let fixedstructreader_utmp_entries_miss: Count = self.entries_miss as Count;
1538 let fixedstructreader_drop_entry_ok: Count = self.drop_entry_ok;
1539 let fixedstructreader_drop_entry_errors: Count = self.drop_entry_errors;
1540 let fixedstructreader_datetime_first = self.dt_first;
1541 let fixedstructreader_datetime_last = self.dt_last;
1542 let fixedstructreader_map_tvpair_fo_max_len: usize = self.map_tvpair_fo_max_len;
1543
1544 SummaryFixedStructReader {
1545 fixedstructreader_fixedstructtype_opt,
1546 fixedstructreader_filetypefixedstruct_opt,
1547 fixedstructreader_fixedstruct_size: self.fixedstruct_size(),
1548 fixedstructreader_high_score,
1549 fixedstructreader_utmp_entries,
1550 fixedstructreader_first_entry_fileoffset,
1551 fixedstructreader_entries_out_of_order,
1552 fixedstructreader_utmp_entries_max,
1553 fixedstructreader_utmp_entries_hit,
1554 fixedstructreader_utmp_entries_miss,
1555 fixedstructreader_drop_entry_ok,
1556 fixedstructreader_drop_entry_errors,
1557 fixedstructreader_datetime_first,
1558 fixedstructreader_datetime_last,
1559 fixedstructreader_map_tvpair_fo_max_len,
1560 }
1561 }
1562
1563 pub fn summary_complete(&self) -> Summary {
1567 let path = self.path().clone();
1568 let path_ntf = None;
1569 let filetype = self.filetype();
1570 let logmessagetype = filetype.to_logmessagetype();
1571 let summaryblockreader = self.blockreader.summary();
1572 let summaryutmpreader = self.summary();
1573 let error: Option<String> = self.error.clone();
1574
1575 Summary::new(
1576 path,
1577 path_ntf,
1578 filetype,
1579 logmessagetype,
1580 Some(summaryblockreader),
1581 None,
1582 None,
1583 None,
1584 Some(summaryutmpreader),
1585 None,
1586 None,
1587 error,
1588 )
1589 }
1590}