/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include /* Exported by generated file */ struct aws_huffman_symbol_coder *test_get_coder(void); static struct huffman_test_code_point s_code_points[] = { #include "test_huffman_static_table.def" }; enum { NUM_CODE_POINTS = sizeof(s_code_points) / sizeof(s_code_points[0]) }; /* Useful data for testing */ static const char s_url_string[] = "www.example.com"; enum { URL_STRING_LEN = sizeof(s_url_string) - 1 }; static uint8_t s_encoded_url[] = {0x9e, 0x79, 0xeb, 0x9b, 0x04, 0xb3, 0x5a, 0x94, 0xd5, 0xe0, 0x4c, 0xdf}; enum { ENCODED_URL_LEN = sizeof(s_encoded_url) }; static const char s_all_codes[] = " !\"#$%&'()*+,-./" "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[" "\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; enum { ALL_CODES_LEN = sizeof(s_all_codes) - 1 }; static uint8_t s_encoded_codes[] = { 0x26, 0x9b, 0xa7, 0x69, 0xfa, 0x86, 0xa3, 0xa9, 0x56, 0xd4, 0xf5, 0x4d, 0x57, 0x56, 0xb9, 0xc4, 0x57, 0xd5, 0xf5, 0x8d, 0x67, 0x5a, 0xd6, 0xf5, 0xcd, 0x77, 0x5e, 0xd7, 0xf6, 0x0d, 0x87, 0x62, 0xd8, 0xf6, 0x4d, 0x97, 0x66, 0xba, 0xd9, 0xf6, 0x8b, 0xbc, 0x4e, 0x2b, 0x17, 0x8c, 0xc6, 0xe3, 0xaf, 0x36, 0x9d, 0xab, 0x1f, 0x90, 0xda, 0xf6, 0xcc, 0x8e, 0xdb, 0xb7, 0x6d, 0xf7, 0xbb, 0x86, 0x4a, 0xfb, 0x71, 0xc9, 0xee, 0x5b, 0x9e, 0xe9, 0xba, 0xee, 0xdb, 0xbe, 0xf0, 0x5b, 0x10, 0x42, 0x68, 0xac, 0xc6, 0x7b, 0xf9, 0x25, 0x99, 0x09, 0xb5, 0x94, 0x52, 0xd8, 0xdc, 0x09, 0xf0, 0x68, 0xde, 0x77, 0xad, 0xef, 0x7c, 0xdf, 0x7f}; enum { ENCODED_CODES_LEN = sizeof(s_encoded_codes) }; static const size_t s_step_sizes[] = {1, 2, 4, 8, 16, 32, 64, 128}; enum { NUM_STEP_SIZES = sizeof(s_step_sizes) / sizeof(s_step_sizes[0]) }; AWS_TEST_CASE(huffman_symbol_encoder, test_huffman_symbol_encoder) static int test_huffman_symbol_encoder(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding each character */ struct aws_huffman_symbol_coder *coder = test_get_coder(); for (size_t i = 0; i < NUM_CODE_POINTS; ++i) { struct huffman_test_code_point *value = &s_code_points[i]; struct aws_huffman_code code = coder->encode(value->symbol, NULL); ASSERT_UINT_EQUALS(value->code.pattern, code.pattern); ASSERT_UINT_EQUALS(value->code.num_bits, code.num_bits); } return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_encoder, test_huffman_encoder) static int test_huffman_encoder(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a short url */ uint8_t output_buffer[ENCODED_URL_LEN + 1]; AWS_ZERO_ARRAY(output_buffer); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, ENCODED_URL_LEN); struct aws_huffman_symbol_coder *coder = test_get_coder(); struct aws_huffman_encoder encoder; aws_huffman_encoder_init(&encoder, coder); struct aws_byte_cursor to_encode = aws_byte_cursor_from_array(s_url_string, URL_STRING_LEN); const size_t encoded_length = aws_huffman_get_encoded_length(&encoder, to_encode); ASSERT_UINT_EQUALS(ENCODED_URL_LEN, encoded_length); int result = aws_huffman_encode(&encoder, &to_encode, &output_buf); ASSERT_SUCCESS(result); ASSERT_UINT_EQUALS(ENCODED_URL_LEN, output_buf.len); ASSERT_UINT_EQUALS(0, output_buffer[ENCODED_URL_LEN]); ASSERT_BIN_ARRAYS_EQUALS(s_encoded_url, ENCODED_URL_LEN, output_buf.buffer, output_buf.len); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_encoder_all_code_points, test_huffman_encoder_all_code_points) static int test_huffman_encoder_all_code_points(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a sequence of all character values expressable as * characters */ uint8_t output_buffer[ENCODED_CODES_LEN + 1]; AWS_ZERO_ARRAY(output_buffer); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, ENCODED_CODES_LEN); struct aws_huffman_symbol_coder *coder = test_get_coder(); struct aws_huffman_encoder encoder; aws_huffman_encoder_init(&encoder, coder); struct aws_byte_cursor to_encode = aws_byte_cursor_from_array(s_all_codes, ALL_CODES_LEN); const size_t encoded_length = aws_huffman_get_encoded_length(&encoder, to_encode); ASSERT_UINT_EQUALS(ENCODED_CODES_LEN, encoded_length); int result = aws_huffman_encode(&encoder, &to_encode, &output_buf); ASSERT_SUCCESS(result); ASSERT_UINT_EQUALS(ENCODED_CODES_LEN, output_buf.len); ASSERT_UINT_EQUALS(0, output_buffer[ENCODED_CODES_LEN]); ASSERT_BIN_ARRAYS_EQUALS(s_encoded_codes, ENCODED_CODES_LEN, output_buf.buffer, output_buf.len); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_encoder_partial_output, test_huffman_encoder_partial_output) static int test_huffman_encoder_partial_output(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding when the output buffer size is limited */ struct aws_huffman_encoder encoder; aws_huffman_encoder_init(&encoder, test_get_coder()); uint8_t output_buffer[ENCODED_CODES_LEN]; for (size_t i = 0; i < NUM_STEP_SIZES; ++i) { const size_t step_size = s_step_sizes[i]; aws_huffman_encoder_reset(&encoder); struct aws_byte_cursor to_encode = aws_byte_cursor_from_array(s_all_codes, ALL_CODES_LEN); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, (size_t)-1); output_buf.capacity = 0; AWS_ZERO_ARRAY(output_buffer); do { output_buf.capacity += step_size; if (output_buf.capacity > ENCODED_CODES_LEN) { output_buf.capacity = ENCODED_CODES_LEN; } const size_t previous_output_len = output_buf.len; int result = aws_huffman_encode(&encoder, &to_encode, &output_buf); ASSERT_TRUE(output_buf.len > previous_output_len); ASSERT_BIN_ARRAYS_EQUALS(s_encoded_codes, output_buf.len, output_buf.buffer, output_buf.len); if (output_buf.len == ENCODED_CODES_LEN) { ASSERT_SUCCESS(result); } else { ASSERT_UINT_EQUALS(AWS_ERROR_SHORT_BUFFER, aws_last_error()); aws_reset_error(); } } while (output_buf.len < ENCODED_CODES_LEN); ASSERT_UINT_EQUALS(ENCODED_CODES_LEN, output_buf.len); ASSERT_BIN_ARRAYS_EQUALS(s_encoded_codes, ENCODED_CODES_LEN, output_buf.buffer, output_buf.len); } return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_encoder_exact_output, test_huffman_encoder_exact_output) static int test_huffman_encoder_exact_output(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding when the output buffer size is exactly the necessary size */ struct aws_huffman_encoder encoder; aws_huffman_encoder_init(&encoder, test_get_coder()); uint8_t output_buffer[2]; struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, 2); /* Encode a character that uses 8 bits into a 1 byte buffer */ struct aws_byte_cursor to_encode = aws_byte_cursor_from_array("?", 1); uint8_t expected_1byte[] = {0xba}; output_buf.capacity = 1; ASSERT_SUCCESS(aws_huffman_encode(&encoder, &to_encode, &output_buf)); ASSERT_BIN_ARRAYS_EQUALS(expected_1byte, 1, output_buf.buffer, output_buf.len); /* Encode 2 characters that sum to 16 bits, into a 2 byte buffer * y: 101000 * z: 1101111001 * combined: 1010001101111001 == 0xa379 */ to_encode = aws_byte_cursor_from_array("yz", 2); uint8_t expected_2byte[] = {0xa3, 0x79}; output_buf.capacity = 2; aws_byte_buf_reset(&output_buf, true /*zero*/); ASSERT_SUCCESS(aws_huffman_encode(&encoder, &to_encode, &output_buf)); ASSERT_BIN_ARRAYS_EQUALS(expected_2byte, 2, output_buf.buffer, output_buf.len); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_symbol_decoder, test_huffman_symbol_decoder) static int test_huffman_symbol_decoder(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test decoding each character */ struct aws_huffman_symbol_coder *coder = test_get_coder(); for (size_t i = 0; i < NUM_CODE_POINTS; ++i) { struct huffman_test_code_point *value = &s_code_points[i]; uint32_t bit_pattern = value->code.pattern << (32 - value->code.num_bits); uint8_t out; size_t bits_read = coder->decode(bit_pattern, &out, NULL); ASSERT_UINT_EQUALS(value->symbol, out); ASSERT_UINT_EQUALS(value->code.num_bits, bits_read); } return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_decoder, test_huffman_decoder) static int test_huffman_decoder(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test decoding a short url */ char output_buffer[URL_STRING_LEN + 1]; AWS_ZERO_ARRAY(output_buffer); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, URL_STRING_LEN); struct aws_huffman_symbol_coder *coder = test_get_coder(); struct aws_huffman_decoder decoder; aws_huffman_decoder_init(&decoder, coder); struct aws_byte_cursor to_decode = aws_byte_cursor_from_array(s_encoded_url, ENCODED_URL_LEN); int result = aws_huffman_decode(&decoder, &to_decode, &output_buf); ASSERT_SUCCESS(result); ASSERT_UINT_EQUALS(URL_STRING_LEN, output_buf.len); ASSERT_UINT_EQUALS(0, to_decode.len); ASSERT_UINT_EQUALS(output_buffer[URL_STRING_LEN], 0); ASSERT_BIN_ARRAYS_EQUALS(s_url_string, URL_STRING_LEN, output_buf.buffer, output_buf.len); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_decoder_all_code_points, test_huffman_decoder_all_code_points) static int test_huffman_decoder_all_code_points(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test decoding a sequence of all character values expressable as * characters */ char output_buffer[ALL_CODES_LEN + 1]; AWS_ZERO_ARRAY(output_buffer); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, ALL_CODES_LEN); struct aws_huffman_symbol_coder *coder = test_get_coder(); struct aws_huffman_decoder decoder; aws_huffman_decoder_init(&decoder, coder); struct aws_byte_cursor to_decode = aws_byte_cursor_from_array(s_encoded_codes, ENCODED_CODES_LEN); int result = aws_huffman_decode(&decoder, &to_decode, &output_buf); ASSERT_SUCCESS(result); ASSERT_UINT_EQUALS(ALL_CODES_LEN, output_buf.len); ASSERT_UINT_EQUALS(0, to_decode.len); ASSERT_UINT_EQUALS(output_buffer[ALL_CODES_LEN], 0); ASSERT_BIN_ARRAYS_EQUALS(s_all_codes, ALL_CODES_LEN, output_buf.buffer, output_buf.len); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_decoder_partial_input, test_huffman_decoder_partial_input) static int test_huffman_decoder_partial_input(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test decoding a buffer in chunks */ struct aws_huffman_decoder decoder; aws_huffman_decoder_init(&decoder, test_get_coder()); char output_buffer[150]; for (size_t i = 0; i < NUM_STEP_SIZES; ++i) { const size_t step_size = s_step_sizes[i]; aws_huffman_decoder_reset(&decoder); struct aws_byte_cursor to_decode = aws_byte_cursor_from_array(s_encoded_codes, ENCODED_CODES_LEN); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, ALL_CODES_LEN); AWS_ZERO_ARRAY(output_buffer); do { const size_t chunk_size = step_size < to_decode.len ? step_size : to_decode.len; struct aws_byte_cursor to_decode_chunk = aws_byte_cursor_advance(&to_decode, chunk_size); int result = aws_huffman_decode(&decoder, &to_decode_chunk, &output_buf); ASSERT_UINT_EQUALS(0, to_decode_chunk.len); ASSERT_BIN_ARRAYS_EQUALS(s_all_codes, output_buf.len, output_buf.buffer, output_buf.len); if (output_buf.len == ALL_CODES_LEN) { ASSERT_SUCCESS(result); } } while (output_buf.len < ALL_CODES_LEN); ASSERT_UINT_EQUALS(ALL_CODES_LEN, output_buf.len); ASSERT_BIN_ARRAYS_EQUALS(s_all_codes, ALL_CODES_LEN, output_buf.buffer, output_buf.len); } return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_decoder_partial_output, test_huffman_decoder_partial_output) static int test_huffman_decoder_partial_output(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test decoding when the output buffer size is limited */ struct aws_huffman_decoder decoder; aws_huffman_decoder_init(&decoder, test_get_coder()); char output_buffer[150]; for (size_t i = 0; i < NUM_STEP_SIZES; ++i) { const size_t step_size = s_step_sizes[i]; aws_huffman_decoder_reset(&decoder); struct aws_byte_cursor to_decode = aws_byte_cursor_from_array(s_encoded_codes, ENCODED_CODES_LEN); struct aws_byte_buf output_buf = aws_byte_buf_from_empty_array(output_buffer, (size_t)-1); output_buf.capacity = 0; /* Can't set above because it sets buffer to 0 */ AWS_ZERO_ARRAY(output_buffer); do { output_buf.capacity += step_size; if (output_buf.capacity > ALL_CODES_LEN) { output_buf.capacity = ALL_CODES_LEN; } const size_t previous_output_size = output_buf.len; int result = aws_huffman_decode(&decoder, &to_decode, &output_buf); ASSERT_TRUE(output_buf.len > previous_output_size); ASSERT_BIN_ARRAYS_EQUALS(s_all_codes, output_buf.len, output_buf.buffer, output_buf.len); if (output_buf.len == ALL_CODES_LEN) { ASSERT_SUCCESS(result); } else { ASSERT_UINT_EQUALS(AWS_ERROR_SHORT_BUFFER, aws_last_error()); aws_reset_error(); } } while (output_buf.len < ALL_CODES_LEN); ASSERT_UINT_EQUALS(ALL_CODES_LEN, output_buf.len); ASSERT_BIN_ARRAYS_EQUALS(s_all_codes, ALL_CODES_LEN, output_buf.buffer, output_buf.len); } return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_decoder_allow_growth, test_huffman_decoder_allow_growth) static int test_huffman_decoder_allow_growth(struct aws_allocator *allocator, void *ctx) { (void)ctx; /* Test that decoder will grow output buffer if allow-growth is set */ struct aws_huffman_decoder decoder; aws_huffman_decoder_init(&decoder, test_get_coder()); aws_huffman_decoder_allow_growth(&decoder, true); struct aws_byte_buf output_buf; ASSERT_SUCCESS(aws_byte_buf_init(&output_buf, allocator, 1 /* way too small */)); struct aws_byte_cursor to_decode = aws_byte_cursor_from_array(s_encoded_url, ENCODED_URL_LEN); ASSERT_SUCCESS(aws_huffman_decode(&decoder, &to_decode, &output_buf)); ASSERT_UINT_EQUALS(0, to_decode.len); ASSERT_BIN_ARRAYS_EQUALS(s_url_string, URL_STRING_LEN, output_buf.buffer, output_buf.len); aws_byte_buf_clean_up(&output_buf); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_transitive, test_huffman_transitive) static int test_huffman_transitive(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a short url and immediately decoding it */ const char *error_message = NULL; int result = huffman_test_transitive(test_get_coder(), s_url_string, URL_STRING_LEN, ENCODED_URL_LEN, &error_message); ASSERT_SUCCESS(result, error_message); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_transitive_even_bytes, test_huffman_transitive_even_bytes) static int test_huffman_transitive_even_bytes(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a string that encodes to a multiple of 8 bits */ const char *error_message = NULL; int result = huffman_test_transitive(test_get_coder(), "cdfh", 4, 3, &error_message); ASSERT_SUCCESS(result, error_message); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_transitive_all_code_points, test_huffman_transitive_all_code_points) static int test_huffman_transitive_all_code_points(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a sequence of all character values expressable as * characters and immediately decoding it */ const char *error_message = NULL; int result = huffman_test_transitive(test_get_coder(), s_all_codes, ALL_CODES_LEN, ENCODED_CODES_LEN, &error_message); ASSERT_SUCCESS(result, error_message); return AWS_OP_SUCCESS; } AWS_TEST_CASE(huffman_transitive_chunked, test_huffman_transitive_chunked) static int test_huffman_transitive_chunked(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; /* Test encoding a sequence of all character values expressable as * characters and immediately decoding it */ for (size_t i = 0; i < NUM_STEP_SIZES; ++i) { const size_t step_size = s_step_sizes[i]; const char *error_message = NULL; int result = huffman_test_transitive_chunked( test_get_coder(), s_all_codes, ALL_CODES_LEN, ENCODED_CODES_LEN, step_size, &error_message); ASSERT_SUCCESS(result, error_message); } return AWS_OP_SUCCESS; }