| /////////////////////////////////////////////////////////////////////////////// |
| // |
| /// \file test_block_header.c |
| /// \brief Tests Block Header coders |
| // |
| // Copyright (C) 2007 Lasse Collin |
| // |
| // This library is free software; you can redistribute it and/or |
| // modify it under the terms of the GNU Lesser General Public |
| // License as published by the Free Software Foundation; either |
| // version 2.1 of the License, or (at your option) any later version. |
| // |
| // This library is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| // Lesser General Public License for more details. |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "tests.h" |
| |
| |
| static uint8_t buffer[4096]; |
| static lzma_stream strm = LZMA_STREAM_INIT; |
| static lzma_options_block known_options; |
| static lzma_options_block decoded_options; |
| |
| // We want to test zero, one, and two filters in the chain. |
| |
| static const lzma_options_filter filters_none[1] = { |
| { |
| .id = LZMA_VLI_VALUE_UNKNOWN, |
| .options = NULL, |
| }, |
| }; |
| |
| static const lzma_options_filter filters_powerpc[2] = { |
| { |
| .id = LZMA_FILTER_POWERPC, |
| .options = NULL, |
| }, { |
| .id = LZMA_VLI_VALUE_UNKNOWN, |
| .options = NULL, |
| }, |
| }; |
| |
| static const lzma_options_delta options_delta = { |
| .distance = 4, |
| }; |
| |
| static const lzma_options_filter filters_delta[3] = { |
| { |
| .id = LZMA_FILTER_DELTA, |
| .options = (void *)(&options_delta), |
| }, { |
| .id = LZMA_FILTER_COPY, |
| .options = NULL, |
| }, { |
| .id = LZMA_VLI_VALUE_UNKNOWN, |
| .options = NULL, |
| }, |
| }; |
| |
| |
| static bool |
| encode(uint32_t header_size) |
| { |
| memcrap(buffer, sizeof(buffer)); |
| |
| if (lzma_block_header_size(&known_options) != LZMA_OK) |
| return true; |
| |
| if (known_options.header_size != header_size) |
| return true; |
| |
| if (lzma_block_header_encode(buffer, &known_options) != LZMA_OK) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static bool |
| decode_ret(uint32_t header_size, lzma_ret ret_ok) |
| { |
| memcrap(&decoded_options, sizeof(decoded_options)); |
| decoded_options.has_crc32 = known_options.has_crc32; |
| |
| expect(lzma_block_header_decoder(&strm, &decoded_options) == LZMA_OK); |
| |
| return decoder_loop_ret(&strm, buffer, header_size, ret_ok); |
| } |
| |
| |
| static bool |
| decode(uint32_t header_size) |
| { |
| memcrap(&decoded_options, sizeof(decoded_options)); |
| decoded_options.has_crc32 = known_options.has_crc32; |
| |
| expect(lzma_block_header_decoder(&strm, &decoded_options) == LZMA_OK); |
| |
| if (decoder_loop(&strm, buffer, header_size)) |
| return true; |
| |
| if (known_options.has_eopm != decoded_options.has_eopm) |
| return true; |
| |
| if (known_options.is_metadata != decoded_options.is_metadata) |
| return true; |
| |
| if (known_options.compressed_size == LZMA_VLI_VALUE_UNKNOWN |
| && known_options.compressed_reserve != 0) { |
| if (decoded_options.compressed_size != 0) |
| return true; |
| } else if (known_options.compressed_size |
| != decoded_options.compressed_size) { |
| return true; |
| } |
| |
| if (known_options.uncompressed_size == LZMA_VLI_VALUE_UNKNOWN |
| && known_options.uncompressed_reserve != 0) { |
| if (decoded_options.uncompressed_size != 0) |
| return true; |
| } else if (known_options.uncompressed_size |
| != decoded_options.uncompressed_size) { |
| return true; |
| } |
| |
| if (known_options.compressed_reserve != 0 |
| && known_options.compressed_reserve |
| != decoded_options.compressed_reserve) |
| return true; |
| |
| if (known_options.uncompressed_reserve != 0 |
| && known_options.uncompressed_reserve |
| != decoded_options.uncompressed_reserve) |
| return true; |
| |
| if (known_options.padding != decoded_options.padding) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static bool |
| code(uint32_t header_size) |
| { |
| return encode(header_size) || decode(header_size); |
| } |
| |
| |
| static bool |
| helper_loop(uint32_t unpadded_size, uint32_t multiple) |
| { |
| for (int i = 0; i <= LZMA_BLOCK_HEADER_PADDING_MAX; ++i) { |
| known_options.padding = i; |
| if (code(unpadded_size + i)) |
| return true; |
| } |
| |
| for (int i = 0 - LZMA_BLOCK_HEADER_PADDING_MAX - 1; |
| i <= LZMA_BLOCK_HEADER_PADDING_MAX + 1; ++i) { |
| known_options.alignment = i; |
| |
| uint32_t size = unpadded_size; |
| while ((size + known_options.alignment) % multiple) |
| ++size; |
| |
| known_options.padding = LZMA_BLOCK_HEADER_PADDING_AUTO; |
| if (code(size)) |
| return true; |
| |
| } while (++known_options.alignment |
| <= LZMA_BLOCK_HEADER_PADDING_MAX + 1); |
| |
| return false; |
| } |
| |
| |
| static bool |
| helper(uint32_t unpadded_size, uint32_t multiple) |
| { |
| known_options.has_crc32 = false; |
| known_options.is_metadata = false; |
| if (helper_loop(unpadded_size, multiple)) |
| return true; |
| |
| known_options.has_crc32 = false; |
| known_options.is_metadata = true; |
| if (helper_loop(unpadded_size, multiple)) |
| return true; |
| |
| known_options.has_crc32 = true; |
| known_options.is_metadata = false; |
| if (helper_loop(unpadded_size + 4, multiple)) |
| return true; |
| |
| known_options.has_crc32 = true; |
| known_options.is_metadata = true; |
| if (helper_loop(unpadded_size + 4, multiple)) |
| return true; |
| |
| return false; |
| } |
| |
| |
| static void |
| test1(void) |
| { |
| known_options = (lzma_options_block){ |
| .has_eopm = true, |
| .compressed_size = LZMA_VLI_VALUE_UNKNOWN, |
| .uncompressed_size = LZMA_VLI_VALUE_UNKNOWN, |
| .compressed_reserve = 0, |
| .uncompressed_reserve = 0, |
| }; |
| memcpy(known_options.filters, filters_none, sizeof(filters_none)); |
| expect(!helper(2, 1)); |
| |
| memcpy(known_options.filters, filters_powerpc, |
| sizeof(filters_powerpc)); |
| expect(!helper(3, 4)); |
| |
| memcpy(known_options.filters, filters_delta, sizeof(filters_delta)); |
| expect(!helper(5, 1)); |
| |
| known_options.padding = LZMA_BLOCK_HEADER_PADDING_MAX + 1; |
| expect(lzma_block_header_size(&known_options) == LZMA_PROG_ERROR); |
| } |
| |
| |
| static void |
| test2_helper(uint32_t unpadded_size, uint32_t multiple) |
| { |
| known_options.has_eopm = true; |
| known_options.compressed_size = LZMA_VLI_VALUE_UNKNOWN; |
| known_options.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN; |
| known_options.compressed_reserve = 1; |
| known_options.uncompressed_reserve = 1; |
| expect(!helper(unpadded_size + 2, multiple)); |
| |
| known_options.compressed_reserve = LZMA_VLI_BYTES_MAX; |
| known_options.uncompressed_reserve = LZMA_VLI_BYTES_MAX; |
| expect(!helper(unpadded_size + 18, multiple)); |
| |
| known_options.compressed_size = 1234; |
| known_options.uncompressed_size = 2345; |
| expect(!helper(unpadded_size + 18, multiple)); |
| |
| known_options.compressed_reserve = 1; |
| known_options.uncompressed_reserve = 1; |
| expect(lzma_block_header_size(&known_options) == LZMA_PROG_ERROR); |
| } |
| |
| |
| static void |
| test2(void) |
| { |
| memcpy(known_options.filters, filters_none, sizeof(filters_none)); |
| test2_helper(2, 1); |
| |
| memcpy(known_options.filters, filters_powerpc, |
| sizeof(filters_powerpc)); |
| test2_helper(3, 4); |
| |
| memcpy(known_options.filters, filters_delta, |
| sizeof(filters_delta)); |
| test2_helper(5, 1); |
| } |
| |
| |
| static void |
| test3(void) |
| { |
| known_options = (lzma_options_block){ |
| .has_crc32 = false, |
| .has_eopm = true, |
| .is_metadata = false, |
| .compressed_size = LZMA_VLI_VALUE_UNKNOWN, |
| .uncompressed_size = LZMA_VLI_VALUE_UNKNOWN, |
| .compressed_reserve = 1, |
| .uncompressed_reserve = 1, |
| }; |
| memcpy(known_options.filters, filters_none, sizeof(filters_none)); |
| |
| known_options.header_size = 3; |
| expect(lzma_block_header_encode(buffer, &known_options) |
| == LZMA_PROG_ERROR); |
| |
| known_options.header_size = 4; |
| expect(lzma_block_header_encode(buffer, &known_options) == LZMA_OK); |
| |
| known_options.header_size = 5; |
| expect(lzma_block_header_encode(buffer, &known_options) |
| == LZMA_PROG_ERROR); |
| |
| // NOTE: This assumes that Filter ID 0x1F is not supported. Update |
| // this test to use some other ID if 0x1F becomes supported. |
| known_options.filters[0].id = 0x1F; |
| known_options.header_size = 5; |
| expect(lzma_block_header_encode(buffer, &known_options) |
| == LZMA_HEADER_ERROR); |
| } |
| |
| |
| static void |
| test4(void) |
| { |
| known_options = (lzma_options_block){ |
| .has_crc32 = false, |
| .has_eopm = true, |
| .is_metadata = false, |
| .compressed_size = 0, |
| .uncompressed_size = 0, |
| .compressed_reserve = LZMA_VLI_BYTES_MAX, |
| .uncompressed_reserve = LZMA_VLI_BYTES_MAX, |
| .padding = 0, |
| }; |
| memcpy(known_options.filters, filters_powerpc, |
| sizeof(filters_powerpc)); |
| expect(!code(21)); |
| |
| // Reserved bits |
| buffer[0] ^= 0x40; |
| expect(!decode_ret(1, LZMA_HEADER_ERROR)); |
| buffer[0] ^= 0x40; |
| |
| buffer[1] ^= 0x40; |
| expect(decode_ret(21, LZMA_HEADER_ERROR)); |
| buffer[1] ^= 0x40; |
| |
| |
| } |
| |
| |
| int |
| main(void) |
| { |
| lzma_init(); |
| |
| test1(); |
| test2(); |
| test3(); |
| test4(); |
| |
| lzma_end(&strm); |
| |
| return 0; |
| } |