11//! Defines the `Record` trait, which contains the shared behaviour that all
22//! DMAP records must have.
33
4+ use crate :: compression:: detect_bz2;
45use crate :: error:: DmapError ;
56use crate :: types:: { parse_scalar, parse_vector, read_data, DmapField , DmapType , DmapVec , Fields } ;
67use bzip2:: read:: BzDecoder ;
78use indexmap:: IndexMap ;
89use rayon:: prelude:: * ;
9- use std:: ffi:: OsStr ;
1010use std:: fmt:: Debug ;
1111use std:: fs:: File ;
1212use std:: io:: { Cursor , Read } ;
@@ -26,12 +26,19 @@ pub trait Record<'a>:
2626 Self : Sized ,
2727 Self : Send ,
2828 {
29- let mut buffer = [ 0 ; 8 ] ; // record size should be an i32 of the data
30- let read_result = dmap_data. read ( & mut buffer[ ..] ) ?;
31- if read_result < buffer. len ( ) {
32- return Err ( DmapError :: CorruptStream ( "Unable to read size of first record" ) )
29+ let mut stream: Box < dyn Read > ;
30+ let ( is_bz2, chunk) = detect_bz2 ( & mut dmap_data) ?;
31+ if is_bz2 {
32+ stream = Box :: new ( BzDecoder :: new ( chunk) ) ;
33+ } else {
34+ stream = Box :: new ( chunk) ;
3335 }
3436
37+ let mut buffer = [ 0 ; 8 ] ; // record size should be an i32 of the data
38+ stream
39+ . read_exact ( & mut buffer)
40+ . map_err ( |_| DmapError :: CorruptStream ( "Unable to read size of first record" ) ) ?;
41+
3542 let rec_size = i32:: from_le_bytes ( buffer[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) as usize ; // advance 4 bytes, skipping the "code" field
3643 if rec_size <= 0 {
3744 return Err ( DmapError :: InvalidRecord ( format ! (
@@ -42,7 +49,7 @@ pub trait Record<'a>:
4249
4350 let mut rec = vec ! [ 0 ; rec_size] ;
4451 rec[ 0 ..8 ] . clone_from_slice ( & buffer[ ..] ) ;
45- dmap_data . read_exact ( & mut rec[ 8 ..] ) ?;
52+ stream . read_exact ( & mut rec[ 8 ..] ) ?;
4653 let first_rec = Self :: parse_record ( & mut Cursor :: new ( rec) ) ?;
4754
4855 Ok ( first_rec)
@@ -57,12 +64,19 @@ pub trait Record<'a>:
5764 Self : Send ,
5865 {
5966 let mut buffer: Vec < u8 > = vec ! [ ] ;
60- dmap_data. read_to_end ( & mut buffer) ?;
67+ let ( is_bz2, mut chunk) = detect_bz2 ( & mut dmap_data) ?;
68+ if is_bz2 {
69+ let mut stream = BzDecoder :: new ( chunk) ;
70+ stream. read_to_end ( & mut buffer) ?;
71+ } else {
72+ chunk. read_to_end ( & mut buffer) ?;
73+ }
6174
6275 let mut slices: Vec < _ > = vec ! [ ] ;
6376 let mut rec_start: usize = 0 ;
6477 let mut rec_size: usize ;
6578 let mut rec_end: usize ;
79+
6680 while ( ( rec_start + 2 * i32:: size ( ) ) as u64 ) < buffer. len ( ) as u64 {
6781 rec_size = i32:: from_le_bytes ( buffer[ rec_start + 4 ..rec_start + 8 ] . try_into ( ) . unwrap ( ) )
6882 as usize ; // advance 4 bytes, skipping the "code" field
@@ -123,7 +137,13 @@ pub trait Record<'a>:
123137 Self : Send ,
124138 {
125139 let mut buffer: Vec < u8 > = vec ! [ ] ;
126- dmap_data. read_to_end ( & mut buffer) ?;
140+ let ( is_bz2, mut chunk) = detect_bz2 ( & mut dmap_data) ?;
141+ if is_bz2 {
142+ let mut stream = BzDecoder :: new ( chunk) ;
143+ stream. read_to_end ( & mut buffer) ?;
144+ } else {
145+ chunk. read_to_end ( & mut buffer) ?;
146+ }
127147
128148 let mut dmap_records: Vec < Self > = vec ! [ ] ;
129149 let mut bad_byte: Option < usize > = None ;
@@ -173,13 +193,7 @@ pub trait Record<'a>:
173193 Self : Send ,
174194 {
175195 let file = File :: open ( infile) ?;
176- match infile. extension ( ) {
177- Some ( ext) if ext == OsStr :: new ( "bz2" ) => {
178- let compressor = BzDecoder :: new ( file) ;
179- Self :: read_records ( compressor)
180- }
181- _ => Self :: read_records ( file) ,
182- }
196+ Self :: read_records ( file)
183197 }
184198
185199 /// Read a DMAP file of type `Self`.
@@ -192,13 +206,7 @@ pub trait Record<'a>:
192206 Self : Send ,
193207 {
194208 let file = File :: open ( infile) ?;
195- match infile. extension ( ) {
196- Some ( ext) if ext == OsStr :: new ( "bz2" ) => {
197- let compressor = BzDecoder :: new ( file) ;
198- Self :: read_records_lax ( compressor)
199- }
200- _ => Self :: read_records_lax ( file) ,
201- }
209+ Self :: read_records_lax ( file)
202210 }
203211
204212 /// Reads the first record of a DMAP file of type `Self`.
@@ -208,13 +216,7 @@ pub trait Record<'a>:
208216 Self : Send ,
209217 {
210218 let file = File :: open ( infile) ?;
211- match infile. extension ( ) {
212- Some ( ext) if ext == OsStr :: new ( "bz2" ) => {
213- let compressor = BzDecoder :: new ( file) ;
214- Self :: read_first_record ( compressor)
215- }
216- _ => Self :: read_first_record ( file) ,
217- }
219+ Self :: read_first_record ( file)
218220 }
219221
220222 /// Reads a record from `cursor`.
@@ -648,6 +650,45 @@ macro_rules! create_record_type {
648650 Self :: coerce:: <[ < $format: camel Record >] >( value, & $fields)
649651 }
650652 }
653+
654+ #[ cfg( test) ]
655+ mod tests {
656+ use super :: * ;
657+ use std:: path:: PathBuf ;
658+
659+ /// Creates a test to ensure that the record is still able to be read, even when missing
660+ /// some of the optional fields.
661+ #[ test]
662+ fn test_missing_optional_fields( ) -> Result <( ) , DmapError > {
663+ let filename: PathBuf = PathBuf :: from( format!( "tests/test_files/test.{}" , stringify!( $format) ) ) ;
664+ let data = [ < $format: camel Record >] :: sniff_file( & filename) . expect( "Unable to sniff file" ) ;
665+ let recs = data. inner( ) ;
666+
667+ for field in $fields. scalars_optional. iter( ) . chain( $fields. vectors_optional. iter( ) ) {
668+ let mut cloned_rec = recs. clone( ) ;
669+ let _ = cloned_rec. shift_remove( field. 0 ) ;
670+ let _ = [ < $format: camel Record >] :: try_from( & mut cloned_rec) ?;
671+ }
672+ Ok ( ( ) )
673+ }
674+
675+ /// Creates a test to ensure that the record is not able to be read when missing
676+ /// some of the required fields.
677+ #[ test]
678+ fn test_missing_required_fields( ) -> Result <( ) , DmapError > {
679+ let filename: PathBuf = PathBuf :: from( format!( "tests/test_files/test.{}" , stringify!( $format) ) ) ;
680+ let data = [ < $format: camel Record >] :: sniff_file( & filename) . expect( "Unable to sniff file" ) ;
681+ let recs = data. inner( ) ;
682+
683+ for field in $fields. scalars_required. iter( ) . chain( $fields. vectors_required. iter( ) ) {
684+ let mut cloned_rec = recs. clone( ) ;
685+ let _ = cloned_rec. shift_remove( field. 0 ) ;
686+ let res = [ < $format: camel Record >] :: try_from( & mut cloned_rec) ;
687+ assert!( res. is_err( ) ) ;
688+ }
689+ Ok ( ( ) )
690+ }
691+ }
651692 }
652693 }
653694}
0 commit comments