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>
2020-09-10 12:31:54 +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>
2022-12-24 22:09:28 +00:00
# include <LibCore/BitStream.h>
2022-12-02 21:01:44 +00:00
# include <LibCore/MemoryStream.h>
2021-04-18 02:20:56 +00:00
# include <cstring>
2020-08-01 20:01:39 +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 ( ) ;
2022-12-07 14:47:44 +00:00
auto memory_stream = MUST ( Core : : Stream : : FixedMemoryStream : : construct ( input ) ) ;
2022-12-02 21:01:44 +00:00
auto bit_stream = MUST ( Core : : Stream : : LittleEndianInputBitStream : : construct ( move ( memory_stream ) ) ) ;
2020-09-10 12:31:54 +00:00
for ( size_t idx = 0 ; idx < 9 ; + + idx )
2022-12-02 21:01:44 +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 ( ) ;
2022-12-07 14:47:44 +00:00
auto memory_stream = MUST ( Core : : Stream : : FixedMemoryStream : : construct ( input ) ) ;
2022-12-02 21:01:44 +00:00
auto bit_stream = MUST ( Core : : Stream : : LittleEndianInputBitStream : : construct ( move ( memory_stream ) ) ) ;
2020-09-10 12:31:54 +00:00
for ( size_t idx = 0 ; idx < 12 ; + + idx )
2022-12-02 21:01:44 +00:00
EXPECT_EQ ( MUST ( huffman . read_symbol ( * bit_stream ) ) , output [ idx ] ) ;
2020-09-10 12:31:54 +00:00
}
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
2020-08-20 16:17:19 +00:00
const u8 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
} ;
const u8 uncompressed [ ] = " Hello, World! " ;
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
} ;
const u8 uncompressed [ ] = " The first block is uncompressed and the second block is compressed. " ;
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 ( ) ;
2021-03-12 23:23:34 +00:00
fill_with_random ( original . data ( ) , 1024 ) ;
auto compressed = Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : STORE ) ;
EXPECT ( compressed . has_value ( ) ) ;
auto uncompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed . value ( ) ) ;
2022-12-02 21:01:44 +00:00
EXPECT ( ! uncompressed . is_error ( ) ) ;
2021-03-12 23:23:34 +00:00
EXPECT ( uncompressed . value ( ) = = original ) ;
}
TEST_CASE ( deflate_round_trip_compress )
{
2021-09-05 22:59:52 +00:00
auto original = ByteBuffer : : create_zeroed ( 2048 ) . release_value ( ) ;
2021-05-24 11:12:49 +00:00
fill_with_random ( original . data ( ) , 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
auto compressed = Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : FAST ) ;
EXPECT ( compressed . has_value ( ) ) ;
auto uncompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed . value ( ) ) ;
2022-12-02 21:01:44 +00:00
EXPECT ( ! uncompressed . is_error ( ) ) ;
2021-03-12 23:23:34 +00:00
EXPECT ( uncompressed . value ( ) = = original ) ;
}
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
2021-03-14 14:08:43 +00:00
fill_with_random ( original . data ( ) , size ) ;
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
auto compressed = Compress : : DeflateCompressor : : compress_all ( original , Compress : : DeflateCompressor : : CompressionLevel : : FAST ) ;
EXPECT ( compressed . has_value ( ) ) ;
auto uncompressed = Compress : : DeflateDecompressor : : decompress_all ( compressed . value ( ) ) ;
2022-12-02 21:01:44 +00:00
EXPECT ( ! uncompressed . is_error ( ) ) ;
2021-03-12 23:23:34 +00:00
EXPECT ( uncompressed . value ( ) = = original ) ;
}
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 } ;
auto compressed = Compress : : DeflateCompressor : : compress_all ( test , Compress : : DeflateCompressor : : CompressionLevel : : GOOD ) ;
EXPECT ( compressed . has_value ( ) ) ;
}