22
33use std:: io:: Cursor ;
44
5- use fastpfor:: rust:: { BLOCK_SIZE_256 , DEFAULT_PAGE_SIZE , FastPFOR , Integer } ;
5+ use fastpfor:: rust:: { FastPFOR , Integer , BLOCK_SIZE_128 , BLOCK_SIZE_256 , DEFAULT_PAGE_SIZE } ;
66use libfuzzer_sys:: fuzz_target;
7+ use std:: num:: NonZeroU32 ;
78
8- fuzz_target ! ( |input_data: Vec <u32 >| {
9- let mut codec = FastPFOR :: new( DEFAULT_PAGE_SIZE , BLOCK_SIZE_256 ) ;
9+ fuzz_target ! ( |data: FuzzInput | {
10+ let input = data. data;
11+ let block_size = NonZeroU32 :: from( data. codec) ;
12+ let mut codec = FastPFOR :: new( DEFAULT_PAGE_SIZE , block_size) ;
1013
11- // Limit input size to avoid timeouts
12- let input_data: Vec <u32 > = input_data. into_iter( ) . take( 10_000 ) . collect( ) ;
14+ // TODO: empty input is encoded as empty, which does not match the CPP version
15+ if input. is_empty( ) {
16+ return ;
17+ }
18+
19+ // TODO: only multiples of block size seem to be exported from the compress - decompress cycle
20+ let bs = block_size. get( ) as usize ;
21+ if input. len( ) < bs {
22+ return ;
23+ }
24+ let last_block_size_multiple = input. len( ) / bs * bs;
25+ let input = input
26+ . into_iter( )
27+ . take( last_block_size_multiple as usize )
28+ . collect:: <Vec <_>>( ) ;
1329
1430 // Allocate output buffer with generous size
15- let mut compressed = vec![ 0u32 ; input_data . len( ) * 2 + 1024 ] ;
31+ let mut compressed = vec![ 0u32 ; input . len( ) * 2 + 1024 ] ;
1632
1733 // Compress the data
1834 let mut output_offset = Cursor :: new( 0 ) ;
1935 codec
2036 . compress(
21- & input_data ,
22- input_data . len( ) as u32 ,
37+ & input ,
38+ input . len( ) as u32 ,
2339 & mut Cursor :: new( 0 ) ,
2440 & mut compressed,
2541 & mut output_offset,
2642 )
2743 . unwrap( ) ;
2844 let compressed_size = output_offset. position( ) as u32 ;
29- if !input_data. is_empty( ) {
30- assert!( compressed_size != 0 , "compression should not be empty" ) ;
31- }
45+ assert!( compressed_size != 0 , "compression should not be empty" ) ;
3246
3347 // Now decompress
34- let mut decompressed = vec![ 0u32 ; input_data . len( ) ] ;
48+ let mut decompressed = vec![ 0u32 ; input . len( ) ] ;
3549 let mut output_offset = Cursor :: new( 0 ) ;
3650
3751 codec
@@ -46,21 +60,50 @@ fuzz_target!(|input_data: Vec<u32>| {
4660 let decompressed_length = output_offset. position( ) as usize ;
4761
4862 // Verify roundtrip
49- if decompressed_length + input_data. len( ) < 200 {
63+ assert_eq!( decompressed_length, input. len( ) ) ;
64+ if decompressed_length + input. len( ) < 200 {
5065 assert_eq!(
51- input_data ,
66+ input ,
5267 decompressed[ ..decompressed_length] ,
53- "Decompressed length mismatch: expected {}, got {decompressed_length}" ,
54- input_data . len( )
68+ "Decompressed mismatch: expected {}, got {decompressed_length}" ,
69+ input . len( )
5570 ) ;
5671 } else {
57- for ( i, ( & original, & decoded) ) in input_data . iter( ) . zip( decompressed. iter( ) ) . enumerate( ) {
72+ for ( i, ( & original, & decoded) ) in input . iter( ) . zip( decompressed. iter( ) ) . enumerate( ) {
5873 assert_eq!(
5974 original, decoded,
6075 "Mismatch at position {}: expected {}, got {}" ,
6176 i, original, decoded
6277 ) ;
6378 }
6479 }
65- assert_eq!( decompressed_length, input_data. len( ) ) ;
6680} ) ;
81+
82+ #[ derive( arbitrary:: Arbitrary ) ]
83+ struct FuzzInput {
84+ data : Vec < u32 > ,
85+ codec : FuzzCodec ,
86+ }
87+
88+ impl std:: fmt:: Debug for FuzzInput {
89+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
90+ f. debug_struct ( "FuzzInput" )
91+ . field ( "data_length" , & self . data . len ( ) )
92+ . field ( "codec" , & self . codec )
93+ . finish ( )
94+ }
95+ }
96+
97+ #[ derive( arbitrary:: Arbitrary , Debug , Clone , Copy , PartialEq , Eq ) ]
98+ enum FuzzCodec {
99+ FastPFOR256 ,
100+ FastPFOR128 ,
101+ }
102+ impl From < FuzzCodec > for NonZeroU32 {
103+ fn from ( codec : FuzzCodec ) -> Self {
104+ match codec {
105+ FuzzCodec :: FastPFOR256 => BLOCK_SIZE_256 ,
106+ FuzzCodec :: FastPFOR128 => BLOCK_SIZE_128 ,
107+ }
108+ }
109+ }
0 commit comments