libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

commit 22a11420e889cc305d79b492ba8c09303d06fa93
parent 0964c7a3c82376f6a7d534ab992a3bd9bcb5e6ad
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Wed, 27 Aug 2025 17:24:37 +0200

Added H2 Huffman unit tests

Diffstat:
Msrc/tests/Makefile.am | 2+-
Asrc/tests/unit/.gitignore | 3+++
Asrc/tests/unit/Makefile.am | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tests/unit/unit_h2_huffman_codec.c | 1689+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1739 insertions(+), 1 deletion(-)

diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am @@ -1,6 +1,6 @@ # This Makefile.am is in the public domain -SUBDIRS = basic +SUBDIRS = unit basic if HAVE_POSIX_THREADS if MHD_SUPPORT_UPGRADE diff --git a/src/tests/unit/.gitignore b/src/tests/unit/.gitignore @@ -0,0 +1,3 @@ +unit_*[a-z0-9_][a-z0-9_][a-z0-9_] +!*.c +!*.h diff --git a/src/tests/unit/Makefile.am b/src/tests/unit/Makefile.am @@ -0,0 +1,46 @@ +# This Makefile.am is in the public domain +EMPTY_ITEM = + +H2_SRC_DIR = $(srcdir)/../../mhd2/h2 +HPACK_SRC_DIR = $(H2_SRC_DIR)/hpack + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/mhd2 \ + -I$(top_srcdir)/src/incl_priv \ + -I$(top_srcdir)/src/include \ + -I$(top_srcdir)/src/tests \ + -DMHD_CPU_COUNT=$(CPU_COUNT) \ + -DMHD_UNIT_TESTING \ + $(CPPFLAGS_ac) $(MHD_LIB_CPPFLAGS) + +AM_CFLAGS = $(CFLAGS_ac) $(MHD_LIB_CFLAGS) + +AM_LDFLAGS = $(LDFLAGS_ac) $(MHD_LIB_LDFLAGS) + +AM_TESTS_ENVIRONMENT = $(TESTS_ENVIRONMENT_ac) + +if USE_COVERAGE + AM_CFLAGS += -fprofile-arcs -ftest-coverage +endif + +LDADD = $(MHD_LIBDEPS) + +check_PROGRAMS = \ + $(EMPTY_ITEM) + +if MHD_SUPPORT_HTTP2 +check_PROGRAMS += \ + unit_h2_huffman_encode \ + unit_h2_huffman_decode \ + $(EMPTY_ITEM) +endif + +TESTS = $(check_PROGRAMS) + +unit_h2_huffman_encode_SOURCES = \ + unit_h2_huffman_codec.c \ + $(srcdir)/../mhdt_has_in_name.h \ + $(srcdir)/../mhdt_has_param.h \ + $(HPACK_SRC_DIR)/h2_huffman_codec.c + +unit_h2_huffman_decode_SOURCES = $(unit_h2_huffman_encode_SOURCES) diff --git a/src/tests/unit/unit_h2_huffman_codec.c b/src/tests/unit/unit_h2_huffman_codec.c @@ -0,0 +1,1689 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ +/* + This file is part of GNU libmicrohttpd. + Copyright (C) 2025 Evgeny Grin (Karlson2k) + + GNU libmicrohttpd 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. + + GNU libmicrohttpd 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. + + Alternatively, you can redistribute GNU libmicrohttpd and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version, together + with the eCos exception, as follows: + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile this + file and link it with other works to produce a work based on this + file, this file does not by itself cause the resulting work to be + covered by the GNU General Public License. However the source code + for this file must still be made available in accordance with + section (3) of the GNU General Public License v2. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + + You should have received copies of the GNU Lesser General Public + License and the GNU General Public License along with this library; + if not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file src/test/unit/unit_h2_huffman_coding.c + * @brief The tests for HTTP/2 Huffman coding function + * @author Karlson2k (Evgeny Grin) + */ + + +#include "mhd_sys_options.h" + +#include "h2/hpack/h2_huffman_codec.h" + +#include <stdio.h> +#include <string.h> + +#include "sys_base_types.h" +#include "mhd_str_types.h" +#include "mhd_str_macros.h" + +#include "mhdt_has_in_name.h" +#include "mhdt_has_param.h" + +#ifndef MHD_ENABLE_SLOW_TESTS +static int enable_deep_tests = 0; +#else +static int enable_deep_tests = ! 0; +#endif + +struct mhdt_HEcondedData +{ + const size_t size; + const uint8_t data[640]; +}; + +struct mhdt_HTestData +{ + const struct MHD_String str; + const struct mhdt_HEcondedData enc; +}; + +static const struct mhdt_HTestData h_data[] = { + /* + ** These test data entries are extracted from RFC 7541 examples ** + */ + { + mhd_MSTR_INIT ("www.example.com"), + { + 12u, + { + 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 0x6b, 0xa0, 0xab, 0x90, + 0xf4, 0xff + } + } + } + , + { + mhd_MSTR_INIT ("no-cache"), + { + 6u, + { + 0xa8, 0xeb, 0x10, 0x64, 0x9c, 0xbf + } + } + } + , + { + mhd_MSTR_INIT ("custom-key"), + { + 8u, + { + 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xa9, 0x7d, 0x7f + } + } + } + , + { + mhd_MSTR_INIT ("custom-value"), + { + 9u, + { + 0x25, 0xa8, 0x49, 0xe9, 0x5b, 0xb8, 0xe8, 0xb4, 0xbf + } + } + } + , + { + mhd_MSTR_INIT ("302"), + { + 2u, + { + 0x64, 0x02 + } + } + } + , + { + mhd_MSTR_INIT ("private"), + { + 5u, + { + 0xae, 0xc3, 0x77, 0x1a, 0x4b + } + } + } + , + { + mhd_MSTR_INIT ("Mon, 21 Oct 2013 20:13:21 GMT"), + { + 22u, + { + 0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44, 0xa8, 0x20, 0x05, + 0x95, 0x04, 0x0b, 0x81, 0x66, 0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff + } + } + } + , + { + mhd_MSTR_INIT ("https://www.example.com"), + { + 17u, + { + 0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f, 0x0b, 0x97, 0xc8, 0xe9, + 0xae, 0x82, 0xae, 0x43, 0xd3 + } + } + } + , + { + mhd_MSTR_INIT ("307"), + { + 3u, + { + 0x64, 0x0e, 0xff + } + } + } + , + { + mhd_MSTR_INIT ("gzip"), + { + 3u, + { + 0x9b, 0xd9, 0xab + } + } + } + , + { + mhd_MSTR_INIT ("foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"), + { + 45u, + { + 0x94, 0xe7, 0x82, 0x1d, 0xd7, 0xf2, 0xe6, 0xc7, 0xb3, 0x35, 0xdf, 0xdf, + 0xcd, 0x5b, 0x39, 0x60, 0xd5, 0xaf, 0x27, 0x08, 0x7f, 0x36, 0x72, 0xc1, + 0xab, 0x27, 0x0f, 0xb5, 0x29, 0x1f, 0x95, 0x87, 0x31, 0x60, 0x65, 0xc0, + 0x03, 0xed, 0x4e, 0xe5, 0xb1, 0x06, 0x3d, 0x50, 0x07 + } + } + } + /* + ** Hand-crafted test entries ** + */ + , + { + mhd_MSTR_INIT ("Hello, Huffman coding!"), + { + 17u, + { + 0xC6, 0x5A, 0x28, 0x3F, 0xD2, 0x98, 0xED, 0x96, 0x5A, 0x47, 0x52, 0x84, + 0x3C, 0x86, 0xAA, 0x6F, 0xE3 + } + } + } + , + { + mhd_MSTR_INIT ("nginx"), + { + 4u, + { + 0xAA, 0x63, 0x55, 0xE7 + } + } + } + , + { + mhd_MSTR_INIT ("apache"), + { + 4u, + { + 0x1D, 0x63, 0x24, 0xE5 + } + } + } + , + { + mhd_MSTR_INIT (" "), + { + 1u, + { + 0x53 + } + } + } + , + { + mhd_MSTR_INIT ("0"), + { + 1u, + { + 0x07 + } + } + } + , + { /* Empty */ + mhd_MSTR_INIT (""), + { + 0u, + { + 0 + } + } + } + , + { /* Long codes */ + mhd_MSTR_INIT ("\x0A" "\x0D" "\x16" "\x0A" "\x0D" "\x16" "\x0A" \ + "\x0D" "\x16" "\x0A"), + { + 38u, + { + 0xFF, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, + 0xBF, 0xFF, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, + 0xEF, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, + 0xFB, 0xFF, 0xFF, 0xFF, 0xCF + } + } + } + , + { /* Shortest + longest codes */ + mhd_MSTR_INIT ("0" "\x0A" "1"), + { + 5u, + { + 0x07, 0xFF, 0xFF, 0xFF, 0x81 + } + } + } + , + { /* Shortest + longest codes */ + mhd_MSTR_INIT ("\x0D" "2a" "\x16" "ce" "\x0A"), + { + 14u, + { + 0xFF, 0xFF, 0xFF, 0xF4, 0x43, 0xFF, 0xFF, 0xFF, 0xF8, 0x85, 0xFF, 0xFF, + 0xFF, 0xF3 + } + } + } + , + { /* Shortest + longest codes */ + mhd_MSTR_INIT ("01" "\x0A" "2" "\x0D" "ac" "\x16" "ei" "\x0A" \ + "os" "\x0D" "t"), + { + 25u, + { + 0x00, 0x7F, 0xFF, 0xFF, 0xFC, 0x17, 0xFF, 0xFF, 0xFF, 0xA3, 0x27, 0xFF, + 0xFF, 0xFF, 0xC5, 0x37, 0xFF, 0xFF, 0xFF, 0x87, 0x47, 0xFF, 0xFF, 0xFF, + 0xA9 + } + } + } + , + { /* Short + long codes */ + mhd_MSTR_INIT ("0 :&!'#" "\x00" "^<\\" "\x80" "\x99" "\x81" \ + "\x01" "\x09" "\xC7" "\xC0" "\xCB" "\x02" "\x0A"), + { + 46u, + { + 0x02, 0x97, 0x3E, 0x3F, 0x8F, 0xF5, 0xFF, 0x5F, 0xF8, 0xFF, 0xF3, 0xFF, + 0xE7, 0xFF, 0xF0, 0xFF, 0xFE, 0x6F, 0xFF, 0xEE, 0x7F, 0xFF, 0xA5, 0xFF, + 0xFF, 0x63, 0xFF, 0xFF, 0xAB, 0xFF, 0xFF, 0xD9, 0xFF, 0xFF, 0xF0, 0x7F, + 0xFF, 0xFD, 0xEF, 0xFF, 0xFF, 0xE2, 0xFF, 0xFF, 0xFF, 0xF3 + } + } + } + , + { /* All printable chars */ + mhd_MSTR_INIT (" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJ" \ + "KLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"), + { + 93u, + { + 0x53, 0xF8, 0xFE, 0x7F, 0xEB, 0xFF, 0x2A, 0xFC, 0x7F, 0xAF, 0xEB, 0xFB, + 0xF9, 0xFF, 0x7F, 0x4B, 0x2E, 0xC0, 0x02, 0x26, 0x5A, 0x6D, 0xC7, 0x5E, + 0x7E, 0xE7, 0xDF, 0xFF, 0xC8, 0x3F, 0xEF, 0xFC, 0xFF, 0xD4, 0x37, 0x6F, + 0x5F, 0xC1, 0x87, 0x16, 0x3C, 0x99, 0x73, 0x67, 0xD1, 0xA7, 0x56, 0xBD, + 0x9B, 0x77, 0x6F, 0xE1, 0xC7, 0x97, 0xE7, 0x3F, 0xDF, 0xFD, 0xFF, 0xFF, + 0x0F, 0xFE, 0x7F, 0xF9, 0x17, 0xFF, 0xD1, 0xC6, 0x49, 0x0B, 0x2C, 0xD3, + 0x9B, 0xA7, 0x5A, 0x29, 0xA8, 0xF5, 0xF6, 0xB1, 0x09, 0xB7, 0xBF, 0x8F, + 0x3E, 0xBD, 0xFF, 0xFE, 0xFF, 0x9F, 0xFE, 0xFF, 0xF7 + } + } + } + , + { /* All printable chars in reverse */ + mhd_MSTR_INIT ("~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUT" \ + "SRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"! "), + { + 93u, + { + 0xFF, 0xEF, 0xFF, 0xBF, 0xF3, 0xFF, 0xF7, 0xBF, 0x5E, 0x7C, 0x77, 0xB5, + 0x28, 0xB3, 0xB5, 0x67, 0xAA, 0x9A, 0x3A, 0xF4, 0x34, 0xF3, 0x4A, 0x59, + 0x09, 0x18, 0xFF, 0xFE, 0xC5, 0xFF, 0xE7, 0xFF, 0x3F, 0xFF, 0x87, 0xFE, + 0xFF, 0x79, 0xFE, 0x72, 0xE3, 0xC3, 0x7E, 0xED, 0xBB, 0x35, 0xEA, 0xD3, + 0xA3, 0x3E, 0x6C, 0xB9, 0x31, 0xE2, 0xC3, 0x82, 0xFD, 0xEB, 0xB0, 0xFF, + 0xEB, 0xFC, 0xFF, 0xB8, 0x3F, 0xFE, 0x7D, 0xDC, 0x7D, 0xE7, 0x5C, 0x6D, + 0xA6, 0x44, 0x10, 0x30, 0xBA, 0xDF, 0x5F, 0xEF, 0xE7, 0xFB, 0xFE, 0xBF, + 0xD7, 0xC2, 0xBF, 0xF9, 0xFF, 0xAF, 0xE7, 0xF8, 0x53 + } + } + } + , + { /* All chars */ + mhd_MSTR_INIT ("\x00" "\x01" "\x02" "\x03" "\x04" "\x05" "\x06" "\x07" \ + "\x08" "\x09" "\x0A" "\x0B" "\x0C" "\x0D" "\x0E" "\x0F" \ + "\x10" "\x11" "\x12" "\x13" "\x14" "\x15" "\x16" "\x17" \ + "\x18" "\x19" "\x1A" "\x1B" "\x1C" "\x1D" "\x1E" "\x1F" \ + " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQR" \ + "STUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" "\x7F" \ + "\x80" "\x81" "\x82" "\x83" "\x84" "\x85" "\x86" "\x87" \ + "\x88" "\x89" "\x8A" "\x8B" "\x8C" "\x8D" "\x8E" "\x8F" \ + "\x90" "\x91" "\x92" "\x93" "\x94" "\x95" "\x96" "\x97" \ + "\x98" "\x99" "\x9A" "\x9B" "\x9C" "\x9D" "\x9E" "\x9F" \ + "\xA0" "\xA1" "\xA2" "\xA3" "\xA4" "\xA5" "\xA6" "\xA7" \ + "\xA8" "\xA9" "\xAA" "\xAB" "\xAC" "\xAD" "\xAE" "\xAF" \ + "\xB0" "\xB1" "\xB2" "\xB3" "\xB4" "\xB5" "\xB6" "\xB7" \ + "\xB8" "\xB9" "\xBA" "\xBB" "\xBC" "\xBD" "\xBE" "\xBF" \ + "\xC0" "\xC1" "\xC2" "\xC3" "\xC4" "\xC5" "\xC6" "\xC7" \ + "\xC8" "\xC9" "\xCA" "\xCB" "\xCC" "\xCD" "\xCE" "\xCF" \ + "\xD0" "\xD1" "\xD2" "\xD3" "\xD4" "\xD5" "\xD6" "\xD7" \ + "\xD8" "\xD9" "\xDA" "\xDB" "\xDC" "\xDD" "\xDE" "\xDF" \ + "\xE0" "\xE1" "\xE2" "\xE3" "\xE4" "\xE5" "\xE6" "\xE7" \ + "\xE8" "\xE9" "\xEA" "\xEB" "\xEC" "\xED" "\xEE" "\xEF" \ + "\xF0" "\xF1" "\xF2" "\xF3" "\xF4" "\xF5" "\xF6" "\xF7" \ + "\xF8" "\xF9" "\xFA" "\xFB" "\xFC" "\xFD" "\xFE" "\xFF"), + { + 583u, + { + 0xFF, 0xC7, 0xFF, 0xFD, 0x8F, 0xFF, 0xFF, 0xE2, 0xFF, 0xFF, 0xFE, 0x3F, + 0xFF, 0xFF, 0xE4, 0xFF, 0xFF, 0xFE, 0x5F, 0xFF, 0xFF, 0xE6, 0xFF, 0xFF, + 0xFE, 0x7F, 0xFF, 0xFF, 0xE8, 0xFF, 0xFF, 0xEA, 0xFF, 0xFF, 0xFF, 0xF3, + 0xFF, 0xFF, 0xFA, 0x7F, 0xFF, 0xFF, 0xAB, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, + 0xFF, 0xEB, 0xFF, 0xFF, 0xFE, 0xCF, 0xFF, 0xFF, 0xED, 0xFF, 0xFF, 0xFE, + 0xEF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xF1, 0xFF, + 0xFF, 0xFF, 0x2F, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, + 0xFD, 0x3F, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xFD, 0xBF, 0xFF, 0xFF, 0xDF, + 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFE, 0xBF, 0xFF, + 0xFF, 0xED, 0x4F, 0xE3, 0xF9, 0xFF, 0xAF, 0xFC, 0xAB, 0xF1, 0xFE, 0xBF, + 0xAF, 0xEF, 0xE7, 0xFD, 0xFD, 0x2C, 0xBB, 0x00, 0x08, 0x99, 0x69, 0xB7, + 0x1D, 0x79, 0xFB, 0x9F, 0x7F, 0xFF, 0x20, 0xFF, 0xBF, 0xF3, 0xFF, 0x50, + 0xDD, 0xBD, 0x7F, 0x06, 0x1C, 0x58, 0xF2, 0x65, 0xCD, 0x9F, 0x46, 0x9D, + 0x5A, 0xF6, 0x6D, 0xDD, 0xBF, 0x87, 0x1E, 0x5F, 0x9C, 0xFF, 0x7F, 0xF7, + 0xFF, 0xFC, 0x3F, 0xF9, 0xFF, 0xE4, 0x5F, 0xFF, 0x47, 0x19, 0x24, 0x2C, + 0xB3, 0x4E, 0x6E, 0x9D, 0x68, 0xA6, 0xA3, 0xD7, 0xDA, 0xC4, 0x26, 0xDE, + 0xFE, 0x3C, 0xFA, 0xF7, 0xFF, 0xFB, 0xFE, 0x7F, 0xFB, 0xFF, 0xDF, 0xFF, + 0xFF, 0xFC, 0xFF, 0xFE, 0x6F, 0xFF, 0xF4, 0xBF, 0xFF, 0x9F, 0xFF, 0xFA, + 0x3F, 0xFF, 0xD3, 0xFF, 0xFF, 0x53, 0xFF, 0xFD, 0x5F, 0xFF, 0xFB, 0x3F, + 0xFF, 0xEB, 0x7F, 0xFF, 0xDA, 0xFF, 0xFF, 0xB7, 0xFF, 0xFF, 0x73, 0xFF, + 0xFE, 0xEF, 0xFF, 0xFD, 0xEF, 0xFF, 0xFE, 0xBF, 0xFF, 0xFB, 0xFF, 0xFF, + 0xFD, 0x9F, 0xFF, 0xFD, 0xBF, 0xFF, 0xEB, 0xFF, 0xFF, 0xE0, 0xFF, 0xFF, + 0xEE, 0xFF, 0xFF, 0xC3, 0xFF, 0xFF, 0x8B, 0xFF, 0xFF, 0x1F, 0xFF, 0xFE, + 0x4F, 0xFF, 0xEE, 0x7F, 0xFF, 0xB1, 0xFF, 0xFF, 0x97, 0xFF, 0xFD, 0x9F, + 0xFF, 0xFC, 0xDF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDA, 0xFF, + 0xFE, 0xEF, 0xFF, 0xF4, 0xFF, 0xFF, 0xB7, 0xFF, 0xFE, 0xE7, 0xFF, 0xFE, + 0x8F, 0xFF, 0xFD, 0x3F, 0xFF, 0xDE, 0xFF, 0xFF, 0xD5, 0xFF, 0xFE, 0xEF, + 0xFF, 0xFB, 0xDF, 0xFF, 0xFE, 0x1F, 0xFF, 0xDF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFF, 0x5F, 0xFF, 0xFE, 0xCF, 0xFF, 0xF0, 0x7F, 0xFF, 0x87, 0xFF, 0xFE, + 0x0F, 0xFF, 0xF1, 0x7F, 0xFF, 0xED, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0x77, + 0xFF, 0xFE, 0xFF, 0xFF, 0xEA, 0xFF, 0xFF, 0x8B, 0xFF, 0xFE, 0x3F, 0xFF, + 0xF9, 0x3F, 0xFF, 0xF8, 0x7F, 0xFF, 0xCB, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, + 0x1F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, 0xE1, 0xFF, 0xFE, 0xBF, 0xFF, 0xE3, + 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0x2F, 0xFF, 0xFA, 0x3F, 0xFF, 0xFD, 0x9F, + 0xFF, 0xFF, 0x17, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xF2, 0x7F, 0xFF, 0xFD, + 0xEF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xF2, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, + 0xFB, 0x7F, 0xFF, 0x97, 0xFF, 0xF8, 0xFF, 0xFF, 0xFE, 0x6F, 0xFF, 0xFF, + 0xC1, 0xFF, 0xFF, 0xF8, 0x7F, 0xFF, 0xFE, 0x7F, 0xFF, 0xFF, 0xC5, 0xFF, + 0xFF, 0xE5, 0xFF, 0xFE, 0x4F, 0xFF, 0xF2, 0xFF, 0xFF, 0xFD, 0x1F, 0xFF, + 0xFF, 0x4F, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xC9, + 0xFF, 0xFF, 0xF9, 0x7F, 0xFF, 0xB3, 0xFF, 0xFF, 0xCF, 0xFF, 0xFB, 0x7F, + 0xFF, 0xCD, 0xFF, 0xFF, 0x4F, 0xFF, 0xF9, 0xFF, 0xFF, 0xD1, 0xFF, 0xFF, + 0xCF, 0xFF, 0xFE, 0xAF, 0xFF, 0xFA, 0xFF, 0xFF, 0xFD, 0xDF, 0xFF, 0xFE, + 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xAB, 0xFF, 0xFF, + 0xA7, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xF9, 0xBF, 0xFF, 0xFE, 0xCF, 0xFF, + 0xFF, 0xB7, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFE, 0x8F, 0xFF, 0xFF, 0xD3, + 0xFF, 0xFF, 0xFA, 0xBF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, + 0xFE, 0xCF, 0xFF, 0xFF, 0xDB, 0xFF, 0xFF, 0xFB, 0xBF, 0xFF, 0xFF, 0x7F, + 0xFF, 0xFF, 0xF0, 0xFF, 0xFF, 0xFB, 0xBF + } + } + } + , + { /* All chars in reverse */ + mhd_MSTR_INIT ("\xFF" "\xFE" "\xFD" "\xFC" "\xFB" "\xFA" "\xF9" "\xF8" \ + "\xF7" "\xF6" "\xF5" "\xF4" "\xF3" "\xF2" "\xF1" "\xF0" \ + "\xEF" "\xEE" "\xED" "\xEC" "\xEB" "\xEA" "\xE9" "\xE8" \ + "\xE7" "\xE6" "\xE5" "\xE4" "\xE3" "\xE2" "\xE1" "\xE0" \ + "\xDF" "\xDE" "\xDD" "\xDC" "\xDB" "\xDA" "\xD9" "\xD8" \ + "\xD7" "\xD6" "\xD5" "\xD4" "\xD3" "\xD2" "\xD1" "\xD0" \ + "\xCF" "\xCE" "\xCD" "\xCC" "\xCB" "\xCA" "\xC9" "\xC8" \ + "\xC7" "\xC6" "\xC5" "\xC4" "\xC3" "\xC2" "\xC1" "\xC0" \ + "\xBF" "\xBE" "\xBD" "\xBC" "\xBB" "\xBA" "\xB9" "\xB8" \ + "\xB7" "\xB6" "\xB5" "\xB4" "\xB3" "\xB2" "\xB1" "\xB0" \ + "\xAF" "\xAE" "\xAD" "\xAC" "\xAB" "\xAA" "\xA9" "\xA8" \ + "\xA7" "\xA6" "\xA5" "\xA4" "\xA3" "\xA2" "\xA1" "\xA0" \ + "\x9F" "\x9E" "\x9D" "\x9C" "\x9B" "\x9A" "\x99" "\x98" \ + "\x97" "\x96" "\x95" "\x94" "\x93" "\x92" "\x91" "\x90" \ + "\x8F" "\x8E" "\x8D" "\x8C" "\x8B" "\x8A" "\x89" "\x88" \ + "\x87" "\x86" "\x85" "\x84" "\x83" "\x82" "\x81" "\x80" \ + "\x7F" "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\\[ZYXWVUTS" \ + "RQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)('&%$#\"! " \ + "\x1F" "\x1E" "\x1D" "\x1C" "\x1B" "\x1A" "\x19" "\x18" \ + "\x17" "\x16" "\x15" "\x14" "\x13" "\x12" "\x11" "\x10" \ + "\x0F" "\x0E" "\x0D" "\x0C" "\x0B" "\x0A" "\x09" "\x08" \ + "\x07" "\x06" "\x05" "\x04" "\x03" "\x02" "\x01" "\x00"), + { + 583u, + { + 0xFF, 0xFF, 0xFB, 0xBF, 0xFF, 0xFF, 0x87, 0xFF, 0xFF, 0xEF, + 0xFF, 0xFF, 0xFD, 0xDF, 0xFF, 0xFF, 0xB7, 0xFF, 0xFF, 0xF6, + 0x7F, 0xFF, 0xFF, 0xF7, 0xFF, 0xFF, 0xEB, 0xFF, 0xFF, 0xFD, + 0x5F, 0xFF, 0xFF, 0xA7, 0xFF, 0xFF, 0xF4, 0x7F, 0xFF, 0xFE, + 0x7F, 0xFF, 0xFF, 0xB7, 0xFF, 0xFF, 0xEC, 0xFF, 0xFF, 0xFC, + 0xDF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xAB, + 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xD3, 0xFF, 0xFF, 0xDF, 0xFF, + 0xFF, 0xEE, 0xFF, 0xFF, 0xAF, 0xFF, 0xFE, 0xAF, 0xFF, 0xFE, + 0x7F, 0xFF, 0xE8, 0xFF, 0xFF, 0x3F, 0xFF, 0xFD, 0x3F, 0xFF, + 0xE6, 0xFF, 0xFE, 0xDF, 0xFF, 0xFF, 0x3F, 0xFF, 0xEC, 0xFF, + 0xFF, 0xFC, 0xBF, 0xFF, 0xFF, 0x93, 0xFF, 0xFF, 0xF1, 0xFF, + 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xD3, 0xFF, 0xFF, 0xF4, 0x7F, + 0xFF, 0x97, 0xFF, 0xFC, 0x9F, 0xFF, 0xFE, 0x5F, 0xFF, 0xFF, + 0x8B, 0xFF, 0xFF, 0xE7, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0x83, 0xFF, 0xFF, 0xE6, 0xFF, 0xFF, 0x1F, 0xFF, 0xF2, 0xFF, + 0xFF, 0xF6, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFC, 0xBF, 0xFF, + 0xFF, 0x7F, 0xFF, 0xFF, 0xEF, 0x7F, 0xFF, 0xFC, 0x9F, 0xFF, + 0xFF, 0x1F, 0xFF, 0xFF, 0xC5, 0xFF, 0xFF, 0xEC, 0xFF, 0xFF, + 0xA3, 0xFF, 0xFF, 0x97, 0xFF, 0xFC, 0xFF, 0xFF, 0xC7, 0xFF, + 0xFA, 0xFF, 0xFF, 0xFE, 0x1F, 0xFF, 0xFF, 0x83, 0xFF, 0xFF, + 0x8F, 0xFF, 0xFC, 0xDF, 0xFF, 0xF2, 0xFF, 0xFF, 0xF0, 0xFF, + 0xFF, 0x93, 0xFF, 0xFE, 0x3F, 0xFF, 0xF8, 0xBF, 0xFF, 0xAB, + 0xFF, 0xFF, 0x7F, 0xFF, 0xFE, 0xEF, 0xFF, 0xF8, 0x7F, 0xFF, + 0xF6, 0xFF, 0xFF, 0x8B, 0xFF, 0xFE, 0x0F, 0xFF, 0xF0, 0xFF, + 0xFF, 0x83, 0xFF, 0xFF, 0x67, 0xFF, 0xFE, 0xBF, 0xFF, 0xF7, + 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xE1, 0xFF, 0xFE, 0xF7, 0xFF, + 0xFB, 0xBF, 0xFF, 0xFA, 0xBF, 0xFF, 0xBD, 0xFF, 0xFF, 0xA7, + 0xFF, 0xFF, 0x47, 0xFF, 0xFB, 0x9F, 0xFF, 0xED, 0xFF, 0xFF, + 0x4F, 0xFF, 0xF7, 0x7F, 0xFF, 0xDA, 0xFF, 0xFF, 0xEF, 0xFF, + 0xFF, 0xCF, 0xFF, 0xFF, 0x9B, 0xFF, 0xFD, 0x9F, 0xFF, 0xFC, + 0xBF, 0xFF, 0xEC, 0x7F, 0xFF, 0x73, 0xFF, 0xFF, 0x27, 0xFF, + 0xFE, 0x3F, 0xFF, 0xFC, 0x5F, 0xFF, 0xF8, 0x7F, 0xFF, 0xFB, + 0xBF, 0xFF, 0xF0, 0x7F, 0xFF, 0xAF, 0xFF, 0xFF, 0xDB, 0xFF, + 0xFF, 0xD9, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xAF, 0xFF, 0xFE, + 0xF7, 0xFF, 0xFD, 0xDF, 0xFF, 0xFB, 0x9F, 0xFF, 0xF6, 0xFF, + 0xFF, 0xED, 0x7F, 0xFF, 0xAD, 0xFF, 0xFF, 0x67, 0xFF, 0xFD, + 0x5F, 0xFF, 0xF5, 0x3F, 0xFF, 0xD3, 0xFF, 0xFE, 0x8F, 0xFF, + 0xE7, 0xFF, 0xFF, 0x4B, 0xFF, 0xF9, 0xBF, 0xFF, 0xFF, 0xF3, + 0xFF, 0xBF, 0xFE, 0xFF, 0xCF, 0xFF, 0xDE, 0xFD, 0x79, 0xF1, + 0xDE, 0xD4, 0xA2, 0xCE, 0xD5, 0x9E, 0xAA, 0x68, 0xEB, 0xD0, + 0xD3, 0xCD, 0x29, 0x64, 0x24, 0x63, 0xFF, 0xFB, 0x17, 0xFF, + 0x9F, 0xFC, 0xFF, 0xFE, 0x1F, 0xFB, 0xFD, 0xE7, 0xF9, 0xCB, + 0x8F, 0x0D, 0xFB, 0xB6, 0xEC, 0xD7, 0xAB, 0x4E, 0x8C, 0xF9, + 0xB2, 0xE4, 0xC7, 0x8B, 0x0E, 0x0B, 0xF7, 0xAE, 0xC3, 0xFF, + 0xAF, 0xF3, 0xFE, 0xE0, 0xFF, 0xF9, 0xF7, 0x71, 0xF7, 0x9D, + 0x71, 0xB6, 0x99, 0x10, 0x40, 0xC2, 0xEB, 0x7D, 0x7F, 0xBF, + 0x9F, 0xEF, 0xFA, 0xFF, 0x5F, 0x0A, 0xFF, 0xE7, 0xFE, 0xBF, + 0x9F, 0xE1, 0x4F, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xAF, + 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0xF7, + 0xFF, 0xFF, 0xFF, 0x6F, 0xFF, 0xFF, 0xF5, 0xFF, 0xFF, 0xFF, + 0x4F, 0xFF, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, + 0xFC, 0xBF, 0xFF, 0xFF, 0xC7, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, + 0xFF, 0xBF, 0xFF, 0xFF, 0xFB, 0xBF, 0xFF, 0xFF, 0xB7, 0xFF, + 0xFF, 0xFB, 0x3F, 0xFF, 0xFF, 0xAF, 0xFF, 0xFF, 0xFF, 0xDF, + 0xFF, 0xFF, 0xEA, 0xFF, 0xFF, 0xFE, 0x9F, 0xFF, 0xFF, 0xFF, + 0x3F, 0xFF, 0xFA, 0xBF, 0xFF, 0xFF, 0xA3, 0xFF, 0xFF, 0xF9, + 0xFF, 0xFF, 0xFF, 0x9B, 0xFF, 0xFF, 0xF9, 0x7F, 0xFF, 0xFF, + 0x93, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xFF, 0x8B, 0xFF, 0xFE, + 0xC7, 0xFE, 0x3F + } + } + } + /* Various paddings */ + , + { /* Zero bits padding */ + mhd_MSTR_INIT ("aaaaaaaa"), + { + 5u, + { + 0x18, 0xC6, 0x31, 0x8C, 0x63 + } + } + } + , + { /* 1 bit padding */ + mhd_MSTR_INIT ("aaa"), + { + 2u, + { + 0x18, 0xC7 + } + } + } + , + { /* 2 bits padding */ + mhd_MSTR_INIT ("aaaaaa"), + { + 4u, + { + 0x18, 0xC6, 0x31, 0x8F + } + } + } + , + { /* 3 bits padding */ + mhd_MSTR_INIT ("a"), + { + 1u, + { + 0x1F + } + } + } + , + { /* 4 bits padding */ + mhd_MSTR_INIT ("aaaa"), + { + 3u, + { + 0x18, 0xC6, 0x3F + } + } + } + , + { /* 5 bits padding */ + mhd_MSTR_INIT ("aaaaaaa"), + { + 5u, + { + 0x18, 0xC6, 0x31, 0x8C, 0x7F + } + } + } + , + { /* 6 bits padding */ + mhd_MSTR_INIT ("aa"), + { + 2u, + { + 0x18, 0xFF + } + } + } + , + { /* 7 bits padding */ + mhd_MSTR_INIT ("aaaaa"), + { + 4u, + { + 0x18, 0xC6, 0x31, 0xFF + } + } + } +}; + + +static void +print_hex (FILE *stream, size_t hex_size, const uint8_t *hex) +{ + size_t i; + if (0 == hex_size) + return; + fprintf (stream, "%02X", (unsigned int) hex[0]); + for (i = 1; i < hex_size; ++i) + fprintf (stream, " %02X", (unsigned int) hex[i]); +} + + +/* Not thread-safe! Assuming that the function is called only in one thread */ +static const char * +print_mixed (size_t str_len, const char *str) +{ + static char tmp_bufs[8][2048]; /* Should be enough for testing */ + static size_t cur_buf_idx = 0; + static const size_t buf_size = sizeof(tmp_bufs[0]); + char *buf; + size_t i; + size_t out_idx; + int quote_open = 0; + + buf = tmp_bufs[(cur_buf_idx++) % (sizeof(tmp_bufs) / sizeof(tmp_bufs[0]))]; + + for (i = 0, out_idx = 0; i < str_len; ++i) + { + const char c = str[i]; + if (0x20 <= c && 0x7E >= c) + { + if (! quote_open) + { + if (0 != i) + { + if (buf_size <= out_idx + 3) + break; + buf[out_idx++] = ' '; + } + else if (buf_size <= out_idx + 2) + break; + + buf[out_idx++] = '"'; + quote_open = ! 0; + } + else if (buf_size <= out_idx + 1) + break; + + buf[out_idx++] = c; + } + else + { + const uint8_t digit1 = ((uint8_t) c) & 0xFu; + const uint8_t digit2 = ((uint8_t) c) >> 4; + if (quote_open) + { + if (buf_size <= out_idx + 8) + break; + buf[out_idx++] = '"'; + quote_open = 0; + buf[out_idx++] = ' '; + } + else if (buf_size <= out_idx + 6) + break; + buf[out_idx++] = '"'; + buf[out_idx++] = '\\'; + buf[out_idx++] = 'x'; + buf[out_idx++] = + (char) ((digit1 < 10) ? ('0' + digit1) : ('A' + digit1 - 10)); + buf[out_idx++] = + (char) ((digit2 < 10) ? ('0' + digit2) : ('A' + digit2 - 10)); + buf[out_idx++] = '"'; + } + } + if (i < str_len) + return "[TOO LARGE STRING]"; + + if (quote_open) + { + if (buf_size <= out_idx + 2) + return "[TOO LARGE STRING]"; + buf[out_idx++] = '"'; + } + + if (buf_size <= out_idx + 1) + return "[TOO LARGE STRING]"; + + buf[out_idx++] = 0; + + return buf; +} + + +/* non-zero error code on error, zero on success */ +static int +basic_data_check (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + /* Check the input data */ +#if 0 /* Disabled check, such data is used for checks */ + if (0 == h_data[i].str.len) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong zero length of the string data for " + "the entry number %lu\n", + (unsigned long) i); + return 99; + } +#endif /* Disabled check */ + if (NULL == h_data[i].str.cstr) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong zero NULL pointer for the string data for " + "the entry number %lu\n", + (unsigned long) i); + return 99; + } + else if (h_data[i].str.len < strlen (h_data[i].str.cstr)) + { + fflush (stdout); + fprintf (stderr, "ERROR: unterminated string data for " + "the entry number %lu\n", + (unsigned long) i); + return 99; + } + if (0 != h_data[i].str.cstr[h_data[i].str.len]) + { + fflush (stdout); + fprintf (stderr, "ERROR: unterminated string for the string data for " + "the entry number %lu\n", + (unsigned long) i); + return 99; + } + if ((0 == h_data[i].enc.size) + && (0 != h_data[i].str.len)) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong zero length of encoded data for " + "the entry with with the string data \"%s\"\n", + h_data[i].str.cstr); + return 99; + } + if (((sizeof(h_data[i].enc.data) != h_data[i].enc.size)) && + (0 != h_data[i].enc.data[h_data[i].enc.size])) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong length of encoded data for " + "the entry with with the string data \"%s\"\n", + h_data[i].str.cstr); + return 99; + } + } + return 0; +} + + +/* ------------------ Encode testing ------------------- */ + + +/* non-zero error code on error, zero on success */ +static int +perform_single_enc_check (size_t str_len, + const char *restrict str, + size_t valid_enc_size, + const uint8_t *restrict valid_enc, + size_t use_buff_size) +{ + uint8_t buff[sizeof(h_data[0].enc.data) * 4]; + size_t res; + + if (sizeof(buff) < use_buff_size) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong size of the buffer specified for " + "the check.\n" + "The specified size: %lu\n" + "The maximum possible size: %lu\n", + (unsigned long) use_buff_size, + (unsigned long) sizeof(buff)); + return 99; + } + memset (buff, 0, sizeof(buff)); + + res = mhd_h2_huffman_encode (str_len, + str, + use_buff_size, + buff); + if (valid_enc_size > use_buff_size) + { + if (0 == res) + return 0; /* Failed as required */ + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_encode(%lu, %s, %lu, buffer) " + "returned value %lu, while expected ZERO\n" + "If the buffer would have enough space then " + "the encoded data must be:", + (unsigned long) str_len, + print_mixed (str_len, str), + (unsigned long) use_buff_size, + (unsigned long) res); + print_hex (stderr, valid_enc_size, valid_enc); + fprintf (stderr, "\nResulting encoded data: "); + print_hex (stderr, res, buff); + fprintf (stderr, "\n"); + return 3; + } + + if (valid_enc_size != res) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_encode(%lu, %s, %lu, buffer) " + "returned value %lu, while expected %lu\n", + (unsigned long) str_len, + print_mixed (str_len, str), + (unsigned long) valid_enc_size, + (unsigned long) res, + (unsigned long) valid_enc_size); + return 1; + } + if (0 != memcmp (valid_enc, buff, res)) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_encode(%lu, %s, %lu, buffer) " + "returned expected value %lu, " + "but the data is encoded incorrectly in the output buffer.\n", + (unsigned long) str_len, + print_mixed (str_len, str), + (unsigned long) valid_enc_size, + (unsigned long) res); + fprintf (stderr, "Expected encoded data: "); + print_hex (stderr, valid_enc_size, valid_enc); + fprintf (stderr, "\nResulting encoded data: "); + print_hex (stderr, res, buff); + fprintf (stderr, "\n"); + return 2; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_encode_perfect_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_enc_check (h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].enc.size); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_encode_larger_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_enc_check (h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].enc.size + 1); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_encode_smaller_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + if (0 == h_data[i].enc.size) + continue; + + res = perform_single_enc_check (h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].enc.size - 1); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_encode_fixed_size (size_t use_size) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_enc_check (h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].enc.size, + h_data[i].enc.data, + use_size); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_encode_fixed_sizes (void) +{ + size_t i; + for (i = 0; i <= 16; ++i) + { + int res; + + res = check_h2_h_encode_fixed_size (i); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +test_encode (void) +{ + int res; + + printf ("Checking H2 Huffman encoding with" + " precise output buffer size...\n"); + res = check_h2_h_encode_perfect_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman encoding with" + " larger output buffer size...\n"); + res = check_h2_h_encode_larger_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman encoding with" + " smaller output buffer size...\n"); + res = check_h2_h_encode_smaller_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman encoding with" + " various fixed output buffer sizes...\n"); + res = check_h2_h_encode_fixed_sizes (); + if (0 != res) + return res; + printf ("Succeed\n"); + + return 0; +} + + +/* ------------------ Decode testing ------------------- */ + +static const char * +decode_result_to_string (enum mhd_H2HuffDecodeRes res) +{ + switch (res) + { + case MHD_H2_HUFF_DEC_RES_OK: + return "'OK'"; + case MHD_H2_HUFF_DEC_RES_NO_SPACE: + return "'NO_SPACE'"; + case MHD_H2_HUFF_DEC_RES_BROKEN_DATA: + return "'BROKEN_DATA'"; + default: + break; + } + return "[UNKNOWN RESULT]"; +} + + +/* non-zero error code on error, zero on success */ +static int +perform_single_dec_valid_check (size_t enc_size, + const uint8_t *restrict enc, + size_t valid_str_len, + const char *restrict valid_str, + size_t use_buff_size) +{ + char buff[257 * 2]; + enum mhd_H2HuffDecodeRes res; + size_t ret_size; + + if (sizeof(buff) < use_buff_size) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong size of the buffer specified for " + "the check.\n" + "The specified size: %lu\n" + "The maximum possible size: %lu\n", + (unsigned long) use_buff_size, + (unsigned long) sizeof(buff)); + return 99; + } + memset (buff, 0, sizeof(buff)); + + ret_size = mhd_h2_huffman_decode (enc_size, + enc, + use_buff_size, + buff, + &res); + if (valid_str_len > use_buff_size) + { + if ((0 == ret_size) && (MHD_H2_HUFF_DEC_RES_NO_SPACE == res)) + return 0; /* Failed as required */ + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) use_buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + if (0 != ret_size) + fprintf (stderr, "The function returned value %lu, while expected ZERO\n", + (unsigned long) ret_size); + else + fprintf (stderr, "The function returned ZERO as expected, but the status " + "must be 'NO_SPACE'\n"); + + fprintf (stderr, "The encoded data is: "); + print_hex (stderr, enc_size, enc); + fprintf (stderr, "\nIf the buffer would have enough space then " + "the decoded string must be: %s\n", + print_mixed (valid_str_len, valid_str)); + return 3; + } + + if ((valid_str_len != ret_size) || + ((MHD_H2_HUFF_DEC_RES_OK != res))) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) use_buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + + if (valid_str_len != ret_size) + fprintf (stderr, "The function returned value %lu, while expected %lu\n", + (unsigned long) ret_size, (unsigned long) valid_str_len); + else + fprintf (stderr, "The function returned expected value %lu, " + "but the status must be 'OK'\n", + (unsigned long) ret_size); + + fprintf (stderr, "The encoded data is: "); + print_hex (stderr, enc_size, enc); + fprintf (stderr, "\nThe expected decoded string: %s\n", + print_mixed (valid_str_len, valid_str)); + + return 1; + } + if (0 != memcmp (valid_str, buff, ret_size)) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, buffer, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) use_buff_size, + decode_result_to_string (res), + (unsigned long) ret_size); + fprintf (stderr,"The function returned expected value %lu and set " + "correct 'OK' status, but the data is decoded incorrectly " + "in the output buffer.\n", + (unsigned long) ret_size); + fprintf (stderr, "The decoded string: %s\n", + print_mixed (ret_size, buff)); + fprintf (stderr, "The expected string: %s\n", + print_mixed (valid_str_len, valid_str)); + fprintf (stderr, "The encoded data is: "); + print_hex (stderr, enc_size, enc); + fprintf (stderr, "\n"); + return 2; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_perfect_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_dec_valid_check (h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].str.len); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_larger_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_dec_valid_check (h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].str.len + 1); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_smaller_size (void) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + if (0 == h_data[i].enc.size) + continue; + + res = perform_single_dec_valid_check (h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].str.len, + h_data[i].str.cstr, + h_data[i].str.len - 1); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_fixed_size (size_t use_size) +{ + size_t i; + for (i = 0; i < sizeof(h_data) / sizeof(h_data[0]); ++i) + { + int res; + + res = perform_single_dec_valid_check (h_data[i].enc.size, + h_data[i].enc.data, + h_data[i].str.len, + h_data[i].str.cstr, + use_size); + + if (0 != res) + return res; + } + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_fixed_sizes (void) +{ + size_t i; + for (i = 0; i <= 16; ++i) + { + int res; + + res = check_h2_h_decode_fixed_size (i); + + if (0 != res) + return res; + } + return 0; +} + + +/* This function tries to decode the data and it is succeed, checks + * whether the decoded string can be encoded back to the original encoded + * data. + * The check does not detect situations when a valid encoded string + * cannot be decoded, however as correct codes are re-checked by encoder + * and the total number of valid codes is counted, this check is strong + * enough (unless decoder and encoder have symmetric bugs). + */ +/* non-zero error code on error, zero on success */ +static int +perform_single_dec_unknow_seq_check (size_t enc_size, + const uint8_t *restrict enc, + uint_fast32_t *valid_codes_counter) +{ + char buff[8 * 4]; + static const size_t buff_size = sizeof(buff); + enum mhd_H2HuffDecodeRes res; + size_t ret_size; + + if (buff_size < enc_size * 4) + { + fflush (stdout); + fprintf (stderr, "ERROR: wrong size of the value specified for " + "the check.\n" + "The specified size: %lu\n" + "The maximum possible size: %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size / 4); + return 99; + } + if (0 == enc_size) + { + fflush (stdout); + fprintf (stderr, "ERROR: zero size of the value specified for " + "the check.\n"); + return 99; + } + + ret_size = mhd_h2_huffman_decode (enc_size, + enc, + buff_size, + buff, + &res); + + if (0 == ret_size && MHD_H2_HUFF_DEC_RES_OK == res) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + fprintf (stderr, "The function returned ZERO, but the status was not " + "set to 'OK' (actual: %s), while the size of the input data" + " is not ZERO.\n", + decode_result_to_string (res)); + return 5; + } + if (0 != ret_size && MHD_H2_HUFF_DEC_RES_OK != res) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + fprintf (stderr, "The function returned non-ZERO, but the status was " + "set to 'OK'.\n"); + return 5; + } + if (MHD_H2_HUFF_DEC_RES_NO_SPACE == res) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + fprintf (stderr, "The function set the status set to 'NO_SPACE', but " + "the buffer should be enough to hold the result.\n"); + return 6; + } + if (MHD_H2_HUFF_DEC_RES_OK == res) + { + uint8_t check_enc_buf[128]; + size_t check_enc_size; + + check_enc_size = mhd_h2_huffman_encode (ret_size, + buff, + sizeof(check_enc_buf), + check_enc_buf); + if (0 == check_enc_size) + { + fflush (stdout); + fprintf (stderr, "ERROR: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) check_enc_size); + fprintf (stderr, "However, when checking the decoded result by " + "encoding it back to H2 Huffman encoding the function " + "mhd_h2_huffman_encode(%lu, %s, %lu, buffer) returned " + "ZERO, which should not happen as the output " + "buffer is large enough.\n", + (unsigned long) ret_size, + print_mixed (ret_size, buff), + (unsigned long) sizeof(check_enc_buf)); + return 99; + } + if ((enc_size != check_enc_size) || + (0 != memcmp (enc, check_enc_buf, check_enc_size))) + { + fflush (stdout); + fprintf (stderr, "FAILED: mhd_h2_huffman_decode(%lu, encoded_data, " + "%lu, %s%s, ->%s) returned %lu\n", + (unsigned long) enc_size, + (unsigned long) buff_size, + (0 == ret_size) ? "buffer" : "->", + (0 == ret_size) ? "" : print_mixed (ret_size, buff), + decode_result_to_string (res), + (unsigned long) ret_size); + if (enc_size != check_enc_size) + fprintf (stderr, "However, when checking the decoded result by " + "encoding it back to H2 Huffman encoding the function " + "mhd_h2_huffman_encode(%lu, %s, %lu, buffer) returned " + "%lu, which does not match the original data size.\n", + (unsigned long) ret_size, + print_mixed (ret_size, buff), + (unsigned long) sizeof(check_enc_buf), + (unsigned long) check_enc_size); + else + fprintf (stderr, "However, when checking the decoded result by " + "encoding it back to H2 Huffman encoding the function " + "mhd_h2_huffman_encode(%lu, %s, %lu, buffer) returned " + "%lu (as expected), but the encoded data does not match " + "original encoded data.\n", + (unsigned long) ret_size, + print_mixed (ret_size, buff), + (unsigned long) sizeof(check_enc_buf), + (unsigned long) ret_size); + + fprintf (stderr, "The decoded string: %s\n", + print_mixed (ret_size, buff)); + fprintf (stderr, "The original encoded data is: "); + print_hex (stderr, enc_size, enc); + fprintf (stderr, "\n" + "The string encoded back is: "); + print_hex (stderr, check_enc_size, check_enc_buf); + fprintf (stderr, "\n"); + + return 7; + } + *valid_codes_counter += 1; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_all_one_byte_values (void) +{ + static const uint_fast32_t known_valid_codes = 74u; + uint_fast32_t valid_codes_counter; + uint_fast16_t i; + + valid_codes_counter = 0; + i = 0; + do + { + uint8_t test_data[1]; + int res; + test_data[0] = (uint8_t) i; + + res = perform_single_dec_unknow_seq_check (sizeof(test_data), + test_data, + &valid_codes_counter); + + if (0 != res) + return res; + } while (0xFFu > i++); + + if (known_valid_codes != valid_codes_counter) + { + fflush (stdout); + fprintf (stderr, "The check found %lu valid code values, while" + "the expected number of valid code values is %lu.\n", + (unsigned long) valid_codes_counter, + (unsigned long) known_valid_codes); + return 9; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_all_two_bytes_values (void) +{ + static const uint_fast32_t known_valid_codes = 14717u; + uint_fast32_t valid_codes_counter; + uint_fast16_t i; + + valid_codes_counter = 0; + i = 0; + do + { + uint8_t test_data[2]; + int res; + test_data[0] = (uint8_t) i & 0xFFu; + test_data[1] = (uint8_t) ((i & 0xFF00u) >> 8u); + + res = perform_single_dec_unknow_seq_check (sizeof(test_data), + test_data, + &valid_codes_counter); + + if (0 != res) + return res; + } while (0xFFFFu > i++); + + if (known_valid_codes != valid_codes_counter) + { + fflush (stdout); + fprintf (stderr, "The check found %lu valid code values, while " + "the expected number of valid code values is %lu.\n", + (unsigned long) valid_codes_counter, + (unsigned long) known_valid_codes); + return 9; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_all_three_bytes_values (void) +{ + static const uint_fast32_t known_valid_codes = 6759631u; + static const uint_fast32_t one_pct_step = 0xFFFFFFu / 100u; + uint_fast32_t valid_codes_counter; + uint_fast32_t i; + + valid_codes_counter = 0; + i = 0; + printf ("Completed: 00%% "); + fflush (stdout); + do + { + uint8_t test_data[3]; + int res; + test_data[0] = (uint8_t) i & 0xFFu; + test_data[1] = (uint8_t) ((i & 0xFF00u) >> 8u); + test_data[2] = (uint8_t) ((i & 0xFF0000u) >> 16u); + + res = perform_single_dec_unknow_seq_check (sizeof(test_data), + test_data, + &valid_codes_counter); + + if (0 != res) + return res; + + /* Imperfect calculations, but enough for tracking the progress */ + if (0u == (i % one_pct_step)) + { + printf ("\b\b\b\b%02u%% ", (unsigned int) (i / one_pct_step)); + fflush (stdout); + } + } while (0xFFFFFFu > i++); + printf ("\n"); + + if (known_valid_codes != valid_codes_counter) + { + fflush (stdout); + fprintf (stderr, "The check found %lu valid code values, while " + "the expected number of valid code values is %lu.\n", + (unsigned long) valid_codes_counter, + (unsigned long) known_valid_codes); + return 9; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +check_h2_h_decode_all_four_bytes_values (void) +{ + static const uint_fast32_t known_valid_codes = 1395134146u; + static const uint_fast32_t one_pct_step = 0xFFFFFFFFu / 100u; + uint_fast32_t valid_codes_counter; + uint_fast32_t i; + + valid_codes_counter = 0; + i = 0; + printf ("Completed: 00%% "); + fflush (stdout); + do + { + uint8_t test_data[4]; + int res; + test_data[0] = (uint8_t) i & 0xFFu; + test_data[1] = (uint8_t) ((i & 0xFF00u) >> 8u); + test_data[2] = (uint8_t) ((i & 0xFF0000u) >> 16u); + test_data[3] = (uint8_t) ((i & 0xFF000000u) >> 24u); + + res = perform_single_dec_unknow_seq_check (sizeof(test_data), + test_data, + &valid_codes_counter); + + if (0 != res) + return res; + + /* Imperfect calculations, but enough for tracking the progress */ + if (0u == (i % one_pct_step)) + { + printf ("\b\b\b\b%02u%% ", (unsigned int) (i / one_pct_step)); + fflush (stdout); + } + } while (0xFFFFFFFFu > i++); + printf ("\n"); + + if (known_valid_codes != valid_codes_counter) + { + fflush (stdout); + fprintf (stderr, "The check found %lu valid code values, while " + "the expected number of valid code values is %lu.\n", + (unsigned long) valid_codes_counter, + (unsigned long) known_valid_codes); + return 9; + } + + return 0; +} + + +/* non-zero error code on error, zero on success */ +static int +test_decode (void) +{ + int res; + + mhd_h2_huffman_init (); + + printf ("Checking H2 Huffman decoding with" + " precise output buffer size...\n"); + res = check_h2_h_decode_perfect_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " larger output buffer size...\n"); + res = check_h2_h_decode_larger_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " smaller output buffer size...\n"); + res = check_h2_h_decode_smaller_size (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " various fixed output buffer sizes...\n"); + res = check_h2_h_decode_fixed_sizes (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " all one byte values...\n"); + res = check_h2_h_decode_all_one_byte_values (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " all combinations of two bytes values...\n"); + res = check_h2_h_decode_all_two_bytes_values (); + if (0 != res) + return res; + printf ("Succeed\n"); + + printf ("Checking H2 Huffman decoding with" + " all combinations of three bytes values...\n"); + printf ("This check may take up to a minute.\n"); + res = check_h2_h_decode_all_three_bytes_values (); + if (0 != res) + return res; + printf ("Succeed\n"); + + if (enable_deep_tests) + { + printf ("Checking H2 Huffman decoding with" + " all combinations of four bytes values...\n"); + printf ("This check may take between 1 minute and 2 hours.\n"); + res = check_h2_h_decode_all_four_bytes_values (); + if (0 != res) + return res; + printf ("Succeed\n"); + } + else + { + printf ("Skipping checking H2 Huffman decoding with" + " all combinations of four bytes values as deep tests " + "are not enabled.\n"); + } + + return 0; +} + + +int +main (int argc, + char *const *argv) +{ + if (argc < 1) + return 99; + + if (! enable_deep_tests) + enable_deep_tests = mhdt_has_param (argc, argv, "--deep"); + + if (0 != basic_data_check ()) + return 99; + + if (mhdt_has_in_name (argv[0], "_decode")) + return test_decode (); + + return test_encode (); + +}