2020-08-01 20:01:39 +00:00
/*
2021-03-12 23:23:34 +00:00
* Copyright ( c ) 2020 - 2021 , the SerenityOS developers .
2020-08-01 20:01:39 +00:00
*
2021-04-22 08:24:48 +00:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-08-01 20:01:39 +00:00
*/
2021-04-25 05:53:23 +00:00
# include <LibTest/TestCase.h>
2020-08-20 16:17:19 +00:00
2020-09-06 19:40:46 +00:00
# include <AK/Array.h>
2023-01-25 19:06:16 +00:00
# include <AK/BitStream.h>
2023-01-25 19:19:05 +00:00
# include <AK/MemoryStream.h>
2021-03-12 23:23:34 +00:00
# include <AK/Random.h>
2020-08-01 20:01:39 +00:00
# include <LibCompress/Deflate.h>
2023-12-01 18:42:54 +00:00
# include <LibCore/File.h>
2021-04-18 02:20:56 +00:00
# include <cstring>
2020-08-01 20:01:39 +00:00
2024-07-04 21:39:15 +00:00
# define TEST_INPUT(x) ("deflate-test-files / " x)
2023-12-01 18:42:54 +00:00
2020-09-10 12:31:54 +00:00
TEST_CASE ( canonical_code_simple )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 32 > const code {
2020-09-10 12:31:54 +00:00
0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 ,
0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 ,
0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05 , 0x05
} ;
2022-04-01 17:58:27 +00:00
Array < u8 , 6 > const input {
2020-09-10 12:31:54 +00:00
0x00 , 0x42 , 0x84 , 0xa9 , 0xb0 , 0x15
} ;
2022-04-01 17:58:27 +00:00
Array < u32 , 9 > const output {
2020-09-10 12:31:54 +00:00
0x00 , 0x01 , 0x01 , 0x02 , 0x03 , 0x05 , 0x08 , 0x0d , 0x15
} ;
2022-04-01 17:58:27 +00:00
auto const huffman = Compress : : CanonicalCode : : from_bytes ( code ) . value ( ) ;
2023-01-30 10:05:43 +00:00
auto memory_stream = MUST ( try_make < FixedMemoryStream > ( input ) ) ;
2023-01-30 10:04:23 +00:00
LittleEndianInputBitStream bit_stream { move ( memory_stream ) } ;
2020-09-10 12:31:54 +00:00
for ( size_t idx = 0 ; idx < 9 ; + + idx )
2023-01-30 10:04:23 +00:00
EXPECT_EQ ( MUST ( huffman . read_symbol ( bit_stream ) ) , output [ idx ] ) ;
2020-09-10 12:31:54 +00:00
}
TEST_CASE ( canonical_code_complex )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 6 > const code {
2020-09-10 12:31:54 +00:00
0x03 , 0x02 , 0x03 , 0x03 , 0x02 , 0x03
} ;
2022-04-01 17:58:27 +00:00
Array < u8 , 4 > const input {
2020-09-10 12:31:54 +00:00
0xa1 , 0xf3 , 0xa1 , 0xf3
} ;
2022-04-01 17:58:27 +00:00
Array < u32 , 12 > const output {
2020-09-10 12:31:54 +00:00
0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05
} ;
2022-04-01 17:58:27 +00:00
auto const huffman = Compress : : CanonicalCode : : from_bytes ( code ) . value ( ) ;
2023-01-30 10:05:43 +00:00
auto memory_stream = MUST ( try_make < FixedMemoryStream > ( input ) ) ;
2023-01-30 10:04:23 +00:00
LittleEndianInputBitStream bit_stream { move ( memory_stream ) } ;
2020-09-10 12:31:54 +00:00
for ( size_t idx = 0 ; idx < 12 ; + + idx )
2023-01-30 10:04:23 +00:00
EXPECT_EQ ( MUST ( huffman . read_symbol ( bit_stream ) ) , output [ idx ] ) ;
2020-09-10 12:31:54 +00:00
}
2023-10-09 16:54:49 +00:00
TEST_CASE ( invalid_canonical_code )
{
Array < u8 , 257 > code ;
code . fill ( 0x08 ) ;
EXPECT ( Compress : : CanonicalCode : : from_bytes ( code ) . is_error ( ) ) ;
}
2020-08-20 16:17:19 +00:00
TEST_CASE ( deflate_decompress_compressed_block )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 28 > const compressed {
2020-08-20 16:17:19 +00:00
0x0B , 0xC9 , 0xC8 , 0x2C , 0x56 , 0x00 , 0xA2 , 0x44 , 0x85 , 0xE2 , 0xCC , 0xDC ,
0x82 , 0x9C , 0x54 , 0x85 , 0x92 , 0xD4 , 0x8A , 0x12 , 0x85 , 0xB4 , 0x4C , 0x20 ,
0xCB , 0x4A , 0x13 , 0x00
} ;
2020-08-01 20:01:39 +00:00
2024-04-24 10:53:44 +00:00
u8 const uncompressed [ ] = " This is a simple text file :) " ;
2020-08-01 20:01:39 +00:00
2022-04-01 17:58:27 +00:00
auto const decompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed ) ;
2020-12-30 23:35:15 +00:00
EXPECT ( decompressed . value ( ) . bytes ( ) = = ReadonlyBytes ( { uncompressed , sizeof ( uncompressed ) - 1 } ) ) ;
2020-08-20 16:17:19 +00:00
}
2020-08-01 20:01:39 +00:00
2020-08-26 12:22:25 +00:00
TEST_CASE ( deflate_decompress_uncompressed_block )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 18 > const compressed {
2020-08-26 12:22:25 +00:00
0x01 , 0x0d , 0x00 , 0xf2 , 0xff , 0x48 , 0x65 , 0x6c , 0x6c , 0x6f , 0x2c , 0x20 ,
0x57 , 0x6f , 0x72 , 0x6c , 0x64 , 0x21
} ;
2024-04-24 10:53:44 +00:00
u8 const uncompressed [ ] = " Hello, World! " ;
2020-08-26 12:22:25 +00:00
2022-04-01 17:58:27 +00:00
auto const decompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed ) ;
2020-12-30 23:35:15 +00:00
EXPECT ( decompressed . value ( ) . bytes ( ) = = ( ReadonlyBytes { uncompressed , sizeof ( uncompressed ) - 1 } ) ) ;
2020-08-26 12:22:25 +00:00
}
TEST_CASE ( deflate_decompress_multiple_blocks )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 84 > const compressed {
2020-08-26 12:22:25 +00:00
0x00 , 0x1f , 0x00 , 0xe0 , 0xff , 0x54 , 0x68 , 0x65 , 0x20 , 0x66 , 0x69 , 0x72 ,
0x73 , 0x74 , 0x20 , 0x62 , 0x6c , 0x6f , 0x63 , 0x6b , 0x20 , 0x69 , 0x73 , 0x20 ,
0x75 , 0x6e , 0x63 , 0x6f , 0x6d , 0x70 , 0x72 , 0x65 , 0x73 , 0x73 , 0x65 , 0x64 ,
0x53 , 0x48 , 0xcc , 0x4b , 0x51 , 0x28 , 0xc9 , 0x48 , 0x55 , 0x28 , 0x4e , 0x4d ,
0xce , 0x07 , 0x32 , 0x93 , 0x72 , 0xf2 , 0x93 , 0xb3 , 0x15 , 0x32 , 0x8b , 0x15 ,
0x92 , 0xf3 , 0x73 , 0x0b , 0x8a , 0x52 , 0x8b , 0x8b , 0x53 , 0x53 , 0xf4 , 0x00
} ;
2024-04-24 10:53:44 +00:00
u8 const uncompressed [ ] = " The first block is uncompressed and the second block is compressed. " ;
2020-08-26 12:22:25 +00:00
2022-04-01 17:58:27 +00:00
auto const decompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed ) ;
2020-12-30 23:35:15 +00:00
EXPECT ( decompressed . value ( ) . bytes ( ) = = ( ReadonlyBytes { uncompressed , sizeof ( uncompressed ) - 1 } ) ) ;
2020-08-26 12:22:25 +00:00
}
TEST_CASE ( deflate_decompress_zeroes )
{
2022-04-01 17:58:27 +00:00
Array < u8 , 20 > const compressed {
2020-08-26 12:22:25 +00:00
0xed , 0xc1 , 0x01 , 0x0d , 0x00 , 0x00 , 0x00 , 0xc2 , 0xa0 , 0xf7 , 0x4f , 0x6d ,
0x0f , 0x07 , 0x14 , 0x00 , 0x00 , 0x00 , 0xf0 , 0x6e
} ;
2022-04-01 17:58:27 +00:00
Array < u8 , 4096 > const uncompressed { 0 } ;
2020-08-26 12:22:25 +00:00
2022-04-01 17:58:27 +00:00
auto const decompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed ) ;
2020-12-30 23:35:15 +00:00
EXPECT ( uncompressed = = decompressed . value ( ) . bytes ( ) ) ;
2020-08-26 12:22:25 +00:00
}
2021-03-12 23:23:34 +00:00
TEST_CASE ( deflate_round_trip_store )
{
2021-09-05 22:59:52 +00:00
auto original = ByteBuffer : : create_uninitialized ( 1024 ) . release_value ( ) ;
2023-04-02 17:08:43 +00:00
fill_with_random ( original ) ;
2023-05-07 18:14:06 +00:00
auto compressed = TRY_OR_FAIL ( Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : STORE ) ) ;
auto uncompressed = TRY_OR_FAIL ( Compress : : DeflateDecompressor : : decompress_all ( compressed ) ) ;
EXPECT ( uncompressed = = original ) ;
2021-03-12 23:23:34 +00:00
}
TEST_CASE ( deflate_round_trip_compress )
{
2021-09-05 22:59:52 +00:00
auto original = ByteBuffer : : create_zeroed ( 2048 ) . release_value ( ) ;
2023-04-02 17:08:43 +00:00
fill_with_random ( original . bytes ( ) . trim ( 1024 ) ) ; // we pre-filled the second half with 0s to make sure we test back references as well
2021-03-12 23:23:34 +00:00
// Since the different levels just change how much time is spent looking for better matches, just use fast here to reduce test time
2023-05-07 18:14:06 +00:00
auto compressed = TRY_OR_FAIL ( Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : FAST ) ) ;
auto uncompressed = TRY_OR_FAIL ( Compress : : DeflateDecompressor : : decompress_all ( compressed ) ) ;
EXPECT ( uncompressed = = original ) ;
2021-03-12 23:23:34 +00:00
}
TEST_CASE ( deflate_round_trip_compress_large )
{
2021-03-14 14:08:43 +00:00
auto size = Compress : : DeflateCompressor : : block_size * 2 ;
2021-09-05 22:59:52 +00:00
auto original = ByteBuffer : : create_uninitialized ( size ) . release_value ( ) ; // Compress a buffer larger than the maximum block size to test the sliding window mechanism
2023-04-02 17:08:43 +00:00
fill_with_random ( original ) ;
2021-03-12 23:23:34 +00:00
// Since the different levels just change how much time is spent looking for better matches, just use fast here to reduce test time
2023-05-07 18:14:06 +00:00
auto compressed = TRY_OR_FAIL ( Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : FAST ) ) ;
auto uncompressed = TRY_OR_FAIL ( Compress : : DeflateDecompressor : : decompress_all ( compressed ) ) ;
EXPECT ( uncompressed = = original ) ;
2021-03-12 23:23:34 +00:00
}
2021-03-14 09:43:18 +00:00
TEST_CASE ( deflate_compress_literals )
{
// This byte array is known to not produce any back references with our lz77 implementation even at the highest compression settings
Array < u8 , 0x13 > test { 0 , 0 , 0 , 0 , 0x72 , 0 , 0 , 0xee , 0 , 0 , 0 , 0x26 , 0 , 0 , 0 , 0x28 , 0 , 0 , 0x72 } ;
2023-05-07 18:14:06 +00:00
auto compressed = TRY_OR_FAIL ( Compress : : DeflateCompressor : : compress_all ( test , Compress : : DeflateCompressor : : CompressionLevel : : GOOD ) ) ;
2021-03-14 09:43:18 +00:00
}
2023-12-01 18:42:54 +00:00
TEST_CASE ( ossfuzz_63183 )
{
auto path = TEST_INPUT ( " clusterfuzz-testcase-minimized-FuzzDeflateCompression-6163230961303552.fuzz " sv ) ;
auto test_file = MUST ( Core : : File : : open ( path , Core : : File : : OpenMode : : Read ) ) ;
auto test_data = MUST ( test_file - > read_until_eof ( ) ) ;
auto compressed = TRY_OR_FAIL ( Compress : : DeflateCompressor : : compress_all ( test_data , Compress : : DeflateCompressor : : CompressionLevel : : GOOD ) ) ;
auto decompressed = TRY_OR_FAIL ( Compress : : DeflateDecompressor : : decompress_all ( compressed ) ) ;
EXPECT ( test_data = = decompressed ) ;
}
2024-01-13 06:28:44 +00:00
TEST_CASE ( ossfuzz_58046 )
{
auto path = TEST_INPUT ( " clusterfuzz-testcase-minimized-FuzzDeflateDecompression-5523852259360768.fuzz " sv ) ;
auto test_file = TRY_OR_FAIL ( Core : : File : : open ( path , Core : : File : : OpenMode : : Read ) ) ;
auto test_data = TRY_OR_FAIL ( test_file - > read_until_eof ( ) ) ;
EXPECT ( Compress : : DeflateDecompressor : : decompress_all ( test_data ) . is_error ( ) ) ;
}