From 7479a69a45a1b8fdb9a209e11f247dce11ac1ba0 Mon Sep 17 00:00:00 2001 From: Jia Tan Date: Thu, 12 Jan 2023 22:29:07 +0800 Subject: [PATCH] Tests: Refactors existing lzma_index tests. Converts the existing lzma_index tests into tuktests and covers every API function from index.h except for lzma_file_info_decoder, which can be tested in the future. --- tests/test_index.c | 2212 +++++++++++++++++++++++++++++++------------- 1 file changed, 1580 insertions(+), 632 deletions(-) diff --git a/tests/test_index.c b/tests/test_index.c index 31b958d5..f63d2a79 100644 --- a/tests/test_index.c +++ b/tests/test_index.c @@ -2,8 +2,12 @@ // /// \file test_index.c /// \brief Tests functions handling the lzma_index structure +/// +/// \todo Implement tests for lzma_file_info_decoder +// +// Authors: Jia Tan +// Lasse Collin // -// Author: Lasse Collin // // This file has been put into the public domain. // You can do whatever you want with this file. @@ -12,65 +16,1167 @@ #include "tests.h" -#define MEMLIMIT (LZMA_VLI_C(1) << 20) - -#define SMALL_COUNT 3 -#define BIG_COUNT 5555 +// liblzma internal header file needed for: +// UNPADDED_SIZE_MIN +// UNPADDED_SIZE_MAX +// vli_ceil4 +#include "common/index.h" -static lzma_index * -create_empty(void) +#define MEMLIMIT (UINT64_C(1) << 20) + +static uint8_t *decode_buffer; +static size_t decode_buffer_size = 0; +static lzma_index *decode_test_index; + + +static void +test_lzma_index_memusage(void) { - lzma_index *i = lzma_index_init(NULL); - expect(i != NULL); - return i; -} + // The return value from lzma_index_memusage is an approximation + // of the amount of memory needed for lzma_index for a given + // amount of Streams and Blocks. It will be an upperbound, + // so this test will mostly sanity check and error check the + // function. + // The maximum number of Streams should be UINT32_MAX in the + // current implementation even though the parameter is lzma_vli. + assert_uint_eq(lzma_index_memusage((lzma_vli)UINT32_MAX + 1, 1), + UINT64_MAX); -static lzma_index * -create_small(void) -{ - lzma_index *i = lzma_index_init(NULL); - expect(i != NULL); - expect(lzma_index_append(i, NULL, 101, 555) == LZMA_OK); - expect(lzma_index_append(i, NULL, 602, 777) == LZMA_OK); - expect(lzma_index_append(i, NULL, 804, 999) == LZMA_OK); - return i; -} + // The maximum number of Blocks should be LZMA_VLI_MAX + assert_uint_eq(lzma_index_memusage(1, LZMA_VLI_MAX), UINT64_MAX); + // Number of Streams must be non-zero + assert_uint_eq(lzma_index_memusage(0, 1), UINT64_MAX); -static lzma_index * -create_big(void) -{ - lzma_index *i = lzma_index_init(NULL); - expect(i != NULL); + // Number of Blocks CAN be zero + assert_uint(lzma_index_memusage(1, 0), !=, UINT64_MAX); - lzma_vli total_size = 0; - lzma_vli uncompressed_size = 0; + // Arbitrary values for Stream and Block should work without error + // and should always increase + uint64_t previous = 1; + lzma_vli streams = 1; + lzma_vli blocks = 1; - // Add pseudo-random sizes (but always the same size values). - uint32_t n = 11; - for (size_t j = 0; j < BIG_COUNT; ++j) { - n = 7019 * n + 7607; - const uint32_t t = n * 3011; - expect(lzma_index_append(i, NULL, t, n) == LZMA_OK); - total_size += (t + 3) & ~LZMA_VLI_C(3); - uncompressed_size += n; + // Test 100 different increasing values for Streams and Block + for (int i = 0; i < 100; i++) { + uint64_t current = lzma_index_memusage(streams, blocks); + assert_uint(current, >, previous); + previous = current; + streams += 29; + blocks += 107; } - expect(lzma_index_block_count(i) == BIG_COUNT); - expect(lzma_index_total_size(i) == total_size); - expect(lzma_index_uncompressed_size(i) == uncompressed_size); - expect(lzma_index_total_size(i) + lzma_index_size(i) - + 2 * LZMA_STREAM_HEADER_SIZE - == lzma_index_stream_size(i)); - - return i; + // Force integer overflow in calculation (should result in an error) + assert_uint_eq(lzma_index_memusage(UINT32_MAX, LZMA_VLI_MAX), + UINT64_MAX); } +static void +test_lzma_index_memused(void) +{ + // Very similar to test_lzma_index_memusage above since + // lzma_index_memused is essentially a wrapper for + // lzma_index_memusage + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test with empty Index + assert_uint(lzma_index_memused(idx), <, UINT64_MAX); + + // Append small Blocks and then test again (should pass). + for (lzma_vli i = 0; i < 10; i++) + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint(lzma_index_memused(idx), <, UINT64_MAX); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_append(void) +{ + // Basic input-ouput test done here. + // Less trivial tests for this function are done throughout + // other tests. + + // First test with NULL lzma_index + assert_lzma_ret(lzma_index_append(NULL, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test with invalid Unpadded Size + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN - 1, 1), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MAX + 1, 1), LZMA_PROG_ERROR); + + // Test with invalid Uncompressed Size + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MAX, LZMA_VLI_MAX + 1), + LZMA_PROG_ERROR); + + // Test expected successful Block appends + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, + 2), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, + 3), LZMA_OK); + + lzma_index_end(idx, NULL); + + // Test uncompressed .xz file size growing too large. + // Should result in LZMA_DATA_ERROR. + idx = lzma_index_init(NULL); + + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MAX, + 1), LZMA_DATA_ERROR); + + // Test compressed size growing too large. + // Should result in LZMA_DATA_ERROR. + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_DATA_ERROR); + + // Currently not testing for error case when the size of the Index + // grows too large to be stored. This was not practical to test for + // since too many Blocks needed to be created to cause this. + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_flags(void) +{ + // Only trivial tests done here testing for basic functionality. + // More in-depth testing for this function will be done in + // test_lzma_index_checks. + + // Testing for NULL inputs + assert_lzma_ret(lzma_index_stream_flags(NULL, NULL), + LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_lzma_ret(lzma_index_stream_flags(idx, NULL), + LZMA_PROG_ERROR); + + lzma_stream_flags stream_flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_checks(void) +{ + // Tests should still pass, even if some of the check types + // are disabled. + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_stream_flags stream_flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_NONE + }; + + // First set the check type to None + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_NONE); + + // Set the check type to CRC32 and repeat + stream_flags.check = LZMA_CHECK_CRC32; + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_CRC32); + + // Set the check type to CRC64 and repeat + stream_flags.check = LZMA_CHECK_CRC64; + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_CRC64); + + // Set the check type to SHA256 and repeat + stream_flags.check = LZMA_CHECK_SHA256; + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_SHA256); + + // Create second lzma_index and cat to first + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Set the check type to CRC32 for the second lzma_index + stream_flags.check = LZMA_CHECK_CRC32; + assert_lzma_ret(lzma_index_stream_flags(second, &stream_flags), + LZMA_OK); + + assert_uint_eq(lzma_index_checks(second), + UINT32_C(1) << LZMA_CHECK_CRC32); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // Index should now have both CRC32 and SHA256 + assert_uint_eq(lzma_index_checks(idx), + (UINT32_C(1) << LZMA_CHECK_CRC32) | + (UINT32_C(1) << LZMA_CHECK_SHA256)); + + // Change the check type of the second Stream to SHA256 + stream_flags.check = LZMA_CHECK_SHA256; + assert_lzma_ret(lzma_index_stream_flags(idx, &stream_flags), + LZMA_OK); + + // Index should now have only SHA256 + assert_uint_eq(lzma_index_checks(idx), + UINT32_C(1) << LZMA_CHECK_SHA256); + + // Test with a third Stream + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + stream_flags.check = LZMA_CHECK_CRC64; + assert_lzma_ret(lzma_index_stream_flags(third, &stream_flags), + LZMA_OK); + + assert_uint_eq(lzma_index_checks(third), + UINT32_C(1) << LZMA_CHECK_CRC64); + + assert_lzma_ret(lzma_index_cat(idx, third, NULL), LZMA_OK); + + // Index should now have CRC64 and SHA256 + assert_uint_eq(lzma_index_checks(idx), + (UINT32_C(1) << LZMA_CHECK_CRC64) | + (UINT32_C(1) << LZMA_CHECK_SHA256)); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_padding(void) +{ + // Test NULL lzma_index + assert_lzma_ret(lzma_index_stream_padding(NULL, 0), + LZMA_PROG_ERROR); + + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Test Stream Padding not a multiple of 4 + assert_lzma_ret(lzma_index_stream_padding(idx, 3), + LZMA_PROG_ERROR); + + // Test Stream Padding too large + assert_lzma_ret(lzma_index_stream_padding(idx, LZMA_VLI_MAX - 3), + LZMA_DATA_ERROR); + + // Test Stream Padding valid + assert_lzma_ret(lzma_index_stream_padding(idx, 0x1000), + LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 4), + LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 0), + LZMA_OK); + + // Test Stream Padding causing the file size to grow too large + assert_lzma_ret(lzma_index_append(idx, NULL, + LZMA_VLI_MAX - 0x1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_stream_padding(idx, 0x1000), + LZMA_DATA_ERROR); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_count(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_uint_eq(lzma_index_stream_count(idx), 1); + + // Appending Blocks should not change the Stream count value + assert_lzma_ret(lzma_index_append(idx, NULL, UNPADDED_SIZE_MIN, + 1), LZMA_OK); + + assert_uint_eq(lzma_index_stream_count(idx), 1); + + // Test with multiple Streams + for (uint32_t i = 0; i < 100; i++) { + lzma_index *idx_cat = lzma_index_init(NULL); + assert_true(idx != NULL); + assert_lzma_ret(lzma_index_cat(idx, idx_cat, NULL), LZMA_OK); + assert_uint_eq(lzma_index_stream_count(idx), i + 2); + } + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_block_count(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_uint_eq(lzma_index_block_count(idx), 0); + + const uint32_t iterations = 0x1000; + for (uint32_t i = 0; i < iterations; i++) { + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_uint_eq(lzma_index_block_count(idx), i + 1); + } + + // Create new lzma_index with a few Blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint_eq(lzma_index_block_count(second), 3); + + // Concatenate the lzma_indexes together and the result should have + // the sum of the two individual counts. + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + assert_uint_eq(lzma_index_block_count(idx), iterations + 3); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + assert_uint_eq(lzma_index_block_count(idx), iterations + 4); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Base size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 0 bytes Records + // 2 bytes Index Padding + // 4 bytes CRC32 + // Total: 8 bytes + assert_uint_eq(lzma_index_size(idx), 8); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + + // New size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 2 bytes Records + // 0 bytes Index Padding + // 4 bytes CRC32 + // Total: 8 bytes + assert_uint_eq(lzma_index_size(idx), 8); + + assert_lzma_ret(lzma_index_append(idx, NULL, + LZMA_VLI_MAX / 4, LZMA_VLI_MAX / 4), LZMA_OK); + + // New size should be: + // 1 byte Index Indicator + // 1 byte Number of Records + // 20 bytes Records + // 2 bytes Index Padding + // 4 bytes CRC32 + // Total: 28 bytes + assert_uint_eq(lzma_index_size(idx), 28); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_stream_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Stream size calculated by: + // Size of Stream Header (12 bytes) + // Size of all Blocks + // Size of the Index + // Size of the Stream Footer (12 bytes) + + // First test with empty Index + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 0 bytes + // Size of Index - 8 bytes + // Size of Stream Footer - 12 bytes + // Total: 32 bytes + assert_uint_eq(lzma_index_stream_size(idx), 32); + + // Next, append a few Blocks and retest + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 3000 bytes + // Size of Index - 16 bytes + // Size of Stream Footer - 12 bytes + // Total: 3040 bytes + assert_uint_eq(lzma_index_stream_size(idx), 3040); + + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_uint_eq(lzma_index_stream_size(second), 32); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 1), LZMA_OK); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 1000 bytes + // Size of Index - 12 bytes + // Size of Stream Footer - 12 bytes + // Total: 1036 bytes + assert_uint_eq(lzma_index_stream_size(second), 1036); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // Stream size should be: + // Size of Stream Header - 12 bytes + // Size of all Blocks - 4000 bytes + // Size of Index - 20 bytes + // Size of Stream Footer - 12 bytes + // Total: 4044 bytes + assert_uint_eq(lzma_index_stream_size(idx), 4044); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_total_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // First test empty lzma_index. + // Result should be 0 since no Blocks have been added. + assert_uint_eq(lzma_index_total_size(idx), 0); + + // Add a few Blocks and retest after each append + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 1000); + + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 2000); + + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(idx), 3000); + + // Create second lzma_index and append Blocks to it. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_uint_eq(lzma_index_total_size(second), 0); + + assert_lzma_ret(lzma_index_append(second, NULL, 100, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(second), 100); + + assert_lzma_ret(lzma_index_append(second, NULL, 100, 1), LZMA_OK); + assert_uint_eq(lzma_index_total_size(second), 200); + + // Concatenate the Streams together + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // The resulting total size should be the size of all Blocks + // from both Streams + assert_uint_eq(lzma_index_total_size(idx), 3200); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_file_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Should be the same as test_lzma_index_stream_size with + // only one Stream and no Stream Padding. + assert_uint_eq(lzma_index_file_size(idx), 32); + + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + + assert_uint_eq(lzma_index_file_size(idx), 3040); + + // Next add Stream Padding + assert_lzma_ret(lzma_index_stream_padding(idx, 1000), + LZMA_OK); + + assert_uint_eq(lzma_index_file_size(idx), 4040); + + // Create second lzma_index. + // Very similar to test_lzma_index_stream_size, but + // the values should include the headers of the second Stream. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 1), LZMA_OK); + assert_uint_eq(lzma_index_stream_size(second), 1036); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // .xz file size should be: + // Size of 2 Stream Headers - 12 * 2 bytes + // Size of all Blocks - 3000 + 1000 bytes + // Size of 2 Indexes - 16 + 12 bytes + // Size of Stream Padding - 1000 bytes + // Size of 2 Stream Footers - 12 * 2 bytes + // Total: 5076 bytes + assert_uint_eq(lzma_index_file_size(idx), 5076); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_uncompressed_size(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + // Empty lzma_index should have 0 uncompressed .xz file size. + assert_uint_eq(lzma_index_uncompressed_size(idx), 0); + + // Append a few small Blocks + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 100), LZMA_OK); + + assert_uint_eq(lzma_index_uncompressed_size(idx), 111); + + // Create another lzma_index + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Append a few small Blocks + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 2), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 20), LZMA_OK); + assert_lzma_ret(lzma_index_append(second, NULL, 1000, 200), LZMA_OK); + + assert_uint_eq(lzma_index_uncompressed_size(second), 222); + + // Concatenate second lzma_index to first + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + + // New uncompressed .xz file size should be the sum of the two Streams + assert_uint_eq(lzma_index_uncompressed_size(idx), 333); + + // Append one more Block to the lzma_index and ensure that + // it is properly updated + assert_lzma_ret(lzma_index_append(idx, NULL, 1000, 111), LZMA_OK); + assert_uint_eq(lzma_index_uncompressed_size(idx), 444); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_iter_init(void) +{ + // Testing basic init functionality. + // The init function should call rewind on the iterator. + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 1); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 2); + + lzma_index_iter_init(&iter, first); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, 3); +} + + +static void +test_lzma_index_iter_rewind(void) +{ + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + // Append 3 Blocks and iterate over each. This is to test + // the LZMA_INDEX_ITER_BLOCK mode. + for (uint32_t i = 0; i < 3; i++) { + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.number_in_file, i + 1); + } + + // Rewind back to the begining and iterate over the Blocks again + lzma_index_iter_rewind(&iter); + + // Should be able to re-iterate over the Blocks again. + for (uint32_t i = 0; i < 3; i++) { + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.number_in_file, i + 1); + } + + // Next concatenate two more lzma_indexes, iterate over them, + // rewind, and iterate over them again. This is to test + // the LZMA_INDEX_ITER_STREAM mode. + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + + assert_uint_eq(iter.stream.number, 3); + + lzma_index_iter_rewind(&iter); + + for (uint32_t i = 0; i < 3; i++) { + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_STREAM)); + assert_uint_eq(iter.stream.number, i + 1); + } + + lzma_index_end(first, NULL); +} + + +static void +test_lzma_index_iter_next(void) +{ + lzma_index *first = lzma_index_init(NULL); + assert_true(first != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, first); + + // First test bad mode values + for (uint32_t i = LZMA_INDEX_ITER_NONEMPTY_BLOCK + 1; i < 100; i++) + assert_true(lzma_index_iter_next(&iter, i)); + + // Test iterating over Blocks + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN * 2, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(first, NULL, + UNPADDED_SIZE_MIN * 3, 100), LZMA_OK); + + // For Blocks, need to verify: + // - number_in_file (overall Block number) + // - compressed_file_offset + // - uncompressed_file_offset + // - number_in_stream (Block number relative to current Stream) + // - compressed_stream_offset + // - uncompressed_stream_offset + // - uncompressed_size + // - unpadded_size + // - total_size + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 1); + + // Should start right after the Stream Header + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + assert_uint_eq(iter.block.number_in_stream, 1); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_stream_offset, 0); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN); + assert_uint_eq(iter.block.total_size, vli_ceil4(UNPADDED_SIZE_MIN)); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 2); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN)); + assert_uint_eq(iter.block.uncompressed_file_offset, 1); + assert_uint_eq(iter.block.number_in_stream, 2); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN)); + assert_uint_eq(iter.block.uncompressed_stream_offset, 1); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN * 2); + assert_uint_eq(iter.block.total_size, vli_ceil4(UNPADDED_SIZE_MIN * 2)); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify Block data stored correctly + assert_uint_eq(iter.block.number_in_file, 3); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2)); + assert_uint_eq(iter.block.uncompressed_file_offset, 11); + assert_uint_eq(iter.block.number_in_stream, 3); + assert_uint_eq(iter.block.compressed_stream_offset, + LZMA_STREAM_HEADER_SIZE + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2)); + assert_uint_eq(iter.block.uncompressed_stream_offset, 11); + assert_uint_eq(iter.block.unpadded_size, UNPADDED_SIZE_MIN * 3); + assert_uint_eq(iter.block.total_size, + vli_ceil4(UNPADDED_SIZE_MIN * 3)); + + // Only three Blocks were added, so this should return true + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + const lzma_vli second_stream_compressed_start = + LZMA_STREAM_HEADER_SIZE * 2 + + vli_ceil4(UNPADDED_SIZE_MIN) + + vli_ceil4(UNPADDED_SIZE_MIN * 2) + + vli_ceil4(UNPADDED_SIZE_MIN * 3) + + lzma_index_size(first); + const lzma_vli second_stream_uncompressed_start = 1 + 10 + 100; + + // Test iterating over Streams. + // The second Stream will have 0 Blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Set Stream Flags for Stream 2 + lzma_stream_flags flags = { + .version = 0, + .backward_size = LZMA_BACKWARD_SIZE_MIN, + .check = LZMA_CHECK_CRC32 + }; + + assert_lzma_ret(lzma_index_stream_flags(second, &flags), LZMA_OK); + + // The Second stream will have 8 bytes of Stream Padding + assert_lzma_ret(lzma_index_stream_padding(second, 8), LZMA_OK); + + const lzma_vli second_stream_index_size = lzma_index_size(second); + + // The third Stream will have 2 Blocks + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_append(third, NULL, 32, 20), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, 64, 40), LZMA_OK); + + const lzma_vli third_stream_index_size = lzma_index_size(third); + + assert_lzma_ret(lzma_index_cat(first, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(first, third, NULL), LZMA_OK); + + // For Streams, need to verify: + // - flags (Stream Flags) + // - number (Stream count) + // - block_count + // - compressed_offset + // - uncompressed_offset + // - compressed_size + // - uncompressed_size + // - padding (Stream Padding) + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Verify Stream + assert_uint_eq(iter.stream.flags->backward_size, + LZMA_BACKWARD_SIZE_MIN); + assert_uint_eq(iter.stream.flags->check, LZMA_CHECK_CRC32); + assert_uint_eq(iter.stream.number, 2); + assert_uint_eq(iter.stream.block_count, 0); + assert_uint_eq(iter.stream.compressed_offset, + second_stream_compressed_start); + assert_uint_eq(iter.stream.uncompressed_offset, + second_stream_uncompressed_start); + assert_uint_eq(iter.stream.compressed_size, + LZMA_STREAM_HEADER_SIZE * 2 + + second_stream_index_size); + assert_uint_eq(iter.stream.uncompressed_size, 0); + assert_uint_eq(iter.stream.padding, 8); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Verify Stream + const lzma_vli third_stream_compressed_start = + second_stream_compressed_start + + LZMA_STREAM_HEADER_SIZE * 2 + + 8 + // Stream padding + second_stream_index_size; + const lzma_vli third_stream_uncompressed_start = + second_stream_uncompressed_start; + + assert_uint_eq(iter.stream.number, 3); + assert_uint_eq(iter.stream.block_count, 2); + assert_uint_eq(iter.stream.compressed_offset, + third_stream_compressed_start); + assert_uint_eq(iter.stream.uncompressed_offset, + third_stream_uncompressed_start); + assert_uint_eq(iter.stream.compressed_size, + LZMA_STREAM_HEADER_SIZE * 2 + + 96 + // Total compressed size + third_stream_index_size); + assert_uint_eq(iter.stream.uncompressed_size, 60); + assert_uint_eq(iter.stream.padding, 0); + + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_STREAM)); + + // Even after a failing call to next with ITER_STREAM mode, + // should still be able to iterate over the 2 Blocks in + // Stream 3. + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Verify both Blocks + + // Next call to iterate Block should return true because the + // first Block can already be read from the LZMA_INDEX_ITER_STREAM + // call. + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + + // Rewind to test LZMA_INDEX_ITER_ANY + lzma_index_iter_rewind(&iter); + + // Iterate past the first three Blocks + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Iterate past the next Stream + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Iterate past the next Stream + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Last call should fail + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + // Rewind to test LZMA_INDEX_ITER_NONEMPTY_BLOCK + lzma_index_iter_rewind(&iter); + + // Iterate past the first three Blocks + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Skip past the next Stream which has no Blocks. + // We will get to the first Block of the third Stream. + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Iterate past the second (the last) Block in the third Stream + assert_false(lzma_index_iter_next(&iter, + LZMA_INDEX_ITER_NONEMPTY_BLOCK)); + + // Last call should fail since there is nothing left to iterate over. + assert_true(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)); + + lzma_index_end(first, NULL); +} + + +static void +test_lzma_index_iter_locate(void) +{ + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_index_iter iter; + lzma_index_iter_init(&iter, idx); + + // Cannot locate anything from an empty Index. + assert_true(lzma_index_iter_locate(&iter, 0)); + assert_true(lzma_index_iter_locate(&iter, 555)); + + // One empty Record: nothing is found since there's no uncompressed + // data. + assert_lzma_ret(lzma_index_append(idx, NULL, 16, 0), LZMA_OK); + assert_true(lzma_index_iter_locate(&iter, 0)); + + // Non-empty Record and we can find something. + assert_lzma_ret(lzma_index_append(idx, NULL, 32, 5), LZMA_OK); + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + // Still cannot find anything past the end. + assert_true(lzma_index_iter_locate(&iter, 5)); + + // Add the third Record. + assert_lzma_ret(lzma_index_append(idx, NULL, 40, 11), LZMA_OK); + + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_next(&iter, LZMA_INDEX_ITER_BLOCK)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_false(lzma_index_iter_locate(&iter, 2)); + assert_uint_eq(iter.block.total_size, 32); + assert_uint_eq(iter.block.uncompressed_size, 5); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_locate(&iter, 5)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_false(lzma_index_iter_locate(&iter, 5 + 11 - 1)); + assert_uint_eq(iter.block.total_size, 40); + assert_uint_eq(iter.block.uncompressed_size, 11); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 16 + 32); + assert_uint_eq(iter.block.uncompressed_file_offset, 5); + + assert_true(lzma_index_iter_locate(&iter, 5 + 11)); + assert_true(lzma_index_iter_locate(&iter, 5 + 15)); + + // Large Index + lzma_index_end(idx, NULL); + idx = lzma_index_init(NULL); + assert_true(idx != NULL); + lzma_index_iter_init(&iter, idx); + + for (uint32_t n = 4; n <= 4 * 5555; n += 4) + assert_lzma_ret(lzma_index_append(idx, NULL, n + 8, n), + LZMA_OK); + + assert_uint_eq(lzma_index_block_count(idx), 5555); + + // First Record + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, 4); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + assert_false(lzma_index_iter_locate(&iter, 3)); + assert_uint_eq(iter.block.total_size, 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, 4); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + // Second Record + assert_false(lzma_index_iter_locate(&iter, 4)); + assert_uint_eq(iter.block.total_size, 2 * 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, 2 * 4); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + 4 + 8); + assert_uint_eq(iter.block.uncompressed_file_offset, 4); + + // Last Record + assert_false(lzma_index_iter_locate( + &iter, lzma_index_uncompressed_size(idx) - 1)); + assert_uint_eq(iter.block.total_size, 4 * 5555 + 8); + assert_uint_eq(iter.block.uncompressed_size, 4 * 5555); + assert_uint_eq(iter.block.compressed_file_offset, + lzma_index_total_size(idx) + + LZMA_STREAM_HEADER_SIZE - 4 * 5555 - 8); + assert_uint_eq(iter.block.uncompressed_file_offset, + lzma_index_uncompressed_size(idx) - 4 * 5555); + + // Allocation chunk boundaries. See INDEX_GROUP_SIZE in + // liblzma/common/index.c. + const uint32_t group_multiple = 256 * 4; + const uint32_t radius = 8; + const uint32_t start = group_multiple - radius; + lzma_vli ubase = 0; + lzma_vli tbase = 0; + uint32_t n; + for (n = 1; n < start; ++n) { + ubase += n * 4; + tbase += n * 4 + 8; + } + + while (n < start + 2 * radius) { + assert_false(lzma_index_iter_locate(&iter, ubase + n * 4)); + + assert_uint_eq(iter.block.compressed_file_offset, + tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, + ubase + n * 4); + + tbase += n * 4 + 8; + ubase += n * 4; + ++n; + + assert_uint_eq(iter.block.total_size, n * 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, n * 4); + } + + // Do it also backwards. + while (n > start) { + assert_false(lzma_index_iter_locate( + &iter, ubase + (n - 1) * 4)); + + assert_uint_eq(iter.block.total_size, n * 4 + 8); + assert_uint_eq(iter.block.uncompressed_size, n * 4); + + --n; + tbase -= n * 4 + 8; + ubase -= n * 4; + + assert_uint_eq(iter.block.compressed_file_offset, + tbase + n * 4 + 8 + + LZMA_STREAM_HEADER_SIZE); + assert_uint_eq(iter.block.uncompressed_file_offset, + ubase + n * 4); + } + + // Test locating in concatenated Index. + lzma_index_end(idx, NULL); + idx = lzma_index_init(NULL); + assert_true(idx != NULL); + lzma_index_iter_init(&iter, idx); + for (n = 0; n < group_multiple; ++n) + assert_lzma_ret(lzma_index_append(idx, NULL, 8, 0), + LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, 16, 1), LZMA_OK); + assert_false(lzma_index_iter_locate(&iter, 0)); + assert_uint_eq(iter.block.total_size, 16); + assert_uint_eq(iter.block.uncompressed_size, 1); + assert_uint_eq(iter.block.compressed_file_offset, + LZMA_STREAM_HEADER_SIZE + group_multiple * 8); + assert_uint_eq(iter.block.uncompressed_file_offset, 0); + + lzma_index_end(idx, NULL); +} + + +static void +test_lzma_index_cat(void) +{ + // Most complex tests for this function are done in other tests. + // This will mostly test basic functionality. + + lzma_index *dest = lzma_index_init(NULL); + assert_true(dest != NULL); + + lzma_index *src = lzma_index_init(NULL); + assert_true(src != NULL); + + // First test NULL dest or src + assert_lzma_ret(lzma_index_cat(NULL, NULL, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_cat(dest, NULL, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_cat(NULL, src, NULL), LZMA_PROG_ERROR); + + // Check for uncompressed size overflow + assert_lzma_ret(lzma_index_append(dest, NULL, + (UNPADDED_SIZE_MAX / 2) + 1, 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(src, NULL, + (UNPADDED_SIZE_MAX / 2) + 1, 1), LZMA_OK); + assert_lzma_ret(lzma_index_cat(dest, src, NULL), LZMA_DATA_ERROR); + + // Check for compressed size overflow + dest = lzma_index_init(NULL); + assert_true(dest != NULL); + + src = lzma_index_init(NULL); + assert_true(src != NULL); + + assert_lzma_ret(lzma_index_append(dest, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + assert_lzma_ret(lzma_index_append(src, NULL, + UNPADDED_SIZE_MIN, LZMA_VLI_MAX - 1), LZMA_OK); + assert_lzma_ret(lzma_index_cat(dest, src, NULL), LZMA_DATA_ERROR); + + lzma_index_end(dest, NULL); + lzma_index_end(src, NULL); +} + + +// Helper function for test_lzma_index_dup(). static bool -is_equal(const lzma_index *a, const lzma_index *b) +index_is_equal(const lzma_index *a, const lzma_index *b) { // Compare only the Stream and Block sizes and offsets. lzma_index_iter ra, rb; @@ -80,8 +1186,11 @@ is_equal(const lzma_index *a, const lzma_index *b) while (true) { bool reta = lzma_index_iter_next(&ra, LZMA_INDEX_ITER_ANY); bool retb = lzma_index_iter_next(&rb, LZMA_INDEX_ITER_ANY); + + // If both iterators finish at the same time, then the Indexes + // are identical. if (reta) - return !(reta ^ retb); + return retb; if (ra.stream.number != rb.stream.number || ra.stream.block_count @@ -123,560 +1232,6 @@ is_equal(const lzma_index *a, const lzma_index *b) } -static void -test_equal(void) -{ - lzma_index *a = create_empty(); - lzma_index *b = create_small(); - lzma_index *c = create_big(); - expect(a && b && c); - - expect(is_equal(a, a)); - expect(is_equal(b, b)); - expect(is_equal(c, c)); - - expect(!is_equal(a, b)); - expect(!is_equal(a, c)); - expect(!is_equal(b, c)); - - lzma_index_end(a, NULL); - lzma_index_end(b, NULL); - lzma_index_end(c, NULL); -} - - -static void -test_overflow(void) -{ - // Integer overflow tests - lzma_index *i = create_empty(); - - expect(lzma_index_append(i, NULL, LZMA_VLI_MAX - 5, 1234) - == LZMA_DATA_ERROR); - - // TODO - - lzma_index_end(i, NULL); -} - - -static void -test_copy(const lzma_index *i) -{ - lzma_index *d = lzma_index_dup(i, NULL); - expect(d != NULL); - expect(is_equal(i, d)); - lzma_index_end(d, NULL); -} - - -static void -test_read(lzma_index *i) -{ - lzma_index_iter r; - lzma_index_iter_init(&r, i); - - // Try twice so we see that rewinding works. - for (size_t j = 0; j < 2; ++j) { - lzma_vli total_size = 0; - lzma_vli uncompressed_size = 0; - lzma_vli stream_offset = LZMA_STREAM_HEADER_SIZE; - lzma_vli uncompressed_offset = 0; - uint32_t count = 0; - - while (!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)) { - ++count; - - total_size += r.block.total_size; - uncompressed_size += r.block.uncompressed_size; - - expect(r.block.compressed_file_offset - == stream_offset); - expect(r.block.uncompressed_file_offset - == uncompressed_offset); - - stream_offset += r.block.total_size; - uncompressed_offset += r.block.uncompressed_size; - } - - expect(lzma_index_total_size(i) == total_size); - expect(lzma_index_uncompressed_size(i) == uncompressed_size); - expect(lzma_index_block_count(i) == count); - - lzma_index_iter_rewind(&r); - } -} - - -static void -test_code(lzma_index *i) -{ -#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) - const size_t alloc_size = 128 * 1024; - uint8_t *buf = malloc(alloc_size); - expect(buf != NULL); - - // Encode - lzma_stream strm = LZMA_STREAM_INIT; - expect(lzma_index_encoder(&strm, i) == LZMA_OK); - const lzma_vli index_size = lzma_index_size(i); - succeed(coder_loop(&strm, NULL, 0, buf, index_size, - LZMA_STREAM_END, LZMA_RUN)); - - // Decode - lzma_index *d; - expect(lzma_index_decoder(&strm, &d, MEMLIMIT) == LZMA_OK); - expect(d == NULL); - succeed(decoder_loop(&strm, buf, index_size)); - - expect(is_equal(i, d)); - - lzma_index_end(d, NULL); - lzma_end(&strm); - - // Decode with hashing - lzma_index_hash *h = lzma_index_hash_init(NULL, NULL); - expect(h != NULL); - lzma_index_iter r; - lzma_index_iter_init(&r, i); - while (!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)) - expect(lzma_index_hash_append(h, r.block.unpadded_size, - r.block.uncompressed_size) == LZMA_OK); - size_t pos = 0; - while (pos < index_size - 1) - expect(lzma_index_hash_decode(h, buf, &pos, pos + 1) - == LZMA_OK); - expect(lzma_index_hash_decode(h, buf, &pos, pos + 1) - == LZMA_STREAM_END); - - lzma_index_hash_end(h, NULL); - - // Encode buffer - size_t buf_pos = 1; - expect(lzma_index_buffer_encode(i, buf, &buf_pos, index_size) - == LZMA_BUF_ERROR); - expect(buf_pos == 1); - - succeed(lzma_index_buffer_encode(i, buf, &buf_pos, index_size + 1)); - expect(buf_pos == index_size + 1); - - // Decode buffer - buf_pos = 1; - uint64_t memlimit = MEMLIMIT; - expect(lzma_index_buffer_decode(&d, &memlimit, NULL, buf, &buf_pos, - index_size) == LZMA_DATA_ERROR); - expect(buf_pos == 1); - expect(d == NULL); - - succeed(lzma_index_buffer_decode(&d, &memlimit, NULL, buf, &buf_pos, - index_size + 1)); - expect(buf_pos == index_size + 1); - expect(is_equal(i, d)); - - lzma_index_end(d, NULL); - - free(buf); -#else - (void)i; -#endif -} - - -static void -test_many(lzma_index *i) -{ - test_copy(i); - test_read(i); - test_code(i); -} - - -static void -test_cat(void) -{ - lzma_index *a, *b, *c, *d, *e, *f; - lzma_index_iter r; - - // Empty Indexes - a = create_empty(); - b = create_empty(); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_block_count(a) == 0); - expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); - expect(lzma_index_file_size(a) - == 2 * (2 * LZMA_STREAM_HEADER_SIZE + 8)); - lzma_index_iter_init(&r, a); - expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); - - b = create_empty(); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_block_count(a) == 0); - expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); - expect(lzma_index_file_size(a) - == 3 * (2 * LZMA_STREAM_HEADER_SIZE + 8)); - - b = create_empty(); - c = create_empty(); - expect(lzma_index_stream_padding(b, 4) == LZMA_OK); - expect(lzma_index_cat(b, c, NULL) == LZMA_OK); - expect(lzma_index_block_count(b) == 0); - expect(lzma_index_stream_size(b) == 2 * LZMA_STREAM_HEADER_SIZE + 8); - expect(lzma_index_file_size(b) - == 2 * (2 * LZMA_STREAM_HEADER_SIZE + 8) + 4); - - expect(lzma_index_stream_padding(a, 8) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_block_count(a) == 0); - expect(lzma_index_stream_size(a) == 2 * LZMA_STREAM_HEADER_SIZE + 8); - expect(lzma_index_file_size(a) - == 5 * (2 * LZMA_STREAM_HEADER_SIZE + 8) + 4 + 8); - - expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); - lzma_index_iter_rewind(&r); - expect(lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); - lzma_index_end(a, NULL); - - // Small Indexes - a = create_small(); - lzma_vli stream_size = lzma_index_stream_size(a); - lzma_index_iter_init(&r, a); - for (int i = SMALL_COUNT; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - b = create_small(); - expect(lzma_index_stream_padding(a, 4) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_file_size(a) == stream_size * 2 + 4); - expect(lzma_index_stream_size(a) > stream_size); - expect(lzma_index_stream_size(a) < stream_size * 2); - for (int i = SMALL_COUNT; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - lzma_index_iter_rewind(&r); - for (int i = SMALL_COUNT * 2; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - b = create_small(); - c = create_small(); - expect(lzma_index_stream_padding(b, 8) == LZMA_OK); - expect(lzma_index_cat(b, c, NULL) == LZMA_OK); - expect(lzma_index_stream_padding(a, 12) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_file_size(a) == stream_size * 4 + 4 + 8 + 12); - - expect(lzma_index_block_count(a) == SMALL_COUNT * 4); - for (int i = SMALL_COUNT * 2; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - lzma_index_iter_rewind(&r); - for (int i = SMALL_COUNT * 4; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - lzma_index_end(a, NULL); - - // Mix of empty and small - a = create_empty(); - b = create_small(); - expect(lzma_index_stream_padding(a, 4) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - lzma_index_iter_init(&r, a); - for (int i = SMALL_COUNT; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - lzma_index_end(a, NULL); - - // Big Indexes - a = create_big(); - stream_size = lzma_index_stream_size(a); - b = create_big(); - expect(lzma_index_stream_padding(a, 4) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_file_size(a) == stream_size * 2 + 4); - expect(lzma_index_stream_size(a) > stream_size); - expect(lzma_index_stream_size(a) < stream_size * 2); - - b = create_big(); - c = create_big(); - expect(lzma_index_stream_padding(b, 8) == LZMA_OK); - expect(lzma_index_cat(b, c, NULL) == LZMA_OK); - expect(lzma_index_stream_padding(a, 12) == LZMA_OK); - expect(lzma_index_cat(a, b, NULL) == LZMA_OK); - expect(lzma_index_file_size(a) == stream_size * 4 + 4 + 8 + 12); - - lzma_index_iter_init(&r, a); - for (int i = BIG_COUNT * 4; i >= 0; --i) - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK) - ^ (i == 0)); - - lzma_index_end(a, NULL); - - // Test for the bug fix 3d5a99ca373a4e86faf671226ca6487febb9eeac. - // lzma_index_checks would previously only return the checks - // for the last stream that was concatenated to the index. - d = create_small(); - e = create_small(); - f = create_small(); - - lzma_stream_flags crc32_flags = { - .backward_size = LZMA_BACKWARD_SIZE_MIN, - .check = LZMA_CHECK_CRC32 - }; - expect(lzma_index_stream_flags(d, &crc32_flags) == LZMA_OK); - - lzma_stream_flags crc64_flags = { - .backward_size = LZMA_BACKWARD_SIZE_MIN, - .check = LZMA_CHECK_CRC64 - }; - expect(lzma_index_stream_flags(e, &crc64_flags) == LZMA_OK); - - lzma_stream_flags sha256_flags = { - .backward_size = LZMA_BACKWARD_SIZE_MIN, - .check = LZMA_CHECK_SHA256 - }; - expect(lzma_index_stream_flags(f, &sha256_flags) == LZMA_OK); - - expect(lzma_index_checks(d) == (1U << LZMA_CHECK_CRC32)); - expect(lzma_index_checks(e) == (1U << LZMA_CHECK_CRC64)); - expect(lzma_index_checks(f) == (1U << LZMA_CHECK_SHA256)); - - expect(lzma_index_cat(d, e, NULL) == LZMA_OK); - expect(lzma_index_checks(d) == ((1U << LZMA_CHECK_CRC32) | - (1U << LZMA_CHECK_CRC64))); - - expect(lzma_index_cat(d, f, NULL) == LZMA_OK); - expect(lzma_index_checks(d) == ((1U << LZMA_CHECK_CRC32) | - (1U << LZMA_CHECK_CRC64) | - (1U << LZMA_CHECK_SHA256))); - - lzma_index_end(d, NULL); - -} - - -static void -test_locate(void) -{ - lzma_index *i = lzma_index_init(NULL); - expect(i != NULL); - lzma_index_iter r; - lzma_index_iter_init(&r, i); - - // Cannot locate anything from an empty Index. - expect(lzma_index_iter_locate(&r, 0)); - expect(lzma_index_iter_locate(&r, 555)); - - // One empty Record: nothing is found since there's no uncompressed - // data. - expect(lzma_index_append(i, NULL, 16, 0) == LZMA_OK); - expect(lzma_index_iter_locate(&r, 0)); - - // Non-empty Record and we can find something. - expect(lzma_index_append(i, NULL, 32, 5) == LZMA_OK); - expect(!lzma_index_iter_locate(&r, 0)); - expect(r.block.total_size == 32); - expect(r.block.uncompressed_size == 5); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16); - expect(r.block.uncompressed_file_offset == 0); - - // Still cannot find anything past the end. - expect(lzma_index_iter_locate(&r, 5)); - - // Add the third Record. - expect(lzma_index_append(i, NULL, 40, 11) == LZMA_OK); - - expect(!lzma_index_iter_locate(&r, 0)); - expect(r.block.total_size == 32); - expect(r.block.uncompressed_size == 5); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16); - expect(r.block.uncompressed_file_offset == 0); - - expect(!lzma_index_iter_next(&r, LZMA_INDEX_ITER_BLOCK)); - expect(r.block.total_size == 40); - expect(r.block.uncompressed_size == 11); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16 + 32); - expect(r.block.uncompressed_file_offset == 5); - - expect(!lzma_index_iter_locate(&r, 2)); - expect(r.block.total_size == 32); - expect(r.block.uncompressed_size == 5); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16); - expect(r.block.uncompressed_file_offset == 0); - - expect(!lzma_index_iter_locate(&r, 5)); - expect(r.block.total_size == 40); - expect(r.block.uncompressed_size == 11); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16 + 32); - expect(r.block.uncompressed_file_offset == 5); - - expect(!lzma_index_iter_locate(&r, 5 + 11 - 1)); - expect(r.block.total_size == 40); - expect(r.block.uncompressed_size == 11); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 16 + 32); - expect(r.block.uncompressed_file_offset == 5); - - expect(lzma_index_iter_locate(&r, 5 + 11)); - expect(lzma_index_iter_locate(&r, 5 + 15)); - - // Large Index - lzma_index_end(i, NULL); - i = lzma_index_init(NULL); - expect(i != NULL); - lzma_index_iter_init(&r, i); - - for (size_t n = 4; n <= 4 * 5555; n += 4) - expect(lzma_index_append(i, NULL, n + 8, n) == LZMA_OK); - - expect(lzma_index_block_count(i) == 5555); - - // First Record - expect(!lzma_index_iter_locate(&r, 0)); - expect(r.block.total_size == 4 + 8); - expect(r.block.uncompressed_size == 4); - expect(r.block.compressed_file_offset == LZMA_STREAM_HEADER_SIZE); - expect(r.block.uncompressed_file_offset == 0); - - expect(!lzma_index_iter_locate(&r, 3)); - expect(r.block.total_size == 4 + 8); - expect(r.block.uncompressed_size == 4); - expect(r.block.compressed_file_offset == LZMA_STREAM_HEADER_SIZE); - expect(r.block.uncompressed_file_offset == 0); - - // Second Record - expect(!lzma_index_iter_locate(&r, 4)); - expect(r.block.total_size == 2 * 4 + 8); - expect(r.block.uncompressed_size == 2 * 4); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + 4 + 8); - expect(r.block.uncompressed_file_offset == 4); - - // Last Record - expect(!lzma_index_iter_locate( - &r, lzma_index_uncompressed_size(i) - 1)); - expect(r.block.total_size == 4 * 5555 + 8); - expect(r.block.uncompressed_size == 4 * 5555); - expect(r.block.compressed_file_offset == lzma_index_total_size(i) - + LZMA_STREAM_HEADER_SIZE - 4 * 5555 - 8); - expect(r.block.uncompressed_file_offset - == lzma_index_uncompressed_size(i) - 4 * 5555); - - // Allocation chunk boundaries. See INDEX_GROUP_SIZE in - // liblzma/common/index.c. - const size_t group_multiple = 256 * 4; - const size_t radius = 8; - const size_t start = group_multiple - radius; - lzma_vli ubase = 0; - lzma_vli tbase = 0; - size_t n; - for (n = 1; n < start; ++n) { - ubase += n * 4; - tbase += n * 4 + 8; - } - - while (n < start + 2 * radius) { - expect(!lzma_index_iter_locate(&r, ubase + n * 4)); - - expect(r.block.compressed_file_offset == tbase + n * 4 + 8 - + LZMA_STREAM_HEADER_SIZE); - expect(r.block.uncompressed_file_offset == ubase + n * 4); - - tbase += n * 4 + 8; - ubase += n * 4; - ++n; - - expect(r.block.total_size == n * 4 + 8); - expect(r.block.uncompressed_size == n * 4); - } - - // Do it also backwards. - while (n > start) { - expect(!lzma_index_iter_locate(&r, ubase + (n - 1) * 4)); - - expect(r.block.total_size == n * 4 + 8); - expect(r.block.uncompressed_size == n * 4); - - --n; - tbase -= n * 4 + 8; - ubase -= n * 4; - - expect(r.block.compressed_file_offset == tbase + n * 4 + 8 - + LZMA_STREAM_HEADER_SIZE); - expect(r.block.uncompressed_file_offset == ubase + n * 4); - } - - // Test locating in concatenated Index. - lzma_index_end(i, NULL); - i = lzma_index_init(NULL); - expect(i != NULL); - lzma_index_iter_init(&r, i); - for (n = 0; n < group_multiple; ++n) - expect(lzma_index_append(i, NULL, 8, 0) == LZMA_OK); - expect(lzma_index_append(i, NULL, 16, 1) == LZMA_OK); - expect(!lzma_index_iter_locate(&r, 0)); - expect(r.block.total_size == 16); - expect(r.block.uncompressed_size == 1); - expect(r.block.compressed_file_offset - == LZMA_STREAM_HEADER_SIZE + group_multiple * 8); - expect(r.block.uncompressed_file_offset == 0); - - lzma_index_end(i, NULL); -} - - -static void -test_corrupt(void) -{ -#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) - const size_t alloc_size = 128 * 1024; - uint8_t *buf = malloc(alloc_size); - expect(buf != NULL); - lzma_stream strm = LZMA_STREAM_INIT; - - lzma_index *i = create_empty(); - expect(lzma_index_append(i, NULL, 0, 1) == LZMA_PROG_ERROR); - lzma_index_end(i, NULL); - - // Create a valid Index and corrupt it in different ways. - i = create_small(); - expect(lzma_index_encoder(&strm, i) == LZMA_OK); - succeed(coder_loop(&strm, NULL, 0, buf, 20, - LZMA_STREAM_END, LZMA_RUN)); - lzma_index_end(i, NULL); - - // Wrong Index Indicator - buf[0] ^= 1; - expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); - succeed(decoder_loop_ret(&strm, buf, 1, LZMA_DATA_ERROR)); - buf[0] ^= 1; - - // Wrong Number of Records and thus CRC32 fails. - --buf[1]; - expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); - succeed(decoder_loop_ret(&strm, buf, 10, LZMA_DATA_ERROR)); - ++buf[1]; - - // Padding not NULs - buf[15] ^= 1; - expect(lzma_index_decoder(&strm, &i, MEMLIMIT) == LZMA_OK); - succeed(decoder_loop_ret(&strm, buf, 16, LZMA_DATA_ERROR)); - - lzma_end(&strm); - free(buf); -#endif -} - - // Allocator that succeeds for the first two allocation but fails the rest. static void * my_alloc(void *opaque, size_t a, size_t b) @@ -690,47 +1245,440 @@ my_alloc(void *opaque, size_t a, size_t b) return malloc(a * b); } -static const lzma_allocator my_allocator = { &my_alloc, NULL, NULL }; +static const lzma_allocator test_index_dup_alloc = { &my_alloc, NULL, NULL }; -int -main(void) +static void +test_lzma_index_dup(void) { - test_equal(); - - test_overflow(); - - lzma_index *i = create_empty(); - test_many(i); - lzma_index_end(i, NULL); - - i = create_small(); - test_many(i); - lzma_index_end(i, NULL); - - i = create_big(); - test_many(i); - lzma_index_end(i, NULL); - - test_cat(); - - test_locate(); - - test_corrupt(); + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); // Test for the bug fix 21515d79d778b8730a434f151b07202d52a04611: // liblzma: Fix lzma_index_dup() for empty Streams. - i = create_empty(); - expect(lzma_index_stream_padding(i, 4) == LZMA_OK); - test_copy(i); - lzma_index_end(i, NULL); + assert_lzma_ret(lzma_index_stream_padding(idx, 4), LZMA_OK); + lzma_index *copy = lzma_index_dup(idx, NULL); + assert_true(copy != NULL); + assert_true(index_is_equal(idx, copy)); + lzma_index_end(copy, NULL); // Test for the bug fix 3bf857edfef51374f6f3fffae3d817f57d3264a0: // liblzma: Fix a memory leak in error path of lzma_index_dup(). // Use Valgrind to see that there are no leaks. - i = create_small(); - expect(lzma_index_dup(i, &my_allocator) == NULL); - lzma_index_end(i, NULL); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); - return 0; + assert_true(lzma_index_dup(idx, &test_index_dup_alloc) == NULL); + + // Test a few streams and blocks + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + assert_lzma_ret(lzma_index_stream_padding(second, 16), LZMA_OK); + + lzma_index *third = lzma_index_init(NULL); + assert_true(third != NULL); + + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 10, 40), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 20, 400), LZMA_OK); + assert_lzma_ret(lzma_index_append(third, NULL, + UNPADDED_SIZE_MIN * 30, 4000), LZMA_OK); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + assert_lzma_ret(lzma_index_cat(idx, third, NULL), LZMA_OK); + + copy = lzma_index_dup(idx, NULL); + assert_true(copy != NULL); + assert_true(index_is_equal(idx, copy)); + + lzma_index_end(idx, NULL); +} + +#if defined(HAVE_ENCODERS) && defined(HAVE_DECODERS) +static void +verify_index_buffer(const lzma_index *idx, const uint8_t *buffer, + const size_t buffer_size) +{ + lzma_index_iter iter; + lzma_index_iter_init(&iter, idx); + + size_t buffer_pos = 0; + + // Verify Index Indicator + assert_uint_eq(buffer[buffer_pos++], 0); + + // Get Number of Records + lzma_vli number_of_records = 0; + lzma_vli block_count = 0; + assert_lzma_ret(lzma_vli_decode(&number_of_records, NULL, buffer, + &buffer_pos, buffer_size), LZMA_OK); + + while (!lzma_index_iter_next(&iter, LZMA_INDEX_ITER_ANY)) { + // Verify each Record (Unpadded Size, then Uncompressed Size). + // Verify Unpadded Size. + lzma_vli unpadded_size, uncompressed_size; + assert_lzma_ret(lzma_vli_decode(&unpadded_size, + NULL, buffer, &buffer_pos, + buffer_size), LZMA_OK); + assert_uint_eq(unpadded_size, + iter.block.unpadded_size); + + // Verify Uncompressed Size + assert_lzma_ret(lzma_vli_decode(&uncompressed_size, + NULL, buffer, &buffer_pos, + buffer_size), LZMA_OK); + assert_uint_eq(uncompressed_size, + iter.block.uncompressed_size); + + block_count++; + } + + // Verify Number of Records + assert_uint_eq(number_of_records, block_count); + + // Verify Index Padding + for (; buffer_pos % 4 != 0; buffer_pos++) + assert_uint_eq(buffer[buffer_pos], 0); + + // Verify CRC32 + uint32_t crc32 = lzma_crc32(buffer, buffer_pos, 0); + assert_uint_eq(read32le(buffer + buffer_pos), crc32); +} + + +// In a few places the Index size is needed as a size_t but lzma_index_size() +// returns lzma_vli. +static size_t +get_index_size(const lzma_index *idx) +{ + const lzma_vli size = lzma_index_size(idx); + assert_uint(size, <, SIZE_MAX); + return (size_t)size; +} +#endif + + +static void +test_lzma_index_encoder(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + lzma_stream strm = LZMA_STREAM_INIT; + + // First do basic NULL checks + assert_lzma_ret(lzma_index_encoder(NULL, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_encoder(&strm, NULL), LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_encoder(NULL, idx), LZMA_PROG_ERROR); + + // Append three small Blocks + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); + + // Encode this lzma_index into a buffer + size_t buffer_size = get_index_size(idx); + uint8_t *buffer = tuktest_malloc(buffer_size); + + assert_lzma_ret(lzma_index_encoder(&strm, idx), LZMA_OK); + + strm.avail_out = buffer_size; + strm.next_out = buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint_eq(strm.avail_out, 0); + + lzma_end(&strm); + + verify_index_buffer(idx, buffer, buffer_size); + + // Test with multiple Streams concatenated into 1 Index + lzma_index *second = lzma_index_init(NULL); + assert_true(second != NULL); + + // Include 1 Block + assert_lzma_ret(lzma_index_append(second, NULL, + UNPADDED_SIZE_MIN * 4, 20), LZMA_OK); + + // Include Stream Padding + assert_lzma_ret(lzma_index_stream_padding(second, 16), LZMA_OK); + + assert_lzma_ret(lzma_index_cat(idx, second, NULL), LZMA_OK); + buffer_size = get_index_size(idx); + buffer = tuktest_malloc(buffer_size); + assert_lzma_ret(lzma_index_encoder(&strm, idx), LZMA_OK); + + strm.avail_out = buffer_size; + strm.next_out = buffer; + + assert_lzma_ret(lzma_code(&strm, LZMA_FINISH), LZMA_STREAM_END); + assert_uint_eq(strm.avail_out, 0); + + verify_index_buffer(idx, buffer, buffer_size); + + lzma_end(&strm); +#endif +} + +static void +generate_index_decode_buffer(void) +{ +#ifdef HAVE_ENCODERS + decode_test_index = lzma_index_init(NULL); + if (decode_test_index == NULL) + return; + + // Add 4 Blocks + for (uint32_t i = 1; i < 5; i++) + if (lzma_index_append(decode_test_index, NULL, + 0x1000 * i, 0x100 * i) != LZMA_OK) + return; + + size_t size = lzma_index_size(decode_test_index); + decode_buffer = tuktest_malloc(size); + + if (lzma_index_buffer_encode(decode_test_index, + decode_buffer, &decode_buffer_size, size) != LZMA_OK) + decode_buffer_size = 0; +#endif +} + + +#ifdef HAVE_DECODERS +static void +decode_index(const uint8_t *buffer, const size_t size, lzma_stream *strm, + lzma_ret expected_error) +{ + strm->avail_in = size; + strm->next_in = buffer; + assert_lzma_ret(lzma_code(strm, LZMA_FINISH), expected_error); +} +#endif + + +static void +test_lzma_index_decoder(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + if (decode_buffer_size == 0) + assert_skip("Could not initialize decode test buffer"); + + lzma_stream strm = LZMA_STREAM_INIT; + + assert_lzma_ret(lzma_index_decoder(NULL, NULL, MEMLIMIT), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_decoder(&strm, NULL, MEMLIMIT), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_decoder(NULL, &decode_test_index, + MEMLIMIT), LZMA_PROG_ERROR); + + // Do actual decode + lzma_index *idx; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + + decode_index(decode_buffer, decode_buffer_size, &strm, + LZMA_STREAM_END); + + // Compare results with expected + assert_true(index_is_equal(decode_test_index, idx)); + + lzma_index_end(idx, NULL); + + // Test again with too low memory limit + assert_lzma_ret(lzma_index_decoder(&strm, &idx, 0), LZMA_OK); + + decode_index(decode_buffer, decode_buffer_size, &strm, + LZMA_MEMLIMIT_ERROR); + + uint8_t *corrupt_buffer = tuktest_malloc(decode_buffer_size); + memcpy(corrupt_buffer, decode_buffer, decode_buffer_size); + + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + + // First corrupt the Index Indicator + corrupt_buffer[0] ^= 1; + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[0] ^= 1; + + // Corrupt something in the middle of Index + corrupt_buffer[decode_buffer_size / 2] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size / 2] ^= 1; + + // Corrupt CRC32 + corrupt_buffer[decode_buffer_size - 1] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size - 1] ^= 1; + + // Corrupt Index Padding by setting it to non-zero + corrupt_buffer[decode_buffer_size - 5] ^= 1; + assert_lzma_ret(lzma_index_decoder(&strm, &idx, MEMLIMIT), + LZMA_OK); + decode_index(corrupt_buffer, decode_buffer_size, &strm, + LZMA_DATA_ERROR); + corrupt_buffer[decode_buffer_size - 1] ^= 1; + + lzma_end(&strm); +#endif +} + + +static void +test_lzma_index_buffer_encode(void) +{ +#if !defined(HAVE_ENCODERS) || !defined(HAVE_DECODERS) + assert_skip("Encoder or decoder support disabled"); +#else + // More simple test than test_lzma_index_encoder() because + // currently lzma_index_buffer_encode() is mostly a wrapper + // around lzma_index_encoder() anyway. + lzma_index *idx = lzma_index_init(NULL); + assert_true(idx != NULL); + + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN, 10), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 2, 100), LZMA_OK); + assert_lzma_ret(lzma_index_append(idx, NULL, + UNPADDED_SIZE_MIN * 3, 1000), LZMA_OK); + + size_t buffer_size = get_index_size(idx); + uint8_t *buffer = tuktest_malloc(buffer_size); + size_t out_pos = 1; + + // First test bad arguments + assert_lzma_ret(lzma_index_buffer_encode(NULL, NULL, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, NULL, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, NULL, 0), + LZMA_PROG_ERROR); + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + 0), LZMA_PROG_ERROR); + out_pos = 0; + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + 1), LZMA_BUF_ERROR); + + // Do encoding + assert_lzma_ret(lzma_index_buffer_encode(idx, buffer, &out_pos, + buffer_size), LZMA_OK); + assert_uint_eq(out_pos, buffer_size); + + // Validate results + verify_index_buffer(idx, buffer, buffer_size); +#endif +} + + +static void +test_lzma_index_buffer_decode(void) +{ +#ifndef HAVE_DECODERS + assert_skip("Decoder support disabled"); +#else + if (decode_buffer_size == 0) + assert_skip("Could not initialize decode test buffer"); + + // Simple test since test_lzma_index_decoder() covers most of the + // lzma_index_buffer_decode() code anyway. + + // First test NULL checks + assert_lzma_ret(lzma_index_buffer_decode(NULL, NULL, NULL, NULL, + NULL, 0), LZMA_PROG_ERROR); + + lzma_index *idx; + uint64_t memlimit = MEMLIMIT; + size_t in_pos = 0; + + assert_lzma_ret(lzma_index_buffer_decode(&idx, NULL, NULL, NULL, + NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + NULL, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, NULL, 0), LZMA_PROG_ERROR); + + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, 0), LZMA_DATA_ERROR); + + in_pos = 1; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, 0), LZMA_PROG_ERROR); + in_pos = 0; + + // Test expected successful decode + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size), LZMA_OK); + + assert_true(index_is_equal(decode_test_index, idx)); + + // Test too small memlimit + in_pos = 0; + memlimit = 1; + assert_lzma_ret(lzma_index_buffer_decode(&idx, &memlimit, NULL, + decode_buffer, &in_pos, decode_buffer_size), + LZMA_MEMLIMIT_ERROR); + assert_uint(memlimit, >, 1); + assert_uint(memlimit, <, MEMLIMIT); +#endif +} + + +extern int +main(int argc, char **argv) +{ + tuktest_start(argc, argv); + generate_index_decode_buffer(); + tuktest_run(test_lzma_index_memusage); + tuktest_run(test_lzma_index_memused); + tuktest_run(test_lzma_index_append); + tuktest_run(test_lzma_index_stream_flags); + tuktest_run(test_lzma_index_checks); + tuktest_run(test_lzma_index_stream_padding); + tuktest_run(test_lzma_index_stream_count); + tuktest_run(test_lzma_index_block_count); + tuktest_run(test_lzma_index_size); + tuktest_run(test_lzma_index_stream_size); + tuktest_run(test_lzma_index_total_size); + tuktest_run(test_lzma_index_file_size); + tuktest_run(test_lzma_index_uncompressed_size); + tuktest_run(test_lzma_index_iter_init); + tuktest_run(test_lzma_index_iter_rewind); + tuktest_run(test_lzma_index_iter_next); + tuktest_run(test_lzma_index_iter_locate); + tuktest_run(test_lzma_index_cat); + tuktest_run(test_lzma_index_dup); + tuktest_run(test_lzma_index_encoder); + tuktest_run(test_lzma_index_decoder); + tuktest_run(test_lzma_index_buffer_encode); + tuktest_run(test_lzma_index_buffer_decode); + lzma_index_end(decode_test_index, NULL); + return tuktest_end(); }