libmicrohttpd2

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

mhd_hpack_codec.c (245605B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2025 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/h2/hpack/mhd_hpack_codec.c
     41  * @brief  The implementation of the HPACK header-compression codec functions.
     42  * @author Karlson2k (Evgeny Grin)
     43  *
     44  * The sizes of all strings are intentionally limited to 32 bits (4GiB).
     45  * The sizes of all strings in the dynamic table are limited to 32 or 16 bits,
     46  * depending on value of #mhd_HPACK_DTBL_BITS macro.
     47  */
     48 
     49 #include "mhd_sys_options.h"
     50 
     51 #include "sys_bool_type.h"
     52 #include "sys_base_types.h"
     53 #include "sys_malloc.h"
     54 #include <string.h>
     55 
     56 #include "mhd_constexpr.h"
     57 #include "mhd_align.h"
     58 
     59 #include "mhd_assert.h"
     60 #include "mhd_static_assert.h"
     61 #include "mhd_unreachable.h"
     62 #include "mhd_predict.h"
     63 
     64 #include "mhd_bithelpers.h"
     65 
     66 #include "mhd_str_types.h"
     67 #include "mhd_str_macros.h"
     68 #include "mhd_buffer.h"
     69 
     70 #include "mhd_tristate.h"
     71 #include "mhd_hpack_dec_types.h"
     72 #include "mhd_hpack_enc_types.h"
     73 
     74 #if !defined(mhd_HPACK_TESTING_TABLES_ONLY) || !defined(MHD_UNIT_TESTING)
     75 #  include "h2_huffman_codec.h"
     76 #  include "h2_huffman_est.h"
     77 #endif
     78 
     79 #include "mhd_hpack_codec.h"
     80 
     81 
     82 /**
     83  * Number of entries in the static table
     84  */
     85 #define mhd_HPACK_STBL_ENTRIES          (61u)
     86 
     87 /**
     88  * The last HPACK index number in the static table
     89  */
     90 #define mhd_HPACK_STBL_LAST_IDX         mhd_HPACK_STBL_ENTRIES
     91 
     92 
     93 /* ****** ----------------- Dynamic table handling ----------------- ****** */
     94 
     95 /* ========================================================================
     96  *
     97  *  The dynamic tables should be accessed only by mhd_* functions.
     98  *
     99  *  All functions prefixed with dtbl_* are internal helpers and should not
    100  *  be used directly.
    101  *
    102  * ========================================================================
    103  */
    104 
    105 #if mhd_HPACK_DTBL_BITS == 32
    106 /**
    107  * A type used to store sizes of dynamic table elements.
    108  *
    109  * This is a compact type; it uses the minimal amount of memory.
    110  */
    111 typedef uint_least32_t dtbl_size_t;
    112 /**
    113  * A type used to operate on sizes of dynamic and static table elements
    114  *
    115  * This type should be more friendly for faster processing by CPU.
    116  * It could be the same underlying type as @a dtbl_size_t
    117  */
    118 typedef uint_fast32_t dtbl_size_ft;
    119 /**
    120  * A type used to store the number of dynamic and static table elements
    121  *
    122  * This is a compact type; it uses the minimal amount of memory.
    123  */
    124 typedef uint_least32_t dtbl_idx_t;
    125 /**
    126  * A type used to operate and address dynamic and static table elements
    127  *
    128  * This type should be more friendly for faster processing by CPU.
    129  * It could be the same underlying type as @a dtbl_idx_t
    130  */
    131 typedef uint_fast32_t dtbl_idx_ft;
    132 /**
    133  * Check whether value @a val fits 32 bits type.
    134  *
    135  * If any non-zero bit is set above the lowest 32 bits, the macro returns
    136  * boolean false.
    137  *
    138  * This macro strictly checks whether the provided value is suitable for use
    139  * in dynamic table elements. Even if the underlying type uint_least32_t is
    140  * wider than 32 bits, this macro enforces the limit to 32 bits only.
    141  *
    142  * This macro is designed to work only with unsigned types. No signed types
    143  * are used in dynamic table data.
    144  *
    145  * The parameter is evaluated only once.
    146  */
    147 #  define mhd_DTBL_VALUE_FITS(val)      (0xFFFFFFFFu == ((val) | 0xFFFFFFFFu))
    148 #elif mhd_HPACK_DTBL_BITS == 16
    149 /**
    150  * A type used to store sizes of dynamic table elements.
    151  *
    152  * This is a compact type; it uses the minimal amount of memory.
    153  */
    154 typedef uint_least16_t dtbl_size_t;
    155 /**
    156  * A type used to operate sizes of dynamic and static table elements
    157  *
    158  * This type should be more friendly for faster processing by CPU.
    159  * It could be the same underlying type as @a dtbl_size_t
    160  */
    161 typedef uint_fast16_t dtbl_size_ft;
    162 /**
    163  * A type used to store the number of dynamic and static table elements
    164  *
    165  * This is a compact type; it uses the minimal amount of memory.
    166  */
    167 typedef uint_least16_t dtbl_idx_t;
    168 /**
    169  * A type used to operate and address dynamic and static table elements
    170  *
    171  * This type should be more friendly for faster processing by CPU.
    172  * It could be the same underlying type as @a dtbl_idx_t
    173  */
    174 typedef uint_fast16_t dtbl_idx_ft;
    175 /**
    176  * Check whether value @a val fits 16 bits type.
    177  *
    178  * If any non-zero bit is set above the lowest 16 bits, the macro returns
    179  * boolean false.
    180  *
    181  * This macro strictly checks whether the provided value is suitable for use
    182  * in dynamic table elements. Even if the underlying type uint_least16_t is
    183  * wider than 16 bits, this macro enforces the limit to 16 bits only.
    184  *
    185  * This macro is designed to work only with unsigned types. No signed types
    186  * are used in dynamic table data.
    187  *
    188  * The parameter is evaluated only once.
    189  */
    190 #  define mhd_DTBL_VALUE_FITS(val)      (0xFFFFu == ((val) | 0xFFFFu))
    191 #else
    192 #  error Unsupported mhd_HPACK_DTBL_BITS value
    193 #endif
    194 
    195 
    196 /**
    197  * The data for a dynamic table entry
    198  */
    199 struct mhd_HpackDTblEntryInfo
    200 {
    201   /**
    202    * The offset of the name in the buffer
    203    */
    204   dtbl_size_t offset;
    205   /**
    206    * The length of the name string.
    207    * The name string is not zero-terminated.
    208    */
    209   dtbl_size_t name_len;
    210   /**
    211    * The length of the value string.
    212    * The value is located at @a offset + @a name_len.
    213    * The value string is not zero-terminated.
    214    */
    215   dtbl_size_t val_len;
    216 };
    217 
    218 /**
    219  * Size (in bytes) of one dynamic-table entry-information record.
    220  */
    221 #define mhd_DTBL_ENTRY_INFO_SIZE \
    222         ((dtbl_size_t) (sizeof(struct mhd_HpackDTblEntryInfo)))
    223 
    224 /**
    225  * HPACK dynamic-table per-entry overhead, in bytes (RFC 7541 4.1).
    226  *
    227  * The macro is needed to statically initialise mhd_dtbl_entry_slack
    228  * in C11 mode (as 'static const' variable).
    229  */
    230 #define mhd_HPACK_ENTRY_OVERHEAD (32u)
    231 
    232 /**
    233  * HPACK dynamic-table per-entry overhead, in bytes (RFC 7541 4.1).
    234  * The size of a dynamic-table entry is:
    235  *   32 + length(header field name) + length(header field value),
    236  * where both lengths are in bytes as defined in RFC 7541 5.2.
    237  */
    238 mhd_constexpr dtbl_size_t mhd_dtbl_entry_overhead =
    239   mhd_HPACK_ENTRY_OVERHEAD;
    240 
    241 
    242 /**
    243  * The extra slack between entries in the strings buffer.
    244  * Used when there is extra space while adding a new entry.
    245  * This extra slack reduces the need to move strings in the buffer when the
    246  * entry is evicted and the strings are replaced with the new entry's strings.
    247  *
    248  * If strings are placed optimally (with this slack), then one entry took
    249  * exactly the formal HPACK size in the buffer (strings + entry information
    250  * data).
    251  */
    252 mhd_constexpr dtbl_size_t mhd_dtbl_entry_slack =
    253   mhd_HPACK_ENTRY_OVERHEAD - mhd_DTBL_ENTRY_INFO_SIZE;
    254 
    255 /**
    256  * The first HPACK index in the dynamic table
    257  */
    258 mhd_constexpr dtbl_idx_t mhd_dtbl_hpack_idx_offset =
    259   mhd_HPACK_STBL_LAST_IDX + 1u;
    260 
    261 /**
    262  * The maximum possible HPACK index when largest possible size of the dynamic
    263  * table is used
    264  */
    265 #define mhd_HPACK_MAX_POSSIBLE_IDX \
    266         ((mhd_DTBL_MAX_SIZE / mhd_HPACK_ENTRY_OVERHEAD) \
    267          + mhd_HPACK_STBL_LAST_IDX)
    268 
    269 /**
    270  * Get the formal HPACK size of the potential new entry.
    271  * @param strings_len the total size of the strings (the length of the name of
    272  *                    the field + the length of the value of the field)
    273  * @return the formal HPACK size of the potential new entry
    274  */
    275 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    276 dtbl_new_entry_strs_size_formal (dtbl_size_ft strings_len)
    277 {
    278   dtbl_size_ft formal_size = strings_len + mhd_dtbl_entry_overhead;
    279   mhd_assert (strings_len < formal_size);
    280   mhd_assert (mhd_DTBL_VALUE_FITS (strings_len));
    281   mhd_assert (mhd_DTBL_VALUE_FITS (formal_size));
    282   return (dtbl_size_t)formal_size;
    283 }
    284 
    285 
    286 /**
    287  * Get the formal HPACK size of the potential new entry.
    288  * @param name_len the length of the name of the field
    289  * @param val_len the length of the value of the field
    290  * @return the formal HPACK size of the potential new entry
    291  */
    292 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    293 dtbl_new_entry_size_formal (dtbl_size_ft name_len,
    294                             dtbl_size_ft val_len)
    295 {
    296   const dtbl_size_ft entry_strs_size = name_len + val_len;
    297   mhd_assert (val_len <= entry_strs_size);
    298   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
    299   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
    300   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
    301   return dtbl_new_entry_strs_size_formal (entry_strs_size);
    302 }
    303 
    304 
    305 /**
    306  * Get the total size of the strings of the entry.
    307  * This is the minimal size required for the entry in the strings buffer.
    308  * @param entr_inf the pointer to the entry info
    309  * @return the total size of the strings of the entry
    310  */
    311 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    312 dtbl_entr_strs_size_min (const struct mhd_HpackDTblEntryInfo *entr_inf)
    313 {
    314   return entr_inf->name_len + entr_inf->val_len;
    315 }
    316 
    317 
    318 /**
    319  * Get the total size of the strings of the entry plus standard slack size.
    320  * This is the optimal size used for the entry in the strings buffer when the
    321  * current insertion slot has enough space.
    322  * @param entr_inf the pointer to the entry info
    323  * @return the total size of the strings of the entry plus standard slack size
    324  */
    325 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    326 dtbl_entr_strs_size_optm (const struct mhd_HpackDTblEntryInfo *entr_inf)
    327 {
    328   return dtbl_entr_strs_size_min (entr_inf) + mhd_dtbl_entry_slack;
    329 }
    330 
    331 
    332 /**
    333  * Get the formal HPACK size of the entry.
    334  * The formal size of the entry is the size of the strings plus fixed
    335  * HPACK per-entry overhead.
    336  * @param entr_inf the pointer to the entry info
    337  * @return the formal HPACK size of the entry
    338  */
    339 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    340 dtbl_entr_size_formal (const struct mhd_HpackDTblEntryInfo *entr_inf)
    341 {
    342   const dtbl_size_t ret = dtbl_new_entry_size_formal (entr_inf->name_len,
    343                                                       entr_inf->val_len);
    344   mhd_assert (dtbl_entr_strs_size_min (entr_inf) + mhd_dtbl_entry_overhead == \
    345               ret);
    346   return ret;
    347 }
    348 
    349 
    350 /**
    351  * Get the position (offset) of the (inclusive) start of the entry's strings
    352  * in the strings buffer.
    353  * This points to the first byte of the entry's strings. If the entry has
    354  * zero-length strings, the pointer denotes a (possibly zero-sized) area
    355  * that may coincide with the start of the entry's slack (if any) or with
    356  * the next entry's strings start (if present).
    357  * @param entr_inf the pointer to the entry info
    358  * @return the position (offset) of the (inclusive) start of the entry's strings
    359  */
    360 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    361 dtbl_entr_strs_start (const struct mhd_HpackDTblEntryInfo *entr_inf)
    362 {
    363   return entr_inf->offset;
    364 }
    365 
    366 
    367 /**
    368  * Get the position of the (exclusive) end of the entry's strings in the
    369  * strings buffer.
    370  * This points to the next char (byte) after the strings of the entry.
    371  * @param entr_inf the pointer to the entry info
    372  * @return the position of the end of the entry's strings in the strings buffer
    373  */
    374 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    375 dtbl_entr_strs_end_min (const struct mhd_HpackDTblEntryInfo *entr_inf)
    376 {
    377   return dtbl_entr_strs_start (entr_inf) + dtbl_entr_strs_size_min (entr_inf);
    378 }
    379 
    380 
    381 /**
    382  * Get the position (offset) immediately after the standard slack following the
    383  * end of the entry's strings in the strings buffer.
    384  * This points to the preferred position of the next entry's strings.
    385  * @param entr_inf the pointer to the entry info
    386  * @return the position (offset) immediately after the standard slack
    387  */
    388 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    389 dtbl_entr_strs_end_optm (const struct mhd_HpackDTblEntryInfo *entr_inf)
    390 {
    391   return dtbl_entr_strs_start (entr_inf) + dtbl_entr_strs_size_optm (entr_inf);
    392 }
    393 
    394 
    395 /*
    396  * The dynamic HPACK table is organised as follows:
    397  * + The shared buffer is placed immediately after mhd_HpackDTblContext in
    398  *   memory.
    399  * + The buffer stores both the strings (names and values) and the entry info
    400  *   data (one mhd_HpackDTblEntryInfo per entry).
    401  * + Strings grow upward from the bottom of the buffer (lower addresses), while
    402  *   entry-info data grow downward from the top of the buffer (higher
    403  *   addresses).
    404  * + Because the buffer is shared, the same area may be used either by strings
    405  *   (few entries with large strings) or by entry info data (many entries with
    406  *   small strings).
    407  * + The topmost entry info data corresponds to the bottommost strings, and
    408  *   vice versa.
    409  * + Both regions (strings and entry info data) effectively form two circular
    410  *   buffers that dynamically share the same memory space region: the bottom
    411  *   part is strings area (filled from bottom to up) and the upper part is
    412  *   entries info data area (filled from top to down). See also "zero position"
    413  *   and the "edge entry" below.
    414  * + The table data tracks the newest entry; the new entries are added at
    415  *   higher (than the newest entry) location numbers.
    416  * + HPACK indices are counted in the opposite direction (the smallest HPACK
    417  *   index refers to the newest entry; the next entry's location number is the
    418  *   newest location minus one).
    419  * + Because the size of an entry info (sizeof(struct mhd_HpackDTblEntryInfo))
    420  *   is smaller than the mandatory HPACK per-entry overhead (32 bytes),
    421  *   strings are inserted with an additional slack when there is enough space
    422  *   before the next entry's strings.
    423  *
    424  * Terminology used below:
    425  * + "entry info" (or "entry information data") -- mhd_HpackDTblEntryInfo data.
    426  * + "zero position entry" -- the entry whose strings are at the bottom of the
    427  *   buffer and whose entry info data is at the very top of the buffer. This
    428  *   is the first entry added to an empty table.
    429  * + "edge entry" -- the entry whose strings lie above all other strings and
    430  *   whose entry info data lies below all other entry info data. Any space
    431  *   between this entry's strings and its entry info data is not used by
    432  *   other entries.
    433  * + "newest" (or latest) entry -- the most recently added entry (the
    434  *   lowest HPACK index).
    435  * + "oldest" entry -- the entry added before all other current entries; its
    436  *   strings immediately follow the newest entry's strings (or are at location
    437  *   zero if the newest entry is the edge entry). Its entry info data
    438  *   immediately precedes the newest entry's data (or is at the top if the
    439  *   newest entry is the edge entry).
    440  */
    441 
    442 /**
    443  * Dynamic HPACK table data
    444  */
    445 struct mhd_HpackDTblContext
    446 {
    447   /**
    448    * The size of the allocated buffer.
    449    * The buffer is located in memory right after this structure.
    450    */
    451   dtbl_size_t buf_alloc_size;
    452 
    453   /**
    454    * The current number of entries used
    455    */
    456   dtbl_idx_t num_entries;
    457 
    458   /**
    459    * Offset of the current newest (most recently added) entry; it also has
    460    * the lowest HPACK index.
    461    * The "next" entry (newest_pos + 1, or 0 when the newest entry is the
    462    * edge entry (newest_pos == num_entries - 1)) is the oldest entry and
    463    * is evicted first if needed.
    464    * When a new entry is added, newest_pos is incremented or wrapped to 0
    465    * (when the newest entry is at the edge and insertion wraps).
    466    */
    467   dtbl_idx_t newest_pos;
    468 
    469   /**
    470    * The cached value of the official table size (as defined by HPACK).
    471    * Used to speed up calculations. Can be re-created from entries information.
    472    */
    473   dtbl_size_t cur_size;
    474 
    475   /**
    476    * The dynamic table size limit as defined by HPACK
    477    */
    478   dtbl_size_t size_limit;
    479 };
    480 
    481 
    482 /* **** ---------- Dynamic table internal helpers -------------- **** */
    483 
    484 /* ** Basic table information ** */
    485 
    486 
    487 /**
    488  * Get the number of entries in the table
    489  * @param dyn the pointer to the dynamic table structure
    490  * @return the number of entries in the table
    491  */
    492 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    493 dtbl_get_num_entries (const struct mhd_HpackDTblContext *dyn)
    494 {
    495   return dyn->num_entries;
    496 }
    497 
    498 
    499 /**
    500  * Check whether the table is empty (no entries)
    501  * @param dyn the pointer to the dynamic table structure
    502  * @return 'true' if table has no entries,
    503  *         'false' otherwise
    504  */
    505 MHD_FN_PURE_ mhd_static_inline bool
    506 dtbl_is_empty (const struct mhd_HpackDTblContext *dyn)
    507 {
    508   mhd_assert ((0u == dyn->num_entries) == (0u == dyn->cur_size));
    509   return (0u == dtbl_get_num_entries (dyn));
    510 }
    511 
    512 
    513 /**
    514  * Get the pointer to the strings buffer
    515  * @param dyn the pointer to the dynamic table structure
    516  * @return the pointer to the strings buffer
    517  */
    518 MHD_FN_CONST_ mhd_static_inline char *
    519 dtbl_get_strs_buff (struct mhd_HpackDTblContext *dyn)
    520 {
    521   return (char *)
    522          (dyn + 1u);
    523 }
    524 
    525 
    526 /**
    527  * Get a const pointer to the strings buffer
    528  * @param dyn the pointer to the dynamic table structure
    529  * @return const pointer to the strings buffer
    530  */
    531 MHD_FN_CONST_ mhd_static_inline const char *
    532 dtbl_get_strs_buffc (const struct mhd_HpackDTblContext *dyn)
    533 {
    534   return (const char *)
    535          (dyn + 1u);
    536 }
    537 
    538 
    539 /**
    540  * Get the pointer to the top (by location in memory) dynamic table entry data.
    541  * The entries info data grow downward.
    542  * @param dyn the pointer to the dynamic table structure
    543  * @return the pointer to the top (by location in memory) entry data
    544  */
    545 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    546 dtbl_get_infos (struct mhd_HpackDTblContext *dyn)
    547 {
    548   return ((struct mhd_HpackDTblEntryInfo *)
    549           (void *)
    550           (dtbl_get_strs_buff (dyn) + dyn->buf_alloc_size)) - 1u;
    551 }
    552 
    553 
    554 /**
    555  * Get a const pointer to the top (by location in memory) dynamic table entry
    556  * data.
    557  * The entries info data grow downward.
    558  * @param dyn const pointer to the dynamic table structure
    559  * @return const pointer to the top (by location in memory) entry data
    560  */
    561 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
    562 dtbl_get_infosc (const struct mhd_HpackDTblContext *dyn)
    563 {
    564   return ((const struct mhd_HpackDTblEntryInfo *)
    565           (const void *)
    566           (dtbl_get_strs_buffc (dyn) + dyn->buf_alloc_size)) - 1u;
    567 }
    568 
    569 
    570 /**
    571  * Get the position of the entry located at the edge of the buffer.
    572  *
    573  * This is the entry with the strings located above all other strings
    574  * and the entry information data located below all other entries information
    575  * data.
    576  *
    577  * If any space is left between entry's strings data and information data, this
    578  * space is not used by other entries.
    579  *
    580  * The result is undefined if the table has no entries.
    581  * @param dyn the pointer to the dynamic table structure
    582  * @return the position of the edge entry,
    583  *         undefined if the table has no entries
    584  */
    585 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    586 dtbl_get_pos_edge (const struct mhd_HpackDTblContext *dyn)
    587 {
    588   mhd_assert (!dtbl_is_empty (dyn));
    589   mhd_assert (dyn->buf_alloc_size >=
    590               mhd_DTBL_ENTRY_INFO_SIZE * dyn->num_entries);
    591   return (dtbl_idx_t)(dyn->num_entries - 1u);
    592 }
    593 
    594 
    595 /**
    596  * Get the position of the previous entry for the specified entry position.
    597  *
    598  * This is a position of the entry previous to the specified entry position.
    599  * The returned value is one less than the specified position or wraps to the
    600  * edge position when the specified position is zero.
    601  *
    602  * The result is undefined if the table has no entries.
    603  * @param dyn the pointer to the dynamic table structure
    604  * @param loc_pos the number of location position
    605  * @return the position of the previous entry for specified entry position,
    606  *         undefined if the table has no entries
    607  */
    608 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    609 dtbl_get_pos_prev (const struct mhd_HpackDTblContext *dyn,
    610                    dtbl_idx_ft loc_pos)
    611 {
    612   mhd_assert (!dtbl_is_empty (dyn));
    613   mhd_assert (loc_pos <= dtbl_get_pos_edge (dyn));
    614 #ifdef MHD_USE_CODE_HARDENING
    615   if (0u == loc_pos)
    616     return dtbl_get_pos_edge (dyn);
    617   return (dtbl_idx_t)(loc_pos - 1u);
    618 #else  /* ! MHD_USE_CODE_HARDENING */
    619   return (dtbl_idx_t)((dyn->num_entries + loc_pos - 1u) % dyn->num_entries);
    620 #endif /* ! MHD_USE_CODE_HARDENING */
    621 }
    622 
    623 
    624 /**
    625  * Get the position of the next entry for the specified entry position.
    626  *
    627  * This is a position of the entry next to the specified entry position.
    628  * The returned value is greater by one than specified position or wraps to
    629  * zero if the specified position is edge position.
    630  *
    631  * The result is undefined if the table has no entries.
    632  * @param dyn the pointer to the dynamic table structure
    633  * @param loc_pos the number of location position
    634  * @return the position of the next entry for the specified entry position,
    635  *         undefined if the table has no entries
    636  */
    637 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    638 dtbl_get_pos_next (const struct mhd_HpackDTblContext *dyn,
    639                    dtbl_idx_ft loc_pos)
    640 {
    641   mhd_assert (!dtbl_is_empty (dyn));
    642   mhd_assert (loc_pos <= dtbl_get_pos_edge (dyn));
    643 #ifdef MHD_USE_CODE_HARDENING
    644   if (dtbl_get_pos_edge (dyn) == loc_pos)
    645     return 0u;
    646   return (dtbl_idx_t)(loc_pos + 1u);
    647 #else /* ! MHD_USE_CODE_HARDENING */
    648   return (dtbl_idx_t)((dyn->num_entries + loc_pos + 1u) % dyn->num_entries);
    649 #endif /* ! MHD_USE_CODE_HARDENING */
    650 }
    651 
    652 
    653 /**
    654  * Get the position of the newest entry.
    655  *
    656  * This is a position of the last added entry.
    657  *
    658  * The result is undefined if the table has no entries.
    659  * @param dyn the pointer to the dynamic table structure
    660  * @return the position of the newest entry,
    661  *         undefined if the table has no entries
    662  */
    663 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    664 dtbl_get_pos_newest (const struct mhd_HpackDTblContext *dyn)
    665 {
    666   mhd_assert (!dtbl_is_empty (dyn));
    667   return dyn->newest_pos;
    668 }
    669 
    670 
    671 /**
    672  * Get the position of the oldest entry.
    673  *
    674  * This is a position of the current oldest entry in the table. This entry
    675  * is evicted first if eviction is needed.
    676  *
    677  * The result is undefined if the table has no entries.
    678  * @param dyn the pointer to the dynamic table structure
    679  * @return the position of the oldest entry,
    680  *         undefined if the table has no entries
    681  */
    682 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    683 dtbl_get_pos_oldest (const struct mhd_HpackDTblContext *dyn)
    684 {
    685   return dtbl_get_pos_next (dyn,
    686                             dtbl_get_pos_newest (dyn));
    687 }
    688 
    689 
    690 /**
    691  * Convert an HPACK table index to the position number in the dynamic table.
    692  *
    693  * The result is undefined if the specified index is less than or equal to the
    694  * number of entries in the static table.
    695  * The result is undefined if the specified index is larger than the last valid
    696  * HPACK index in the table.
    697  * @param dyn the pointer to the dynamic table structure
    698  * @param hpack_idx the HPACK index of the entry
    699  * @return the position of the requested entry in the table,
    700  *         undefined if the @a hpack_idx is not valid for the table
    701  */
    702 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    703 dtbl_get_pos_from_hpack_idx (const struct mhd_HpackDTblContext *dyn,
    704                              dtbl_idx_ft hpack_idx)
    705 {
    706   dtbl_idx_ft pos_back_from_newest =
    707     (dtbl_idx_ft)(hpack_idx - mhd_dtbl_hpack_idx_offset);
    708   mhd_assert (mhd_DTBL_VALUE_FITS (hpack_idx));
    709   mhd_assert (mhd_HPACK_STBL_LAST_IDX < hpack_idx);
    710   mhd_assert (dtbl_get_num_entries (dyn) + mhd_dtbl_hpack_idx_offset > \
    711               hpack_idx);
    712 
    713 #ifdef MHD_USE_CODE_HARDENING
    714   if (dtbl_get_pos_newest (dyn) >= pos_back_from_newest)
    715     return (dtbl_idx_t)(dtbl_get_pos_newest (dyn) - pos_back_from_newest);
    716   return (dtbl_idx_t)(dtbl_get_num_entries (dyn) + dtbl_get_pos_newest (dyn)
    717                       - pos_back_from_newest);
    718 #else  /* ! MHD_USE_CODE_HARDENING */
    719   return
    720     (dtbl_idx_t)
    721     ((dtbl_get_num_entries (dyn)
    722       + dtbl_get_pos_newest (dyn) - pos_back_from_newest)
    723      % dtbl_get_num_entries (dyn));
    724 #endif /* ! MHD_USE_CODE_HARDENING */
    725 }
    726 
    727 
    728 /**
    729  * Convert a dynamic-table location position to the corresponding HPACK index.
    730  *
    731  * This is the inverse of #dtbl_get_pos_from_hpack_idx().
    732  * The returned HPACK index is strictly greater than the last index in the
    733  * static table (#mhd_HPACK_STBL_LAST_IDX).
    734  *
    735  * Behaviour is undefined if @a loc_pos is not a valid position for @a dyn.
    736  * @param dyn the pointer to the dynamic table structure
    737  * @param loc_pos the location position number (0 .. #dtbl_get_pos_edge())
    738  * @return the HPACK index corresponding to @a loc_pos
    739  *         undefined if the @a loc_pos is not valid for the table
    740  */
    741 MHD_FN_PURE_ mhd_static_inline dtbl_idx_t
    742 dtbl_get_hpack_idx_from_pos (const struct mhd_HpackDTblContext *dyn,
    743                              dtbl_idx_ft loc_pos)
    744 {
    745   mhd_assert (mhd_DTBL_VALUE_FITS (loc_pos));
    746   mhd_assert (dtbl_get_pos_edge (dyn) >= loc_pos);
    747 
    748 #ifdef MHD_USE_CODE_HARDENING
    749   if (dtbl_get_pos_newest (dyn) >= loc_pos)
    750     return (dtbl_idx_t)(dtbl_get_pos_newest (dyn) - loc_pos
    751                         + mhd_dtbl_hpack_idx_offset);
    752   return (dtbl_idx_t)(dtbl_get_num_entries (dyn) + dtbl_get_pos_newest (dyn)
    753                       - loc_pos + mhd_dtbl_hpack_idx_offset);
    754 #else  /* ! MHD_USE_CODE_HARDENING */
    755   return
    756     (dtbl_idx_t)
    757     (((dtbl_get_num_entries (dyn) + dtbl_get_pos_newest (dyn) - loc_pos))
    758      % dtbl_get_num_entries (dyn) + mhd_dtbl_hpack_idx_offset);
    759 #endif /* ! MHD_USE_CODE_HARDENING */
    760 }
    761 
    762 
    763 /**
    764  * Get the current exclusive upper bound (in bytes) for valid offsets
    765  * within the strings region.
    766 
    767  * This equals the distance from the start of the strings region to the first
    768  * byte occupied by entry-information data. As more entry information is added,
    769  * this limit decreases. For an empty table, the limit equals buf_alloc_size.
    770  * @param dyn the pointer to the dynamic table structure
    771  * @return the current exclusive upper bound for offsets in the strings region
    772  */
    773 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    774 dtbl_get_strs_ceiling (const struct mhd_HpackDTblContext *dyn)
    775 {
    776   dtbl_size_ft ceiling =
    777     dyn->buf_alloc_size
    778     - (dtbl_size_ft)mhd_DTBL_ENTRY_INFO_SIZE * dyn->num_entries;
    779 
    780   mhd_assert (dyn->buf_alloc_size >=
    781               mhd_DTBL_ENTRY_INFO_SIZE * dyn->num_entries);
    782   mhd_assert (mhd_DTBL_VALUE_FITS (ceiling));
    783 
    784   return (dtbl_size_t)ceiling;
    785 }
    786 
    787 
    788 /**
    789  * Get the formal maximum HPACK size in the table.
    790  * @param dyn the pointer to the dynamic table structure
    791  * @return the formal HPACK size in the table
    792  */
    793 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    794 dtbl_get_size_max_formal (const struct mhd_HpackDTblContext *dyn)
    795 {
    796   return dyn->size_limit;
    797 }
    798 
    799 
    800 /**
    801  * Get the amount of formal HPACK free space in the table.
    802  * @param dyn the pointer to the dynamic table structure
    803  * @return the formal HPACK free space in the table
    804  */
    805 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    806 dtbl_get_free_formal (const struct mhd_HpackDTblContext *dyn)
    807 {
    808   mhd_assert (dyn->size_limit >= dyn->cur_size);
    809   return dyn->size_limit - dyn->cur_size;
    810 }
    811 
    812 
    813 /**
    814  * Get the amount of formal HPACK used space in the table.
    815  * @param dyn the pointer to the dynamic table structure
    816  * @return the formal HPACK used space in the table
    817  */
    818 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
    819 dtbl_get_used_formal (const struct mhd_HpackDTblContext *dyn)
    820 {
    821   mhd_assert (dyn->size_limit >= dyn->cur_size);
    822   return dyn->cur_size;
    823 }
    824 
    825 
    826 /* ** Location of entry information data based on entry position in the
    827       table ** */
    828 
    829 /**
    830  * Get the pointer to the dynamic table entry by location position number.
    831  * This is not the same as HPACK index.
    832  * The result is undefined if the table has no entries.
    833  * @param dyn the pointer to the dynamic table structure
    834  * @param loc_pos the number of location position
    835  * @return the pointer to the dynamic table entry,
    836  *         undefined if the table has no entries
    837  */
    838 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    839 dtbl_pos_entry_info (struct mhd_HpackDTblContext *dyn,
    840                      dtbl_idx_ft loc_pos)
    841 {
    842   mhd_assert (mhd_DTBL_VALUE_FITS (loc_pos));
    843   mhd_assert (!dtbl_is_empty (dyn));
    844   mhd_assert (dyn->num_entries > loc_pos);
    845   mhd_assert (dyn->buf_alloc_size >=
    846               mhd_DTBL_ENTRY_INFO_SIZE * dyn->num_entries);
    847   return dtbl_get_infos (dyn) - loc_pos;
    848 }
    849 
    850 
    851 /**
    852  * Get a const pointer to the dynamic table entry by location position number.
    853  * This is not the same as HPACK index.
    854  * The result is undefined if the table has no entries.
    855  * @param dyn const pointer to the dynamic table structure
    856  * @param loc_pos the number of location position
    857  * @return the pointer to the dynamic table entry,
    858  *         undefined if the table has no entries
    859  */
    860 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
    861 dtbl_pos_entry_infoc (const struct mhd_HpackDTblContext *dyn,
    862                       dtbl_idx_ft loc_pos)
    863 {
    864   mhd_assert (mhd_DTBL_VALUE_FITS (loc_pos));
    865   mhd_assert (!dtbl_is_empty (dyn));
    866   mhd_assert (dyn->num_entries > loc_pos);
    867   mhd_assert (dyn->buf_alloc_size >=
    868               mhd_DTBL_ENTRY_INFO_SIZE * dyn->num_entries);
    869   return dtbl_get_infosc (dyn) - loc_pos;
    870 }
    871 
    872 
    873 /**
    874  * Get the pointer to the zero location entry information data.
    875  * This is the highest address of the entries data location in the table.
    876  * The result is undefined if the table has no entries.
    877  * @param dyn the pointer to the dynamic table structure
    878  * @return the pointer to the zero location entry info data,
    879  *         undefined if the table has no entries
    880  */
    881 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    882 dtbl_zero_entry_info (struct mhd_HpackDTblContext *dyn)
    883 {
    884   return dtbl_pos_entry_info (dyn,
    885                               0u);
    886 }
    887 
    888 
    889 /**
    890  * Get a const pointer to the zero location entry information data.
    891  * This is the highest address of the entries data location in the table.
    892  * The result is undefined if the table has no entries.
    893  * @param dyn the pointer to the dynamic table structure
    894  * @return const pointer to the zero location entry information data,
    895  *         undefined if the table has no entries
    896  */
    897 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
    898 dtbl_zero_entry_infoc (const struct mhd_HpackDTblContext *dyn)
    899 {
    900   return dtbl_pos_entry_infoc (dyn,
    901                                0u);
    902 }
    903 
    904 
    905 /**
    906  * Get the pointer to the table's edge entry information data.
    907  * This is the lowest address of the entries data location in the table.
    908  * The result is undefined if the table has no entries.
    909  * @param dyn the pointer to the dynamic table structure
    910  * @return the pointer to the table's edge entry information data,
    911  *         undefined if the table has no entries
    912  */
    913 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    914 dtbl_edge_entry_info (struct mhd_HpackDTblContext *dyn)
    915 {
    916   struct mhd_HpackDTblEntryInfo *const ptr =
    917     dtbl_pos_entry_info (dyn,
    918                          dtbl_get_pos_edge (dyn));
    919   mhd_assert (((const void *)ptr) == \
    920               ((const void *)(dtbl_get_strs_buffc (dyn)
    921                               + dtbl_get_strs_ceiling (dyn))));
    922   return ptr;
    923 }
    924 
    925 
    926 /**
    927  * Get a const pointer to the table edge entry information data.
    928  * This is the lowest address of the entries data location in the table.
    929  * The result is undefined if the table has no entries.
    930  * @param dyn the pointer to the dynamic table structure
    931  * @return const pointer to the table edge entry information data,
    932  *         undefined if the table has no entries
    933  */
    934 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
    935 dtbl_edge_entry_infoc (const struct mhd_HpackDTblContext *dyn)
    936 {
    937   const struct mhd_HpackDTblEntryInfo *const ptr =
    938     dtbl_pos_entry_infoc (dyn,
    939                           dtbl_get_pos_edge (dyn));
    940   mhd_assert (((const void *)ptr) == \
    941               ((const void *)(dtbl_get_strs_buffc (dyn)
    942                               + dtbl_get_strs_ceiling (dyn))));
    943   return ptr;
    944 }
    945 
    946 
    947 /**
    948  * Get the pointer to the newest entry information data.
    949  * The result is undefined if the table has no entries.
    950  * @param dyn the pointer to the dynamic table structure
    951  * @return the pointer to the newest entry information data,
    952  *         undefined if the table has no entries
    953  */
    954 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    955 dtbl_newest_entry_info (struct mhd_HpackDTblContext *dyn)
    956 {
    957   return dtbl_pos_entry_info (dyn,
    958                               dtbl_get_pos_newest (dyn));
    959 }
    960 
    961 
    962 /**
    963  * Get a const pointer to the newest entry information data.
    964  * The result is undefined if the table has no entries.
    965  * @param dyn const pointer to the dynamic table structure
    966  * @return const pointer to the newest entry information data,
    967  *         undefined if the table has no entries
    968  */
    969 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
    970 dtbl_newest_entry_infoc (const struct mhd_HpackDTblContext *dyn)
    971 {
    972   return dtbl_pos_entry_infoc (dyn,
    973                                dtbl_get_pos_newest (dyn));
    974 }
    975 
    976 
    977 /**
    978  * Get the pointer to the oldest entry information data.
    979  * The result is undefined if the table has no entries.
    980  * @param dyn the pointer to the dynamic table structure
    981  * @return the pointer to the oldest entry information data,
    982  *         undefined if the table has no entries
    983  */
    984 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
    985 dtbl_oldest_entry_info (struct mhd_HpackDTblContext *dyn)
    986 {
    987   return dtbl_pos_entry_info (dyn,
    988                               dtbl_get_pos_oldest (dyn));
    989 }
    990 
    991 
    992 /**
    993  * Get a const pointer to the oldest entry information data.
    994  * The result is undefined if the table has no entries.
    995  * @param dyn const pointer to the dynamic table structure
    996  * @return const pointer to the oldest entry information data,
    997  *         undefined if the table has no entries
    998  */
    999 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
   1000 dtbl_oldest_entry_infoc (const struct mhd_HpackDTblContext *dyn)
   1001 {
   1002   return dtbl_pos_entry_infoc (dyn,
   1003                                dtbl_get_pos_oldest (dyn));
   1004 }
   1005 
   1006 
   1007 /* ** Entries strings information based on the entry position in the table ** */
   1008 
   1009 /**
   1010  * Get the total size of the strings of the entry.
   1011  * This is the minimal size required for the entry in the strings buffer.
   1012  * @param dyn the pointer to the dynamic table structure
   1013  * @param loc_pos the number of location position
   1014  * @return the total size of the strings of the entry
   1015  */
   1016 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1017 dtbl_pos_strs_size_min (const struct mhd_HpackDTblContext *dyn,
   1018                         dtbl_idx_ft loc_pos)
   1019 {
   1020   return dtbl_entr_strs_size_min (dtbl_pos_entry_infoc (dyn,
   1021                                                         loc_pos));
   1022 }
   1023 
   1024 
   1025 /**
   1026  * Get the total size of the strings of the entry plus standard slack size.
   1027  * This is the optimal size used for the entry in the strings buffer when the
   1028  * current insertion slot has enough space.
   1029  * @param dyn the pointer to the dynamic table structure
   1030  * @param loc_pos the number of location position
   1031  * @return the total size of the strings of the entry plus standard slack size
   1032  */
   1033 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1034 dtbl_pos_strs_size_optm (const struct mhd_HpackDTblContext *dyn,
   1035                          dtbl_idx_ft loc_pos)
   1036 {
   1037   return dtbl_entr_strs_size_optm (dtbl_pos_entry_infoc (dyn,
   1038                                                          loc_pos));
   1039 }
   1040 
   1041 
   1042 /**
   1043  * Get the formal HPACK size of the entry.
   1044  * The formal size of the entry is the size of the strings plus fixed
   1045  * HPACK per-entry overhead.
   1046  * @param dyn the pointer to the dynamic table structure
   1047  * @param loc_pos the number of location position
   1048  * @return the formal HPACK size of the entry
   1049  */
   1050 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1051 dtbl_pos_size_formal (const struct mhd_HpackDTblContext *dyn,
   1052                       dtbl_idx_ft loc_pos)
   1053 {
   1054   return dtbl_entr_size_formal (dtbl_pos_entry_infoc (dyn,
   1055                                                       loc_pos));
   1056 }
   1057 
   1058 
   1059 /**
   1060  * Get the position (offset) of the (inclusive) start of the entry's strings
   1061  * in the strings buffer.
   1062  * This points to the first byte of the entry's strings. If the entry has
   1063  * zero-length strings, the pointer denotes a (possibly zero-sized) area
   1064  * that may coincide with the start of the entry's slack (if any) or with
   1065  * the next entry's strings start (if present).
   1066  * @param dyn the pointer to the dynamic table structure
   1067  * @param loc_pos the number of location position
   1068  * @return the position (offset) of the (inclusive) start of the entry's strings
   1069  */
   1070 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1071 dtbl_pos_strs_start (const struct mhd_HpackDTblContext *dyn,
   1072                      dtbl_idx_ft loc_pos)
   1073 {
   1074   return dtbl_entr_strs_start (dtbl_pos_entry_infoc (dyn,
   1075                                                      loc_pos));
   1076 }
   1077 
   1078 
   1079 /**
   1080  * Get the position of the (exclusive) end of the entry's strings in the
   1081  * strings buffer.
   1082  * This points to the next char (byte) after the strings of the entry.
   1083  * @param dyn the pointer to the dynamic table structure
   1084  * @param loc_pos the number of location position
   1085  * @return the position of the end of the entry's strings in the strings buffer
   1086  */
   1087 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1088 dtbl_pos_strs_end_min (const struct mhd_HpackDTblContext *dyn,
   1089                        dtbl_idx_ft loc_pos)
   1090 {
   1091   return dtbl_entr_strs_end_min (dtbl_pos_entry_infoc (dyn,
   1092                                                        loc_pos));
   1093 }
   1094 
   1095 
   1096 /**
   1097  * Get the position after standard slack after the end of the entry's strings
   1098  * in the strings buffer.
   1099  * This points to the preferred position of the next entry's strings.
   1100  * @param dyn the pointer to the dynamic table structure
   1101  * @param loc_pos the number of location position
   1102  * @return the position of the end of the entry's strings in the strings buffer
   1103  */
   1104 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1105 dtbl_pos_strs_end_optm (const struct mhd_HpackDTblContext *dyn,
   1106                         dtbl_idx_ft loc_pos)
   1107 {
   1108   return dtbl_entr_strs_end_optm (dtbl_pos_entry_infoc (dyn,
   1109                                                         loc_pos));
   1110 }
   1111 
   1112 
   1113 /* ** Entries strings location information based on the pointer to the
   1114       entry ** */
   1115 
   1116 /**
   1117  * Get a pointer to the (inclusive) start of the entry's strings in the
   1118  * strings buffer.
   1119  * This points to the first byte of the entry's strings. If the entry has
   1120  * zero-length strings, the pointer denotes a (possibly zero-sized) area
   1121  * that may coincide with the start of the entry's slack (if any) or with
   1122  * the next entry's strings start (if present).
   1123  * The result is undefined if the entry is not in the table.
   1124  * @param dyn the pointer to the dynamic table structure
   1125  * @param entr_inf the pointer to the entry information
   1126  * @return the pointer of the (inclusive) start of the entry's strings,
   1127  *         result is undefined if the entry is not in the table
   1128  */
   1129 MHD_FN_PURE_ mhd_static_inline char *
   1130 dtbl_entr_strs_ptr_start (struct mhd_HpackDTblContext *dyn,
   1131                           const struct mhd_HpackDTblEntryInfo *entr_inf)
   1132 {
   1133   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1134   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1135   return dtbl_get_strs_buff (dyn) + dtbl_entr_strs_start (entr_inf);
   1136 }
   1137 
   1138 
   1139 /**
   1140  * Get const pointer to the (inclusive) start of the entry's strings in the
   1141  * strings buffer.
   1142  * This points to the first byte of the entry's strings. If the entry has
   1143  * zero-length strings, the pointer denotes a (possibly zero-sized) area
   1144  * that may coincide with the start of the entry's slack (if any) or with
   1145  * the next entry's strings start (if present).
   1146  * The result is undefined if the entry is not in the table.
   1147  * @param dyn the pointer to the dynamic table structure
   1148  * @param entr_inf the pointer to the entry information
   1149  * @return const pointer of the (inclusive) start of the entry's strings,
   1150  *         result is undefined if the entry is not in the table
   1151  */
   1152 MHD_FN_PURE_ mhd_static_inline const char *
   1153 dtbl_entr_strs_ptr_startc (const struct mhd_HpackDTblContext *dyn,
   1154                            const struct mhd_HpackDTblEntryInfo *entr_inf)
   1155 {
   1156   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1157   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1158   return dtbl_get_strs_buffc (dyn) + dtbl_entr_strs_start (entr_inf);
   1159 }
   1160 
   1161 
   1162 /**
   1163  * Get a pointer to the (exclusive) end of the entry's strings in the
   1164  * strings buffer.
   1165  * This points to the next char (byte) after the strings of the entry.
   1166  * The result is undefined if the entry is not in the table.
   1167  * @param dyn the pointer to the dynamic table structure
   1168  * @param entr_inf the pointer to the entry information
   1169  * @return the pointer to the (exclusive) end of the entry's strings,
   1170  *         result is undefined if the entry is not in the table
   1171  */
   1172 MHD_FN_PURE_ mhd_static_inline char *
   1173 dtbl_entr_strs_ptr_end (struct mhd_HpackDTblContext *dyn,
   1174                         const struct mhd_HpackDTblEntryInfo *entr_inf)
   1175 {
   1176   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1177   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1178   return dtbl_get_strs_buff (dyn) + dtbl_entr_strs_end_min (entr_inf);
   1179 }
   1180 
   1181 
   1182 /**
   1183  * Get a const pointer to the (exclusive) end of the entry's strings in the
   1184  * strings buffer.
   1185  * This points to the next char (byte) after the strings of the entry.
   1186  * The result is undefined if the entry is not in the table.
   1187  * @param dyn const pointer to the dynamic table structure
   1188  * @param entr_inf const pointer to the entry information
   1189  * @return const pointer to the (exclusive) end of the entry's strings,
   1190  *         result is undefined if the entry is not in the table
   1191  */
   1192 MHD_FN_PURE_ mhd_static_inline const char *
   1193 dtbl_entr_strs_ptr_endc (const struct mhd_HpackDTblContext *dyn,
   1194                          const struct mhd_HpackDTblEntryInfo *entr_inf)
   1195 {
   1196   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1197   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1198   return dtbl_get_strs_buffc (dyn) + dtbl_entr_strs_end_min (entr_inf);
   1199 }
   1200 
   1201 
   1202 /**
   1203  * Get a const pointer to the (exclusive) end of the entry's standard slack
   1204  * after the entry's strings in the strings buffer.
   1205  * This points to the preferred location of the next entry's strings.
   1206  * The result is undefined if the entry is not in the table.
   1207  * @param dyn const pointer to the dynamic table structure
   1208  * @param entr_inf const pointer to the entry information
   1209  * @return const pointer to the (exclusive) end of the entry's standard slack,
   1210  *         result is undefined if the entry is not in the table
   1211  */
   1212 MHD_FN_PURE_ mhd_static_inline const char *
   1213 dtbl_entr_strs_ptr_end_slackc (const struct mhd_HpackDTblContext *dyn,
   1214                                const struct mhd_HpackDTblEntryInfo *entr_inf)
   1215 {
   1216   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1217   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1218   return dtbl_get_strs_buffc (dyn) + dtbl_entr_strs_end_optm (entr_inf);
   1219 }
   1220 
   1221 
   1222 /**
   1223  * Get const pointer to the entry's name.
   1224  * This points to the first byte of the entry's name. If the entry has
   1225  * zero-length name, the pointer denotes a zero-sized area.
   1226  * The result is undefined if the entry is not in the table.
   1227  * @param dyn the pointer to the dynamic table structure
   1228  * @param entr_inf the pointer to the entry information
   1229  * @return const pointer to the entry's name,
   1230  *         result is undefined if the entry is not in the table
   1231  */
   1232 MHD_FN_PURE_ mhd_static_inline const char *
   1233 dtbl_entr_strs_ptr_namec (const struct mhd_HpackDTblContext *dyn,
   1234                           const struct mhd_HpackDTblEntryInfo *entr_inf)
   1235 {
   1236   return dtbl_entr_strs_ptr_startc (dyn,
   1237                                     entr_inf);
   1238 }
   1239 
   1240 
   1241 /**
   1242  * Get const pointer to the entry's value.
   1243  * This points to the first byte of the entry's value. If the entry has
   1244  * zero-length value, the pointer denotes a zero-sized area.
   1245  * The result is undefined if the entry is not in the table.
   1246  * @param dyn the pointer to the dynamic table structure
   1247  * @param entr_inf the pointer to the entry information
   1248  * @return const pointer to the entry's value,
   1249  *         result is undefined if the entry is not in the table
   1250  */
   1251 MHD_FN_PURE_ mhd_static_inline const char *
   1252 dtbl_entr_strs_ptr_valuec (const struct mhd_HpackDTblContext *dyn,
   1253                            const struct mhd_HpackDTblEntryInfo *entr_inf)
   1254 {
   1255   return dtbl_entr_strs_ptr_startc (dyn,
   1256                                     entr_inf) + entr_inf->name_len;
   1257 }
   1258 
   1259 
   1260 /* ** Information about the entry in the table based on the pointer to
   1261       the entry ** */
   1262 
   1263 /**
   1264  * Get the size of the space between entry's strings and entry information data
   1265  * as if the provided entry were an edge entry.
   1266  * The gap could be zero in some conditions.
   1267  * The result is undefined if the entry is not in the table.
   1268  * @param dyn const pointer to the dynamic table structure
   1269  * @param entr_inf const pointer to the entry information
   1270  * @return the size of the space between entry's strings and information,
   1271  *         result is undefined if the entry is not in the table
   1272  */
   1273 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1274 dtbl_entr_as_edge_get_gap (const struct mhd_HpackDTblContext *dyn,
   1275                            const struct mhd_HpackDTblEntryInfo *entr_inf)
   1276 {
   1277   const char *upper_ptr = (const char *)entr_inf;
   1278   const char *lower_ptr = dtbl_entr_strs_ptr_endc (dyn, entr_inf);
   1279   const dtbl_size_ft gap = (dtbl_size_ft)(upper_ptr - lower_ptr);
   1280 
   1281   mhd_assert (dtbl_zero_entry_infoc (dyn) >= entr_inf);
   1282   mhd_assert (dtbl_edge_entry_infoc (dyn) <= entr_inf);
   1283   mhd_assert (lower_ptr <= upper_ptr);
   1284   mhd_assert (mhd_DTBL_VALUE_FITS (gap));
   1285   mhd_assert (gap < dyn->buf_alloc_size);
   1286 
   1287   return (dtbl_size_t)gap;
   1288 }
   1289 
   1290 
   1291 /* ** Entries strings location information based on entry position in
   1292       the table ** */
   1293 
   1294 /**
   1295  * Get a pointer to the (inclusive) start of the entry's strings in the
   1296  * strings buffer.
   1297  * This points to the first char (byte) of the entry's strings. If the entry
   1298  * has zero-length strings then this points to the first byte of entry slack
   1299  * (if any) or the first char of the next entry's strings (if any).
   1300  * The result is undefined if the location number is equal to or greater than
   1301  * the number of entries in the table.
   1302  * @param dyn the pointer to the dynamic table structure
   1303  * @param loc_pos the number of location position
   1304  * @return the pointer to the (inclusive) start of the entry's strings
   1305  */
   1306 MHD_FN_PURE_ mhd_static_inline char *
   1307 dtbl_pos_strs_ptr_start (struct mhd_HpackDTblContext *dyn,
   1308                          dtbl_idx_ft loc_pos)
   1309 {
   1310   return dtbl_entr_strs_ptr_start (dyn,
   1311                                    dtbl_pos_entry_info (dyn,
   1312                                                         loc_pos));
   1313 }
   1314 
   1315 
   1316 /**
   1317  * Get a const pointer to the (inclusive) start of the entry's strings in the
   1318  * strings buffer.
   1319  * This points to the first char (byte) of the entry's strings. If the entry
   1320  * has zero-length strings then this points to the first byte of entry slack
   1321  * (if any) or the first char of the next entry's strings (if any).
   1322  * The result is undefined if the location number is equal to or greater than
   1323  * the number of entries in the table.
   1324  * @param dyn const pointer to the dynamic table structure
   1325  * @param loc_pos the number of location position
   1326  * @return const pointer to the (inclusive) start of the entry's strings,
   1327  *         result is undefined if the entry is not in the table
   1328  */
   1329 MHD_FN_PURE_ mhd_static_inline const char *
   1330 dtbl_pos_strs_ptr_startc (const struct mhd_HpackDTblContext *dyn,
   1331                           dtbl_idx_ft loc_pos)
   1332 {
   1333   return dtbl_entr_strs_ptr_startc (dyn,
   1334                                     dtbl_pos_entry_infoc (dyn,
   1335                                                           loc_pos));
   1336 }
   1337 
   1338 
   1339 /**
   1340  * Get a pointer to the (exclusive) end of the entry's strings in the
   1341  * strings buffer.
   1342  * This points to the next char (byte) after the strings of the entry.
   1343  * The result is undefined if the location number is equal or greater than the
   1344  * number of entries in the table.
   1345  * @param dyn the pointer to the dynamic table structure
   1346  * @param loc_pos the number of location position
   1347  * @return the pointer to the (exclusive) end of the entry's strings
   1348  */
   1349 MHD_FN_PURE_ mhd_static_inline char *
   1350 dtbl_pos_strs_ptr_end (struct mhd_HpackDTblContext *dyn,
   1351                        dtbl_idx_ft loc_pos)
   1352 {
   1353   return dtbl_entr_strs_ptr_end (dyn,
   1354                                  dtbl_pos_entry_info (dyn,
   1355                                                       loc_pos));
   1356 }
   1357 
   1358 
   1359 /**
   1360  * Get a const pointer to the (exclusive) end of the entry's strings in the
   1361  * strings buffer.
   1362  * This points to the next char (byte) after the strings of the entry.
   1363  * The result is undefined if the location number is equal or greater than the
   1364  * number of entries in the table.
   1365  * @param dyn const pointer to the dynamic table structure
   1366  * @param loc_pos the number of location position
   1367  * @return const pointer to the (exclusive) end of the entry's strings
   1368  */
   1369 MHD_FN_PURE_ mhd_static_inline const char *
   1370 dtbl_pos_strs_ptr_endc (const struct mhd_HpackDTblContext *dyn,
   1371                         dtbl_idx_ft loc_pos)
   1372 {
   1373   return dtbl_entr_strs_ptr_endc (dyn,
   1374                                   dtbl_pos_entry_infoc (dyn,
   1375                                                         loc_pos));
   1376 }
   1377 
   1378 
   1379 /**
   1380  * Get a const pointer to the (exclusive) end of the entry's standard slack
   1381  * after the entry's strings in the strings buffer.
   1382  * This points to the preferred location of the next entry's strings.
   1383  * The result is undefined if the location number is equal or greater than the
   1384  * number of entries in the table.
   1385  * @param dyn const pointer to the dynamic table structure
   1386  * @param loc_pos the number of location position
   1387  * @return const pointer to the (exclusive) end of the entry's standard slack
   1388  */
   1389 MHD_FN_PURE_ mhd_static_inline const char *
   1390 dtbl_pos_strs_ptr_end_slackc (const struct mhd_HpackDTblContext *dyn,
   1391                               dtbl_idx_ft loc_pos)
   1392 {
   1393   return dtbl_entr_strs_ptr_end_slackc (dyn,
   1394                                         dtbl_pos_entry_infoc (dyn,
   1395                                                               loc_pos));
   1396 }
   1397 
   1398 
   1399 /* ** Information about the entry in the table based on entry position in
   1400       the table ** */
   1401 
   1402 /**
   1403  * Get the size of the space between entry's strings and entry information data
   1404  * as if the provided entry were an edge entry.
   1405  * The gap could be zero in some conditions.
   1406  * The result is undefined if the location number is equal or greater than the
   1407  * number of entries in the table.
   1408  * @param dyn const pointer to the dynamic table structure
   1409  * @param loc_pos the number of location position
   1410  * @return the size of the space between entry's strings and information
   1411  */
   1412 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1413 dtbl_pos_as_edge_get_gap (const struct mhd_HpackDTblContext *dyn,
   1414                           dtbl_idx_ft loc_pos)
   1415 {
   1416   return dtbl_entr_as_edge_get_gap (dyn,
   1417                                     dtbl_pos_entry_infoc (dyn,
   1418                                                           loc_pos));
   1419 }
   1420 
   1421 
   1422 /* ** Additional means of access to the entries information ** */
   1423 
   1424 /**
   1425  * Get table's entries information as a pointer to an array.
   1426  *
   1427  * The returned array has #dtbl_get_num_entries() elements.
   1428  * The returned pointer becomes invalid if any entry is added or evicted
   1429  * from the table.
   1430  *
   1431  * Behaviour is undefined if table is empty.
   1432  * @param dyn the pointer to the dynamic table structure
   1433  * @return table's entries information as a pointer to an array
   1434  */
   1435 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
   1436 dtbl_get_infos_as_array (struct mhd_HpackDTblContext *dyn)
   1437 {
   1438   return dtbl_edge_entry_info (dyn);
   1439 }
   1440 
   1441 
   1442 /**
   1443  * Get table's entries information as a pointer to a const array.
   1444  *
   1445  * The returned array has #dtbl_get_num_entries() elements.
   1446  *
   1447  * The first (zero index) item in the array is the edge entry, the last item
   1448  * in the array is zero position entry.
   1449  *
   1450  * The returned pointer becomes invalid if any entry is added or evicted
   1451  * from the table.
   1452  *
   1453  * Behaviour is undefined if table is empty.
   1454  * @param dyn the pointer to the dynamic table structure
   1455  * @return table's entries information as a pointer to a const array
   1456  */
   1457 MHD_FN_PURE_ mhd_static_inline const struct mhd_HpackDTblEntryInfo *
   1458 dtbl_get_infos_as_arrayc (const struct mhd_HpackDTblContext *dyn)
   1459 {
   1460   return dtbl_edge_entry_infoc (dyn);
   1461 }
   1462 
   1463 
   1464 /* ** Additional information about the table ** */
   1465 
   1466 /**
   1467  * Get the size of the free space available for new entries (including
   1468  * entry's strings, entry info data, and per-entry slack) between the
   1469  * string region and the entry-info region in the shared buffer.
   1470  *
   1471  * The gap could be zero in some conditions.
   1472 
   1473  * Unlike #dtbl_bottom_gap(), this space is used for both strings data and
   1474  * entries info data when adding new entries.
   1475  *
   1476  * This is not the formal HPACK free size.
   1477  * @param dyn const pointer to the dynamic table structure
   1478  * @return the size of the space available at the edge of the table
   1479  */
   1480 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1481 dtbl_edge_gap (const struct mhd_HpackDTblContext *dyn)
   1482 {
   1483   if (dtbl_is_empty (dyn))
   1484     return dyn->buf_alloc_size;
   1485 
   1486   return dtbl_entr_as_edge_get_gap (dyn,
   1487                                     dtbl_edge_entry_infoc (dyn));
   1488 }
   1489 
   1490 
   1491 /**
   1492  * Get the size of the free space available for strings at the bottom of
   1493  * the shared buffer.
   1494  *
   1495  * Unlike #dtbl_edge_gap(), if table is not empty, this space can be used only
   1496  * for the strings data for an entry added at zero position.
   1497  *
   1498  * @param dyn const pointer to the dynamic table structure
   1499  * @return the size of the space available at the bottom of the table
   1500  */
   1501 MHD_FN_PURE_ mhd_static_inline dtbl_size_t
   1502 dtbl_bottom_gap (const struct mhd_HpackDTblContext *dyn)
   1503 {
   1504   if (dtbl_is_empty (dyn))
   1505     return dyn->buf_alloc_size;
   1506 
   1507   return dtbl_pos_strs_start (dyn, 0u);
   1508 }
   1509 
   1510 
   1511 /* ** Manipulating strings in the dynamic table ** */
   1512 
   1513 /**
   1514  * Choose the offset of the strings in the strings buffer for a new entry
   1515  * following another entry (non-zero position).
   1516  *
   1517  * If enough space is available, up to the standard slack bytes are left
   1518  * between entries' strings.
   1519  *
   1520  * Result is undefined if @a size_of_space is less than @a entry_strs_size.
   1521  * @param space_start the offset of the start (inclusive) of free space in
   1522  *                    the buffer
   1523  * @param size_of_space the amount of free space at the @a space_start offset
   1524  * @param entry_strs_size the size of new entries strings
   1525  * @return the offset to put entries strings in the buffer
   1526  */
   1527 MHD_FN_CONST_ mhd_static_inline dtbl_size_t
   1528 dtbl_choose_strs_offset_for_size (dtbl_size_ft space_start,
   1529                                   dtbl_size_ft size_of_space,
   1530                                   dtbl_size_ft entry_strs_size)
   1531 {
   1532   const dtbl_size_ft extra_space = size_of_space - entry_strs_size;
   1533 
   1534   mhd_assert (size_of_space >= entry_strs_size);
   1535   mhd_assert (mhd_DTBL_VALUE_FITS (space_start));
   1536   mhd_assert (mhd_DTBL_VALUE_FITS (size_of_space));
   1537   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   1538 
   1539   if (mhd_dtbl_entry_slack <= extra_space)
   1540     return (dtbl_size_t)(space_start + mhd_dtbl_entry_slack);
   1541 
   1542   return (dtbl_size_t)(space_start + extra_space);
   1543 }
   1544 
   1545 
   1546 /**
   1547  * Completely reset dynamic table data.
   1548  * This fully removes all entries from the table, leaving the size of the table
   1549  * and the table allocation the same.
   1550  * @param dyn the pointer to the dynamic table structure
   1551  */
   1552 mhd_static_inline void
   1553 dtbl_reset (struct mhd_HpackDTblContext *dyn)
   1554 {
   1555   dyn->num_entries = 0u;
   1556   dyn->newest_pos = 0u;
   1557   dyn->cur_size = 0u;
   1558 }
   1559 
   1560 
   1561 /**
   1562  * Move selected entries' strings in the strings buffer down (to the start of
   1563  * the buffer).
   1564  * The strings are moved for all entries from @a from_pos up to the
   1565  * edge (highest number) entry.
   1566  * @param dyn the pointer to the dynamic table structure
   1567  * @param from_pos the first entry position to move strings
   1568  * @param shift_down_size the amount of bytes to shift
   1569  */
   1570 static void
   1571 dtbl_move_strs_down (struct mhd_HpackDTblContext *dyn,
   1572                      dtbl_idx_ft from_pos,
   1573                      dtbl_size_ft shift_down_size)
   1574 {
   1575   char *move_area_src = dtbl_pos_strs_ptr_start (dyn,
   1576                                                  from_pos);
   1577   size_t move_area_size =
   1578     (size_t)
   1579     (dtbl_pos_strs_ptr_endc (dyn,
   1580                              dtbl_get_pos_edge (dyn)) - move_area_src);
   1581   dtbl_idx_ft i;
   1582 
   1583   mhd_assert (mhd_DTBL_VALUE_FITS (from_pos));
   1584   mhd_assert (mhd_DTBL_VALUE_FITS (shift_down_size));
   1585   mhd_assert (0u != shift_down_size);
   1586   mhd_assert (dtbl_get_pos_edge (dyn) >= from_pos);
   1587   mhd_assert (shift_down_size <= dtbl_pos_strs_start (dyn, from_pos));
   1588   mhd_assert ((0u == from_pos) \
   1589               || (dtbl_pos_strs_end_min (dyn, from_pos - 1u) <= \
   1590                   dtbl_pos_strs_start (dyn, from_pos) - shift_down_size));
   1591   mhd_assert ((0u != from_pos) \
   1592               || (dtbl_bottom_gap (dyn) >= shift_down_size));
   1593   mhd_assert (dtbl_edge_gap (dyn) < dyn->buf_alloc_size);
   1594   mhd_assert (dyn->buf_alloc_size > move_area_size);
   1595 
   1596   /* Optimisation ideas: instead of shifting all entries uniformly, they
   1597    * can be "compressed" by eliminating the slack between some of the top
   1598    * entries. This will require more processing, more movements on the next
   1599    * rounds, but saves a lot if the dynamic table is large. */
   1600 
   1601   /* Move all strings in the buffer for selected entries */
   1602   memmove (move_area_src - shift_down_size,
   1603            move_area_src,
   1604            move_area_size);
   1605 
   1606 #ifndef NDEBUG
   1607   /* Zero-out standard string slack of the last entry strings */
   1608   if (mhd_dtbl_entry_slack <= shift_down_size)
   1609     memset (move_area_src - shift_down_size + move_area_size,
   1610             0,
   1611             mhd_dtbl_entry_slack);
   1612   else
   1613     memset (move_area_src - shift_down_size + move_area_size,
   1614             0,
   1615             shift_down_size);
   1616 #endif /* ! NDEBUG */
   1617 
   1618   for (i = from_pos; dtbl_get_pos_edge (dyn) >= i; ++i)
   1619     dtbl_pos_entry_info (dyn,
   1620                          i)->offset -= (dtbl_size_t)shift_down_size;
   1621 }
   1622 
   1623 
   1624 /**
   1625  * Move selected entries' strings in the strings buffer up (to the entries
   1626  * information data).
   1627  * The strings are moved for all entries from @a from_entry up to the
   1628  * edge (highest number) entry.
   1629  * @param dyn the pointer to the dynamic table structure
   1630  * @param from_pos the first entry position to move strings
   1631  * @param shift_up_size the amount of bytes to shift
   1632  */
   1633 static void
   1634 dtbl_move_strs_up (struct mhd_HpackDTblContext *dyn,
   1635                    dtbl_idx_ft from_pos,
   1636                    dtbl_size_ft shift_up_size)
   1637 {
   1638   char *move_area_src = dtbl_pos_strs_ptr_start (dyn,
   1639                                                  from_pos);
   1640   size_t move_area_size =
   1641     (size_t)
   1642     (dtbl_pos_strs_ptr_endc (dyn,
   1643                              dtbl_get_pos_edge (dyn)) - move_area_src);
   1644   dtbl_idx_ft i;
   1645 
   1646   mhd_assert (mhd_DTBL_VALUE_FITS (shift_up_size));
   1647   mhd_assert (0u != shift_up_size);
   1648   mhd_assert (dtbl_get_pos_edge (dyn) >= from_pos);
   1649   mhd_assert (shift_up_size < (dyn->buf_alloc_size));
   1650   mhd_assert (dtbl_edge_gap (dyn) >= shift_up_size);
   1651   mhd_assert (dyn->buf_alloc_size > move_area_size);
   1652 
   1653   /* Optimisation ideas: instead of shifting all entries uniformly, they
   1654    * can be "compacted" by eliminating the slack between some of the bottom
   1655    * entries. This will require more processing and probably more movements on
   1656    * the next rounds, but saves a lot if the dynamic table is large. */
   1657 
   1658 #ifndef NDEBUG
   1659   /* Zero-out standard string slack of the last entry strings AFTER the moved
   1660      data if space is available */
   1661   if (1)
   1662   {
   1663     const dtbl_size_ft top_gap_final = dtbl_edge_gap (dyn) - shift_up_size;
   1664 
   1665     if (mhd_dtbl_entry_slack <= top_gap_final)
   1666       memset (move_area_src + shift_up_size + move_area_size,
   1667               0,
   1668               mhd_dtbl_entry_slack);
   1669     else if (0u != top_gap_final)
   1670       memset (move_area_src + shift_up_size + move_area_size,
   1671               0,
   1672               top_gap_final);
   1673   }
   1674 #endif /* ! NDEBUG */
   1675 
   1676   /* Move all strings in the buffer for selected entries */
   1677   memmove (move_area_src + shift_up_size,
   1678            move_area_src,
   1679            move_area_size);
   1680 
   1681   for (i = from_pos; dtbl_get_pos_edge (dyn) >= i; ++i)
   1682     dtbl_pos_entry_info (dyn,
   1683                          i)->offset += (dtbl_size_t)shift_up_size;
   1684 }
   1685 
   1686 
   1687 /**
   1688  * Compact strings in the shared buffer so that all currently unused space
   1689  * is located at the edge (between the strings region and entry information
   1690  * data region).
   1691  *
   1692  * If the newest entry is not the edge entry, the function removes any extra
   1693  * gap between the newest and the oldest entries, keeping only the standard
   1694  * slack. Otherwise, the extra gap at the bottom of the buffer is eliminated.
   1695  *
   1696  * The function does not change the number of entries or their formal sizes.
   1697  * Behaviour is undefined if table's internal data is not consistent.
   1698  * @param dyn the pointer to the dynamic table structure
   1699  */
   1700 static void
   1701 dtbl_compact_strs (struct mhd_HpackDTblContext *dyn)
   1702 {
   1703   if (dtbl_get_pos_edge (dyn) != dtbl_get_pos_newest (dyn))
   1704   {
   1705     /* Remove extra space between the newest and the oldest,
   1706        leave the standard slack only. */
   1707     const dtbl_size_t strs_start_optimal =
   1708       dtbl_pos_strs_end_optm (dyn,
   1709                               dtbl_get_pos_newest (dyn));
   1710     const dtbl_size_t strs_start_current =
   1711       dtbl_pos_strs_start (dyn,
   1712                            dtbl_get_pos_oldest (dyn));
   1713     if (strs_start_current > strs_start_optimal)
   1714     {
   1715       /* There is an extra slack */
   1716       const dtbl_size_t shift_size = strs_start_current - strs_start_optimal;
   1717       dtbl_move_strs_down (dyn,
   1718                            dtbl_get_pos_oldest (dyn),
   1719                            shift_size);
   1720     }
   1721   }
   1722   else
   1723   {
   1724     /* Remove extra space at the bottom of the strings */
   1725     const dtbl_size_t shift_size = dtbl_pos_strs_start (dyn,
   1726                                                         0u);
   1727 
   1728     /* If there is an extra space - remove it */
   1729     if (0u != shift_size)
   1730       dtbl_move_strs_down (dyn,
   1731                            0u,
   1732                            shift_size);
   1733   }
   1734   /* All the free space must be at the edge of the buffer.
   1735      The buffer allocation is larger than the formal table size. */
   1736   mhd_assert (dtbl_edge_gap (dyn) > dtbl_get_free_formal (dyn));
   1737 }
   1738 
   1739 
   1740 /**
   1741  * Choose the offset of the strings in the strings buffer for a new entry
   1742  * following another entry (non-zero position).
   1743  *
   1744  * If enough space is available, leave up to the standard slack between
   1745  * entries' strings.
   1746  *
   1747  * Result is undefined if @a space_end is less than @a space_start.
   1748  * Result is undefined if not enough space for @a entry_strs_size is in
   1749  * between @a space_start and @a space_end.
   1750  * @param space_start the offset of the start (inclusive) of free space in
   1751  *                    the buffer
   1752  * @param space_end the offset of the end (exclusive) of free space in
   1753  *                  the buffer
   1754  * @param entry_strs_size the size of new entries strings
   1755  * @return the offset to put entries strings in the buffer
   1756  */
   1757 MHD_FN_CONST_ mhd_static_inline dtbl_size_t
   1758 dtbl_choose_strs_offset (dtbl_size_ft space_start,
   1759                          dtbl_size_ft space_end,
   1760                          dtbl_size_ft entry_strs_size)
   1761 {
   1762   const dtbl_size_ft space_size = space_end - space_start;
   1763 
   1764   mhd_assert (space_start <= space_end);
   1765   mhd_assert (mhd_DTBL_VALUE_FITS (space_start));
   1766   mhd_assert (mhd_DTBL_VALUE_FITS (space_end));
   1767   mhd_assert (mhd_DTBL_VALUE_FITS (space_size));
   1768   mhd_assert (space_end >= space_size);
   1769 
   1770   return (dtbl_size_t)dtbl_choose_strs_offset_for_size (space_start,
   1771                                                         space_size,
   1772                                                         entry_strs_size);
   1773 }
   1774 
   1775 
   1776 #ifndef NDEBUG
   1777 
   1778 /**
   1779  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1780  * entry.
   1781  * The input data is a pointer to the end of strings and available space.
   1782  * @param entr_strs_end_ptr the pointer to the end of the strings
   1783  * @param space_available amount of space before next used memory area
   1784  */
   1785 mhd_static_inline void
   1786 dtbl_zeroout_strs_slack_ptr_space (char *entr_strs_end_ptr,
   1787                                    dtbl_size_ft space_available)
   1788 {
   1789   const dtbl_size_ft zero_out_size =
   1790     (mhd_dtbl_entry_slack <= space_available) ?
   1791     mhd_dtbl_entry_slack : space_available;
   1792   mhd_assert (mhd_DTBL_VALUE_FITS (space_available));
   1793 
   1794   if (0u != space_available)
   1795     memset (entr_strs_end_ptr,
   1796             0,
   1797             zero_out_size);
   1798 }
   1799 
   1800 
   1801 /**
   1802  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1803  * entry.
   1804  * The input data is an offset of the end of strings and available space.
   1805  * @param dyn pointer to the dynamic table structure
   1806  * @param entr_strs_end_offset the offset of the end of the strings
   1807  * @param space_available amount of space before next used memory area
   1808  */
   1809 mhd_static_inline void
   1810 dtbl_zeroout_strs_slack_offset_space (struct mhd_HpackDTblContext *dyn,
   1811                                       dtbl_size_ft entr_strs_end_offset,
   1812                                       dtbl_size_ft space_available)
   1813 {
   1814   mhd_assert (dyn->buf_alloc_size >= entr_strs_end_offset);
   1815   mhd_assert (dyn->buf_alloc_size >= space_available);
   1816   mhd_assert (dyn->buf_alloc_size >= (entr_strs_end_offset + space_available));
   1817   dtbl_zeroout_strs_slack_ptr_space (dtbl_get_strs_buff (dyn)
   1818                                      + entr_strs_end_offset,
   1819                                      space_available);
   1820 }
   1821 
   1822 
   1823 /**
   1824  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1825  * entry.
   1826  * The input data is dynamic table struct, an offset of the end of strings and
   1827  * offset of the next data in the buffer.
   1828  * @param dyn pointer to the dynamic table structure
   1829  * @param entr_strs_end_offset the offset of the end of the strings
   1830  * @param next_data_offset the offset of the next used memory area in the
   1831  *                         buffer
   1832  */
   1833 mhd_static_inline void
   1834 dtbl_zeroout_strs_slack_offset_next (struct mhd_HpackDTblContext *dyn,
   1835                                      dtbl_size_ft entr_strs_end_offset,
   1836                                      dtbl_size_ft next_data_offset)
   1837 {
   1838   mhd_assert (dyn->buf_alloc_size >= entr_strs_end_offset);
   1839   mhd_assert (dyn->buf_alloc_size >= next_data_offset);
   1840   mhd_assert (next_data_offset >= entr_strs_end_offset);
   1841   dtbl_zeroout_strs_slack_offset_space (dyn,
   1842                                         entr_strs_end_offset,
   1843                                         next_data_offset
   1844                                         - entr_strs_end_offset);
   1845 }
   1846 
   1847 
   1848 /**
   1849  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1850  * entry.
   1851  * @param dyn pointer to the dynamic table structure
   1852  * @param entry the pointer to the entry information data
   1853  * @param space_available amount of space before next used memory area
   1854  */
   1855 mhd_static_inline void
   1856 dtbl_zeroout_strs_slack_entry_space (struct mhd_HpackDTblContext *dyn,
   1857                                      const struct mhd_HpackDTblEntryInfo *entry,
   1858                                      dtbl_size_ft space_available)
   1859 {
   1860   mhd_assert (dyn->buf_alloc_size >= space_available);
   1861   dtbl_zeroout_strs_slack_ptr_space (dtbl_entr_strs_ptr_end (dyn, entry),
   1862                                      space_available);
   1863 }
   1864 
   1865 
   1866 /**
   1867  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1868  * entry.
   1869  * @param dyn pointer to the dynamic table structure
   1870  * @param entry the pointer to the entry information data
   1871  * @param next_data_offset the offset of the next used memory area in the
   1872  *                         buffer
   1873  */
   1874 mhd_static_inline void
   1875 dtbl_zeroout_strs_slack_entry_next (struct mhd_HpackDTblContext *dyn,
   1876                                     const struct mhd_HpackDTblEntryInfo *entry,
   1877                                     dtbl_size_ft next_data_offset)
   1878 {
   1879   mhd_assert (dyn->buf_alloc_size >= next_data_offset);
   1880   dtbl_zeroout_strs_slack_offset_next (dyn,
   1881                                        dtbl_entr_strs_end_min (entry),
   1882                                        next_data_offset);
   1883 }
   1884 
   1885 
   1886 /**
   1887  * Zero-out end up to mhd_dtbl_entry_slack at the end of the strings of some
   1888  * entry.
   1889  * @param dyn pointer to the dynamic table structure
   1890  * @param loc_pos the number of location position
   1891  */
   1892 mhd_static_inline void
   1893 dtbl_zeroout_strs_slack_pos (struct mhd_HpackDTblContext *dyn,
   1894                              dtbl_idx_ft loc_pos)
   1895 {
   1896   if (dtbl_get_pos_edge (dyn) == loc_pos)
   1897     dtbl_zeroout_strs_slack_entry_space (dyn,
   1898                                          dtbl_pos_entry_infoc (dyn,
   1899                                                                loc_pos),
   1900                                          dtbl_edge_gap (dyn));
   1901   else
   1902     dtbl_zeroout_strs_slack_offset_next (dyn,
   1903                                          dtbl_pos_strs_end_min (dyn,
   1904                                                                 loc_pos),
   1905                                          dtbl_pos_strs_start (dyn,
   1906                                                               loc_pos + 1u));
   1907 }
   1908 
   1909 
   1910 #else  /* NDEBUG */
   1911 
   1912 /**
   1913  * No-op macro in non-debug builds.
   1914  */
   1915 #  define dtbl_zeroout_strs_slack_ptr_space(ptr, space)       ((void) 0)
   1916 
   1917 /**
   1918  * No-op macro in non-debug builds.
   1919  */
   1920 #  define dtbl_zeroout_strs_slack_offset_space(dyn, offset, space) \
   1921           ((void) 0)
   1922 
   1923 /**
   1924  * No-op macro in non-debug builds.
   1925  */
   1926 #  define dtbl_zeroout_strs_slack_offset_next(dyn, offset, next_offset) \
   1927           ((void) 0)
   1928 
   1929 /**
   1930  * No-op macro in non-debug builds.
   1931  */
   1932 #  define dtbl_zeroout_strs_slack_entry_space(dyn, entry, space) \
   1933           ((void) 0)
   1934 
   1935 /**
   1936  * No-op macro in non-debug builds.
   1937  */
   1938 #  define dtbl_zeroout_strs_slack_entry_next(dyn, entry, next_offset) \
   1939           ((void) 0)
   1940 
   1941 /**
   1942  * No-op macro in non-debug builds.
   1943  */
   1944 #  define dtbl_zeroout_strs_slack_pos(dyn, loc_pos)   ((void) 0)
   1945 
   1946 #endif /* NDEBUG */
   1947 
   1948 /**
   1949  * Copy strings to the strings buffer for a potential new entry.
   1950  *
   1951  * This function ONLY copies strings to the strings buffer.
   1952  * It does not create a new entry, nor update any numbers or limits.
   1953  *
   1954  * The caller may create a new entry pointing to copied strings and update
   1955  * related data in the dynamic table structure following the call of this
   1956  * function.
   1957  *
   1958  * The table data must be in consistent and valid state.
   1959  *
   1960  * In debug builds the function checks whether the copied data does not
   1961  * overwrite any other used data in the buffer.
   1962  *
   1963  * @param dyn pointer to the dynamic table structure
   1964  * @param name the name of the header, does NOT need to be zero-terminated
   1965  * @param val the value of the header, does NOT need to be zero terminated
   1966  * @param new_entry the pointer to the newly created entry; this entry must not
   1967  *                  be in the table; must contain the lengths of the name
   1968  *                  and the value corresponding to the strings pointed to by
   1969  *                  @a name and @a val respectively.
   1970  */
   1971 static void
   1972 dtbl_new_entry_copy_entr_strs (
   1973   struct mhd_HpackDTblContext *restrict dyn,
   1974   const char *restrict name,
   1975   const char *restrict val,
   1976   const struct mhd_HpackDTblEntryInfo *restrict new_entry)
   1977 {
   1978   char *const strs_buff = dtbl_get_strs_buff (dyn);
   1979 
   1980 #ifndef MHD_ASAN_ACTIVE
   1981 #  ifdef HAVE_UINTPTR_T
   1982   /* The new entry must not be in the table */
   1983   mhd_assert (dtbl_is_empty (dyn)
   1984               || (((uintptr_t)(const void *)dtbl_zero_entry_infoc (dyn)) < \
   1985                   (uintptr_t)(const void *)new_entry) \
   1986               || (((uintptr_t)(const void *)dtbl_zero_entry_infoc (dyn)) > \
   1987                   (uintptr_t)(const void *)new_entry));
   1988 #  endif /* HAVE_UINTPTR_T */
   1989 #endif /* ! MHD_ASAN_ACTIVE*/
   1990 
   1991 #ifndef NDEBUG
   1992   if (1)
   1993   {
   1994     /* Find position of the entry which string is located after the new copied
   1995        strings. */
   1996     dtbl_idx_ft i;
   1997     dtbl_size_ft next_data_offset = 0u;
   1998     for (i = 0u; dyn->num_entries > i; ++i)
   1999     {
   2000       /* Check whether the buffer area referenced in the new entry is not used
   2001          by other entries */
   2002       mhd_assert ((0u == dtbl_pos_strs_size_min (dyn, i)) \
   2003                   || (dtbl_pos_strs_end_min (dyn, i) <= \
   2004                       dtbl_entr_strs_start (new_entry)) \
   2005                   || (dtbl_entr_strs_end_min (new_entry) <= \
   2006                       dtbl_pos_strs_start (dyn, i)));
   2007 
   2008       if (dtbl_entr_strs_end_min (new_entry) <= \
   2009           dtbl_pos_strs_start (dyn, i))
   2010       {
   2011         next_data_offset = dtbl_pos_strs_start (dyn, i);
   2012         break;
   2013       }
   2014     }
   2015     if (dyn->num_entries == i)
   2016     {
   2017       /* Adding strings are at the edge of the strings buffer */
   2018       mhd_assert (0u == next_data_offset);
   2019       mhd_assert (dtbl_entr_strs_end_min (new_entry) <= \
   2020                   dtbl_get_strs_ceiling (dyn));
   2021       next_data_offset = dtbl_get_strs_ceiling (dyn);
   2022     }
   2023     mhd_assert (dtbl_entr_strs_end_min (new_entry) <= next_data_offset);
   2024     dtbl_zeroout_strs_slack_entry_next (dyn,
   2025                                         new_entry,
   2026                                         next_data_offset);
   2027   }
   2028 #endif
   2029 
   2030   /* Do not use dtbl_entr_strs_ptr_start() here as it does not work with
   2031      entries outside the table. */
   2032   if (0u != new_entry->name_len)
   2033     memcpy (strs_buff + dtbl_entr_strs_start (new_entry),
   2034             name,
   2035             new_entry->name_len);
   2036   if (0u != new_entry->val_len)
   2037     memcpy (strs_buff + dtbl_entr_strs_start (new_entry) + new_entry->name_len,
   2038             val,
   2039             new_entry->val_len);
   2040 }
   2041 
   2042 
   2043 /**
   2044  * Return a pointer to the slot for the next entry info.
   2045  * The new slot is assumed to be located at the next edge location (below
   2046  * the current edge entry location).
   2047  * This function neither modifies the table nor reserves memory.
   2048  * The returned pointer refers to writable but not yet initialised space
   2049  * inside the table buffer; the caller must fill it and then increment
   2050  * dyn->num_entries.
   2051  * The result is undefined if there is no space in the buffer for the
   2052  * additional entry info.
   2053  * @param dyn pointer to the dynamic table structure
   2054  * @return pointer to writable memory for the next entry info
   2055  */
   2056 MHD_FN_PURE_ mhd_static_inline struct mhd_HpackDTblEntryInfo *
   2057 dtbl_new_edge_peek_slot (struct mhd_HpackDTblContext *dyn)
   2058 {
   2059   mhd_assert (mhd_DTBL_ENTRY_INFO_SIZE <= dtbl_edge_gap (dyn));
   2060   /* Do not call dtbl_pos_entry_info() as it works only with valid position
   2061    * numbers, while the new position number is not valid yet. */
   2062   return dtbl_get_infos (dyn) - dyn->num_entries;
   2063 }
   2064 
   2065 
   2066 /* ** Intrusive dangerous functions ** */
   2067 
   2068 /**
   2069  * Shift entries info data toward higher location positions by one location
   2070  * position, starting at the specified location position and INCLUDING the
   2071  * edge entry (i.e., the block [insert_pos_loc .. edge] is moved to
   2072  * [insert_pos_loc + 1 .. edge + 1]). The entry information at
   2073  * @a insert_pos_loc becomes uninitialised.
   2074  *
   2075  * Only entries information data are moved; strings in the buffer are not
   2076  * modified.
   2077  *
   2078  * Note: this function internally moves data downward as higher location
   2079  * numbers correspond to lower entry info addresses.
   2080  *
   2081  * This function does not update any table's data. The caller is responsible
   2082  * for setting a valid entry data at the @a insert_pos_loc position, updating
   2083  * the number of entries in the table, correcting the total size of the data
   2084  * in the table and probably updating the position of the newest entry.
   2085  *
   2086  * Behaviour is undefined if @a insert_pos_loc is not a valid position in the
   2087  * table or if the location of the next edge position is already used by the
   2088  * strings in the buffer.
   2089  *
   2090  * @warning This function leaves table's data in an inconsistent state, the
   2091  * caller should update the table's data properly. Until the data is fixed,
   2092  * many dynamic table helper functions will work incorrectly.
   2093  *
   2094  * @param dyn pointer to the dynamic table structure
   2095  * @param insert_pos_loc the location position of the first entry data to move
   2096  */
   2097 mhd_static_inline void
   2098 dtbl_move_infos_up (struct mhd_HpackDTblContext *dyn,
   2099                     const dtbl_idx_ft insert_pos_loc)
   2100 {
   2101   mhd_assert (dtbl_get_pos_edge (dyn) >= insert_pos_loc);
   2102   mhd_assert (dtbl_edge_gap (dyn) >= mhd_DTBL_ENTRY_INFO_SIZE);
   2103   memmove (dtbl_new_edge_peek_slot (dyn),
   2104            dtbl_edge_entry_infoc (dyn),
   2105            (size_t)
   2106            ((dtbl_get_pos_edge (dyn) - insert_pos_loc + 1u)
   2107             * mhd_DTBL_ENTRY_INFO_SIZE));
   2108 }
   2109 
   2110 
   2111 /**
   2112  * Shift entries info data for a contiguous range of locations toward lower
   2113  * location positions to the specified location position.
   2114  * The block [first .. last] is moved to [final .. final + last - first].
   2115  * Depending on direction of the move, the entry-info slots in the range
   2116  * (final + last - first .. last] or in the range [first .. final) become
   2117  * uninitialised.
   2118  *
   2119  * Only entries information data are moved; strings in the buffer are not
   2120  * modified.
   2121  *
   2122  * This function does not update any table's data. The caller is responsible
   2123  * for updating the number of entries in the table, correcting the total size
   2124  * of the data in the table and probably updating the position of the newest
   2125  * entry.
   2126  *
   2127  * Behaviour is undefined if the specified positions are not valid for the
   2128  * table.
   2129  *
   2130  * @warning This function leaves table's data in inconsistent state, the caller
   2131  * should update the table's data properly. Until the data is fixed, many
   2132  * dynamic table helper functions will work incorrectly.
   2133  *
   2134  * @param dyn pointer to the dynamic table structure
   2135  * @param range_first_loc the first inclusive (lowest-numbered) entry position
   2136  *                        to move
   2137  * @param range_last_loc the last inclusive (higher number) entry position to
   2138  *                       move, could be equal to @a range_first_loc
   2139  * @param final_first_loc the final position location number of the first entry
   2140  */
   2141 mhd_static_inline void
   2142 dtbl_move_infos_pos (struct mhd_HpackDTblContext *dyn,
   2143                      const dtbl_idx_ft range_first_loc,
   2144                      const dtbl_idx_ft range_last_loc,
   2145                      const dtbl_idx_ft final_first_loc)
   2146 {
   2147   /** Number of elements to move, including both the last and the first */
   2148   const dtbl_idx_ft num_elements = range_last_loc - range_first_loc + 1u;
   2149   /** The final position location number of the last entry */
   2150   const dtbl_idx_ft final_last_loc = final_first_loc + num_elements - 1u;
   2151   /* Do not use dtbl_pos_entry_info() here to avoid triggering asserts as
   2152      the table data can be inconsistent */
   2153   struct mhd_HpackDTblEntryInfo *const zero_info_pos = dtbl_get_infos (dyn);
   2154   const struct mhd_HpackDTblEntryInfo *const src =
   2155     zero_info_pos - range_last_loc;
   2156   struct mhd_HpackDTblEntryInfo *const dst = zero_info_pos - final_last_loc;
   2157   mhd_assert ((dyn->buf_alloc_size / mhd_DTBL_ENTRY_INFO_SIZE) \
   2158               >= range_first_loc);
   2159   mhd_assert ((dyn->buf_alloc_size / mhd_DTBL_ENTRY_INFO_SIZE) \
   2160               >= range_last_loc);
   2161   mhd_assert ((dyn->buf_alloc_size / mhd_DTBL_ENTRY_INFO_SIZE) \
   2162               >= final_first_loc);
   2163   mhd_assert ((dyn->buf_alloc_size / mhd_DTBL_ENTRY_INFO_SIZE) \
   2164               >= final_last_loc);
   2165   mhd_assert (range_first_loc <= range_last_loc);
   2166 
   2167   if (range_first_loc == final_first_loc)
   2168     return;
   2169 
   2170   memmove (dst,
   2171            src,
   2172            (size_t)(num_elements * mhd_DTBL_ENTRY_INFO_SIZE));
   2173 }
   2174 
   2175 
   2176 /* ** Manipulating functions ** */
   2177 
   2178 #ifndef NDEBUG
   2179 /**
   2180  * Check internal consistency of the dynamic table internal data.
   2181  * @param dyn the pointer to the dynamic table structure to check
   2182  */
   2183 static void
   2184 dtbl_check_internals (const struct mhd_HpackDTblContext *dyn)
   2185 {
   2186   mhd_assert (0u != dyn->buf_alloc_size);
   2187   mhd_assert (dyn->buf_alloc_size > dyn->size_limit);
   2188   mhd_assert (dyn->cur_size <= dyn->size_limit);
   2189   mhd_assert (dyn->buf_alloc_size >= \
   2190               (dyn->num_entries * mhd_DTBL_ENTRY_INFO_SIZE));
   2191   mhd_assert (dyn->newest_pos <= dyn->num_entries);
   2192   if (dtbl_is_empty (dyn))
   2193   {
   2194     mhd_assert (0u == dyn->cur_size);
   2195     mhd_assert (0u == dyn->newest_pos);
   2196   }
   2197   else
   2198   {
   2199     const struct mhd_HpackDTblEntryInfo *const zero_entry =
   2200       dtbl_zero_entry_infoc (dyn);
   2201     dtbl_size_ft counted_size = 0u;
   2202     dtbl_idx_ft i;
   2203 
   2204     mhd_assert (dyn->newest_pos < dyn->num_entries);
   2205     mhd_assert ((0u != dyn->cur_size) \
   2206                 && "Each entry has minimal size, even with zero-length strings");
   2207     mhd_assert (dyn->cur_size >= \
   2208                 (dyn->num_entries * mhd_dtbl_entry_overhead));
   2209     mhd_assert (dtbl_edge_gap (dyn) <= dyn->buf_alloc_size);
   2210 
   2211     /* Check zero entry individually */
   2212     /* If the newest entry is the edge entry, zero entry may have gap
   2213        at the start of the buffer. */
   2214     if (0u != dtbl_get_pos_oldest (dyn))
   2215     {
   2216       mhd_assert ((0u == zero_entry->offset) \
   2217                   && "The extra gap between entries' strings is allowed only " \
   2218                   "between the newest and the oldest entries");
   2219     }
   2220     mhd_assert (zero_entry->offset < dyn->buf_alloc_size);
   2221     mhd_assert (zero_entry->name_len < dyn->buf_alloc_size);
   2222     mhd_assert (zero_entry->val_len < dyn->buf_alloc_size);
   2223     mhd_assert (dtbl_entr_strs_end_min (zero_entry) < dyn->buf_alloc_size);
   2224     mhd_assert (dtbl_entr_strs_ptr_endc (dyn, zero_entry) <= \
   2225                 (const char *)dtbl_edge_entry_infoc (dyn));
   2226     counted_size += dtbl_entr_size_formal (zero_entry);
   2227     mhd_assert (counted_size <= dyn->cur_size);
   2228 
   2229     for (i = 1u; i <= dtbl_get_pos_edge (dyn); ++i)
   2230     {
   2231       const struct mhd_HpackDTblEntryInfo *const check_entry =
   2232         dtbl_pos_entry_infoc (dyn,
   2233                               i);
   2234 
   2235       mhd_assert ((dtbl_pos_strs_end_min (dyn, i - 1u) <= \
   2236                    dtbl_pos_strs_start (dyn, i)) \
   2237                   && "Strings data cannot overlap between entries");
   2238 
   2239       if (dtbl_get_pos_oldest (dyn) != i)
   2240         mhd_assert ((dtbl_pos_strs_end_optm (dyn, i - 1u) >= \
   2241                      dtbl_pos_strs_start (dyn, i)) \
   2242                     && "The extra gap between entries' strings is allowed only " \
   2243                     "between the newest and the oldest entries");
   2244 
   2245       mhd_assert (dtbl_pos_strs_start (dyn, i) < dyn->buf_alloc_size);
   2246       mhd_assert (check_entry->name_len < dyn->buf_alloc_size);
   2247       mhd_assert (check_entry->val_len < dyn->buf_alloc_size);
   2248       mhd_assert (dtbl_entr_strs_end_min (check_entry) < dyn->buf_alloc_size);
   2249       mhd_assert (dtbl_entr_strs_ptr_endc (dyn, check_entry) <= \
   2250                   (const char *)dtbl_edge_entry_infoc (dyn));
   2251       if (dtbl_get_pos_edge (dyn) != i)
   2252         mhd_assert (0u != dtbl_pos_as_edge_get_gap (dyn, i));
   2253 
   2254       counted_size += dtbl_entr_size_formal (check_entry);
   2255       mhd_assert (counted_size <= dyn->cur_size);
   2256     }
   2257 
   2258     mhd_assert (dyn->cur_size == counted_size);
   2259   }
   2260 }
   2261 
   2262 
   2263 #else  /* NDEBUG */
   2264 /* No-op in non-debug builds */
   2265 #  define dtbl_check_internals(dyn)       ((void) 0)
   2266 #endif /* NDEBUG */
   2267 
   2268 /**
   2269  * Add the first entry to the table
   2270  *
   2271  * The table must be empty otherwise the behaviour is undefined.
   2272  * The table must have enough space for the new entry.
   2273  *
   2274  * @param dyn the pointer to the dynamic table structure
   2275  * @param name_len the length of the @a name
   2276  * @param name the name of the header, does NOT need to be zero-terminated
   2277  * @param val_len the length of the @a val
   2278  * @param val the value of the header, does NOT need to be zero terminated
   2279  */
   2280 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) void
   2281 dtbl_add_first_entry (struct mhd_HpackDTblContext *restrict dyn,
   2282                       const dtbl_size_ft name_len,
   2283                       const char *restrict name,
   2284                       const dtbl_size_ft val_len,
   2285                       const char *restrict val)
   2286 {
   2287   const dtbl_size_ft entry_strs_size = name_len + val_len;
   2288   struct mhd_HpackDTblEntryInfo new_entry;
   2289 
   2290   /* Check parameters */
   2291   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   2292   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   2293   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   2294   mhd_assert (entry_strs_size >= name_len);
   2295   mhd_assert (entry_strs_size >= val_len);
   2296 
   2297   /* Check conditions */
   2298   mhd_assert (dtbl_is_empty (dyn));
   2299 
   2300   dtbl_check_internals (dyn);
   2301 
   2302   new_entry.name_len = (dtbl_size_t)name_len;
   2303   new_entry.val_len = (dtbl_size_t)val_len;
   2304   new_entry.offset = 0u;
   2305 
   2306   mhd_assert (dtbl_get_free_formal (dyn) >= \
   2307               dtbl_entr_size_formal (&new_entry));
   2308   mhd_assert (dtbl_edge_gap (dyn) == dtbl_get_strs_ceiling (dyn));
   2309   mhd_assert (dtbl_get_strs_ceiling (dyn) >= \
   2310               (dtbl_entr_strs_size_min (&new_entry) \
   2311                + mhd_DTBL_ENTRY_INFO_SIZE));
   2312 
   2313   dtbl_new_entry_copy_entr_strs (dyn,
   2314                                  name,
   2315                                  val,
   2316                                  &new_entry);
   2317 
   2318   *(dtbl_new_edge_peek_slot (dyn)) = new_entry;
   2319   dyn->num_entries = 1u;
   2320   dyn->cur_size = dtbl_entr_size_formal (&new_entry);
   2321   mhd_assert (0u == dtbl_get_pos_newest (dyn));
   2322 }
   2323 
   2324 
   2325 /**
   2326  * Add new entry into the table at the new edge position
   2327  *
   2328  * This function adds a new entry after the existing edge-position entry,
   2329  * updates all internal table data.
   2330  * The function does NOT move strings in the strings buffer. The table's
   2331  * buffer must have enough space for the new entry's strings and the new
   2332  * entry data.
   2333  *
   2334  * The newest entry must be the edge entry.
   2335  * The table must have enough space for the new entry.
   2336  * The table must not be empty otherwise behaviour is undefined.
   2337  *
   2338  * @param dyn the pointer to the dynamic table structure
   2339  * @param name_len the length of the @a name
   2340  * @param name the name of the header, does NOT need to be zero-terminated
   2341  * @param val_len the length of the @a val
   2342  * @param val the value of the header, does NOT need to be zero terminated
   2343  */
   2344 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) void
   2345 dtbl_add_new_entry_at_new_edge (struct mhd_HpackDTblContext *restrict dyn,
   2346                                 const dtbl_size_ft name_len,
   2347                                 const char *restrict name,
   2348                                 const dtbl_size_ft val_len,
   2349                                 const char *restrict val)
   2350 {
   2351   /** The total size of the strings of the new entry */
   2352   const dtbl_size_ft entry_strs_size = name_len + val_len;
   2353   struct mhd_HpackDTblEntryInfo new_entry;
   2354 
   2355   /* Check parameters */
   2356   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   2357   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   2358   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   2359   mhd_assert (entry_strs_size >= name_len);
   2360   mhd_assert (entry_strs_size >= val_len);
   2361   mhd_assert (dtbl_get_free_formal (dyn) >= \
   2362               dtbl_new_entry_size_formal (name_len, val_len));
   2363 
   2364   /* Check conditions */
   2365   mhd_assert (!dtbl_is_empty (dyn));
   2366   mhd_assert (dtbl_get_pos_edge (dyn) == dtbl_get_pos_newest (dyn));
   2367   mhd_assert (dtbl_edge_gap (dyn) >= \
   2368               entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE);
   2369 
   2370   dtbl_check_internals (dyn);
   2371 
   2372   /* Inserting at the edge */
   2373   /* The simple case: just add new data at the edge. The previous entry
   2374    * exists.  */
   2375   /* Both strings and the entry info data must be stored in this memory
   2376      area (edge gap). */
   2377 
   2378   new_entry.name_len = (dtbl_size_t)name_len;
   2379   new_entry.val_len = (dtbl_size_t)val_len;
   2380   new_entry.offset =
   2381     dtbl_choose_strs_offset (dtbl_pos_strs_end_min (dyn,
   2382                                                     dtbl_get_pos_edge (dyn)),
   2383                              dtbl_get_strs_ceiling (dyn)
   2384                              - mhd_DTBL_ENTRY_INFO_SIZE,
   2385                              entry_strs_size);
   2386 
   2387   mhd_assert (dtbl_edge_gap (dyn) >= \
   2388               dtbl_entr_strs_size_min (&new_entry) + mhd_DTBL_ENTRY_INFO_SIZE);
   2389 
   2390   dtbl_new_entry_copy_entr_strs (dyn,
   2391                                  name,
   2392                                  val,
   2393                                  &new_entry);
   2394 
   2395   *(dtbl_new_edge_peek_slot (dyn)) = new_entry;
   2396   dyn->newest_pos = dyn->num_entries;
   2397   ++(dyn->num_entries);
   2398   dyn->cur_size += dtbl_entr_size_formal (&new_entry);
   2399 
   2400   mhd_assert (dyn->cur_size > dtbl_entr_size_formal (&new_entry));
   2401   mhd_assert (!dtbl_is_empty (dyn));
   2402   mhd_assert (0u != dyn->newest_pos);
   2403   mhd_assert (dyn->size_limit >= dyn->cur_size);
   2404   /* The next assert evaluates dtbl_edge_gap(), which also checks the
   2405      strings/infos do not overlap. */
   2406   mhd_assert (dyn->buf_alloc_size > dtbl_edge_gap (dyn));
   2407 }
   2408 
   2409 
   2410 /**
   2411  * Insert new entry into the table after the current newest (latest added)
   2412  * entry. If the latest entry is at the edge, then the new entry is inserted
   2413  * at zero position.
   2414  *
   2415  * This function inserts a new entry, moving entries information data as
   2416  * necessary, updates all internal table data.
   2417  * The function does NOT move strings in the strings buffer. The strings
   2418  * buffer after the latest entry must have enough space for the new entry
   2419  * strings.
   2420  *
   2421  * This function never inserts an entry at the edge (zero position is used
   2422  * instead).
   2423  * The table must have enough space for the new entry.
   2424  * The table must not be empty otherwise behaviour is undefined.
   2425  *
   2426  * @param dyn the pointer to the dynamic table structure
   2427  * @param name_len the length of the @a name
   2428  * @param name the name of the header, does NOT need to be zero-terminated
   2429  * @param val_len the length of the @a val
   2430  * @param val the value of the header, does NOT need to be zero terminated
   2431  */
   2432 static void
   2433 dtbl_insert_next_new_entry (struct mhd_HpackDTblContext *restrict dyn,
   2434                             const dtbl_size_ft name_len,
   2435                             const char *restrict name,
   2436                             const dtbl_size_ft val_len,
   2437                             const char *restrict val)
   2438 {
   2439   /** The total size of the strings of the new entry */
   2440   const dtbl_size_ft entry_strs_size = name_len + val_len;
   2441   const dtbl_idx_ft loc_pos = dtbl_get_pos_oldest (dyn);
   2442   const bool insert_at_zero = (0u == loc_pos);
   2443   /** The pointer to the insert entry.
   2444       The entry information data will be moved (together with higher numbered
   2445       entries) and new entry will be inserted to this location. */
   2446   struct mhd_HpackDTblEntryInfo *const insert_entry_ptr =
   2447     dtbl_oldest_entry_info (dyn);
   2448   /** The offset of the start of the available space */
   2449   const dtbl_size_ft avail_space_start =
   2450     insert_at_zero ? 0u : dtbl_entr_strs_end_min (dtbl_newest_entry_info (dyn));
   2451   /** The offset of the end of the available space */
   2452   const dtbl_size_ft avail_space_end =
   2453     dtbl_entr_strs_start (dtbl_oldest_entry_infoc (dyn));
   2454   struct mhd_HpackDTblEntryInfo new_entry;
   2455 
   2456   /* Check parameters */
   2457   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   2458   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   2459   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   2460   mhd_assert (entry_strs_size >= name_len);
   2461   mhd_assert (entry_strs_size >= val_len);
   2462   mhd_assert (dtbl_get_pos_prev (dyn, loc_pos) == dtbl_get_pos_newest (dyn));
   2463   mhd_assert (dtbl_get_pos_edge (dyn) >= loc_pos);
   2464   /* Insertion as zero position is possible only if the newest entry
   2465      is the edge entry (and the insertion wraps to the other side of
   2466      the buffer). */
   2467   mhd_assert (insert_at_zero ==
   2468               (dtbl_get_pos_newest (dyn) == dtbl_get_pos_edge (dyn)));
   2469 
   2470   /* Check conditions */
   2471   mhd_assert (!dtbl_is_empty (dyn));
   2472 
   2473   dtbl_check_internals (dyn);
   2474 
   2475   /* The new entry must be inserted either between two entries or at zero
   2476      location position. The inserted entry is not at the edge (is followed by
   2477      another entry). */
   2478   mhd_assert (avail_space_end >= avail_space_start);
   2479 
   2480   new_entry.name_len = (dtbl_size_t)name_len;
   2481   new_entry.val_len = (dtbl_size_t)val_len;
   2482   new_entry.offset =
   2483     insert_at_zero ? 0u : dtbl_choose_strs_offset (avail_space_start,
   2484                                                    avail_space_end,
   2485                                                    entry_strs_size);
   2486 
   2487   mhd_assert (avail_space_start <= new_entry.offset);
   2488   mhd_assert (avail_space_end >= new_entry.offset);
   2489   mhd_assert (avail_space_end >= new_entry.offset + entry_strs_size);
   2490   mhd_assert (avail_space_end >= dtbl_entr_strs_end_min (&new_entry));
   2491   mhd_assert (dtbl_get_free_formal (dyn) >= \
   2492               dtbl_entr_size_formal (&new_entry));
   2493 
   2494   dtbl_new_entry_copy_entr_strs (dyn,
   2495                                  name,
   2496                                  val,
   2497                                  &new_entry);
   2498 
   2499   /* Move entries info data as the new entry data must be inserted */
   2500   dtbl_move_infos_up (dyn,
   2501                       loc_pos);
   2502 
   2503   *insert_entry_ptr = new_entry;
   2504   ++(dyn->num_entries);
   2505   dyn->newest_pos = (dtbl_idx_t)loc_pos;
   2506   dyn->cur_size += dtbl_entr_size_formal (&new_entry);
   2507 
   2508   mhd_assert (dyn->cur_size > dtbl_entr_size_formal (&new_entry));
   2509   mhd_assert (dtbl_get_pos_edge (dyn) > dtbl_get_pos_newest (dyn));
   2510   mhd_assert (!dtbl_is_empty (dyn));
   2511   mhd_assert (dyn->size_limit >= dyn->cur_size);
   2512   /* The next assert calls dtbl_edge_gap() which force checking non-overlap
   2513      of entries and strings. */
   2514   mhd_assert (dyn->buf_alloc_size > dtbl_edge_gap (dyn));
   2515 }
   2516 
   2517 
   2518 /**
   2519  * Extend the table by inserting a new entry without prior eviction.
   2520  *
   2521  * The table must have enough formal free space for the new entry.
   2522  * Behaviour is undefined if table's internal data is not consistent.
   2523  * @param dyn the pointer to the dynamic table structure
   2524  * @param name_len the length of the @a name
   2525  * @param name the name of the header, does NOT need to be zero-terminated
   2526  * @param val_len the length of the @a val
   2527  * @param val the value of the header, does NOT need to be zero terminated
   2528  */
   2529 static void
   2530 dtbl_extend_with_entry (struct mhd_HpackDTblContext *restrict dyn,
   2531                         const dtbl_size_ft name_len,
   2532                         const char *restrict name,
   2533                         const dtbl_size_ft val_len,
   2534                         const char *restrict val)
   2535 {
   2536   const dtbl_size_ft entry_strs_size = name_len + val_len;
   2537 
   2538   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   2539   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   2540   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   2541   mhd_assert (entry_strs_size >= name_len);
   2542   mhd_assert (entry_strs_size >= val_len);
   2543   mhd_assert (dtbl_get_free_formal (dyn) >= \
   2544               dtbl_new_entry_size_formal (name_len, val_len));
   2545 
   2546   dtbl_check_internals (dyn);
   2547 
   2548   if (dtbl_is_empty (dyn))
   2549   {
   2550     /* Empty table */
   2551     dtbl_add_first_entry (dyn,
   2552                           name_len,
   2553                           name,
   2554                           val_len,
   2555                           val);
   2556 
   2557     return;  /* Inserted at zero position */
   2558   }
   2559   else if (dtbl_get_pos_newest (dyn) == dtbl_get_pos_edge (dyn))
   2560   {
   2561     /* Current insert position is at the edge */
   2562 
   2563     /* This section selects where to add a new entry. There are two options:
   2564        + insert at the edge;
   2565        + insert at the bottom (position wrap). */
   2566 
   2567     /** The space left on the top for strings and the new entry info */
   2568     const dtbl_size_ft top_gap = dtbl_edge_gap (dyn);
   2569     /** The space left on the bottom for strings */
   2570     const dtbl_size_ft bottom_gap = dtbl_bottom_gap (dyn);
   2571     /* 'true' to insert at the edge, 'false' to insert at the bottom */
   2572     bool insert_at_the_edge;
   2573     mhd_assert (!dtbl_is_empty (dyn));
   2574     mhd_assert (0u != dyn->cur_size);
   2575 
   2576     if (mhd_DTBL_ENTRY_INFO_SIZE > top_gap)
   2577     {
   2578       /* Not enough space to add new entry info data */
   2579       mhd_assert (0u != bottom_gap);
   2580       mhd_assert (top_gap + bottom_gap >= \
   2581                   mhd_DTBL_ENTRY_INFO_SIZE + entry_strs_size);
   2582       dtbl_move_strs_down (dyn,
   2583                            0u,
   2584                            bottom_gap);
   2585       mhd_assert (0u == dtbl_bottom_gap (dyn));
   2586       insert_at_the_edge = true;
   2587     }
   2588     else if (entry_strs_size + mhd_dtbl_entry_slack <= bottom_gap)
   2589     {
   2590       /* The new strings and the standard slack fully fit the bottom space
   2591        * in the buffer, the top space is enough for the new entry info. */
   2592       insert_at_the_edge = false;
   2593     }
   2594     else if (entry_strs_size + mhd_dtbl_entry_slack
   2595              + mhd_DTBL_ENTRY_INFO_SIZE <= top_gap)
   2596     {
   2597       /* The new strings, the new entry info and the standard slack fully fit
   2598        * the top space in the buffer. */
   2599       insert_at_the_edge = true;
   2600     }
   2601     else if (entry_strs_size <= bottom_gap)
   2602     {
   2603       /* The new strings without the standard slack fully fit the bottom space
   2604        * in the buffer, the top space is enough for the new entry info. */
   2605       insert_at_the_edge = false;
   2606     }
   2607     else if (entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE <= top_gap)
   2608     {
   2609       /* The new strings without the standard slack and the new entry info
   2610        * fully fit the top space in the buffer. */
   2611       insert_at_the_edge = true;
   2612     }
   2613     else
   2614     {
   2615       /* Neither top nor bottom of the buffer is enough for the new entry.
   2616        * The buffer needs to be moved. */
   2617       /* Strings could be moved either down or up */
   2618       /* As the strings must be moved in any case, move strings to the bottom
   2619          to insert the new entry at the edge and thus avoid moving entries
   2620          info data in memory. */
   2621       mhd_assert (top_gap < entry_strs_size \
   2622                   + mhd_dtbl_entry_slack + mhd_DTBL_ENTRY_INFO_SIZE);
   2623       mhd_assert ((top_gap + bottom_gap >= \
   2624                    mhd_dtbl_entry_slack \
   2625                    + entry_strs_size \
   2626                    + mhd_dtbl_entry_slack + mhd_DTBL_ENTRY_INFO_SIZE) \
   2627                   && "The total allocation size of the buffer is larger than " \
   2628                   "required for strict HPACK. All extra size should be now " \
   2629                   "on the top and on the bottom, as all other strings " \
   2630                   "should now be place optimally or denser. The total free " \
   2631                   "space must be enough for the previous entry slack and " \
   2632                   "for complete new entry, including slack and info data.");
   2633 
   2634       dtbl_move_strs_down (dyn,
   2635                            0u,
   2636                            bottom_gap);
   2637 
   2638       mhd_assert (dtbl_edge_gap (dyn) >= \
   2639                   entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE);
   2640       mhd_assert ((dtbl_edge_gap (dyn) >= \
   2641                    mhd_dtbl_entry_slack \
   2642                    + entry_strs_size \
   2643                    + mhd_dtbl_entry_slack + mhd_DTBL_ENTRY_INFO_SIZE) \
   2644                   && "Strings have been compacted up to optimal space or "
   2645                   "denser. The free space should be enough for optimal "
   2646                   "placement.");
   2647       insert_at_the_edge = true;
   2648     }
   2649 
   2650     if (insert_at_the_edge)
   2651     {
   2652       dtbl_add_new_entry_at_new_edge (dyn,
   2653                                       name_len,
   2654                                       name,
   2655                                       val_len,
   2656                                       val);
   2657 
   2658       return;  /* Inserted at new edge position */
   2659     }
   2660   }
   2661   else
   2662   {
   2663     /* Current insert position is in between two entries */
   2664 
   2665     /** The end of the strings of the newest entry */
   2666     const dtbl_size_ft newest_entry_end =
   2667       dtbl_pos_strs_end_min (dyn,
   2668                              dtbl_get_pos_newest (dyn));
   2669     /** The gap between the newest entry and the oldest entry */
   2670     /** The start of the strings of the newest entry */
   2671     const dtbl_size_ft oldest_entry_start =
   2672       dtbl_pos_strs_start (dyn,
   2673                            dtbl_get_pos_oldest (dyn));
   2674     const dtbl_size_ft inbetween_gap =
   2675       oldest_entry_start - newest_entry_end;
   2676     /** The space left on the top for the new entry info data */
   2677     const dtbl_size_ft top_gap = dtbl_edge_gap (dyn);
   2678     /** The optimal space to place a new entry.
   2679         The size consist of standard slack for previous entry string,
   2680         the new entry strings and the standard slack for this entry. */
   2681     const dtbl_size_ft optimal_inbetween_size =
   2682       mhd_dtbl_entry_slack + entry_strs_size + mhd_dtbl_entry_slack;
   2683 
   2684     mhd_assert (dtbl_get_pos_edge (dyn) != dtbl_get_pos_newest (dyn));
   2685     mhd_assert (dtbl_get_pos_oldest (dyn) > dtbl_get_pos_newest (dyn));
   2686     mhd_assert (oldest_entry_start >= newest_entry_end);
   2687     mhd_assert (0u != dtbl_get_pos_oldest (dyn));
   2688     mhd_assert (0u != dyn->cur_size);
   2689 
   2690     mhd_assert (top_gap + inbetween_gap >= \
   2691                 entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE);
   2692     mhd_assert ((top_gap + inbetween_gap >= \
   2693                  optimal_inbetween_size + mhd_DTBL_ENTRY_INFO_SIZE) \
   2694                 && "This is not required for the insertion of the entry " \
   2695                 "but this is guaranteed by the checking the overall size " \
   2696                 "of the buffer before the insertion, so this is a check " \
   2697                 "for the overall handling logic.");
   2698 
   2699     if (mhd_DTBL_ENTRY_INFO_SIZE > top_gap)
   2700     {
   2701       /* Not enough space to add new entry info data */
   2702       /* Shrink in-between space to the optimal entry strings size */
   2703       const dtbl_size_ft shift_size = inbetween_gap - optimal_inbetween_size;
   2704 
   2705       mhd_assert (inbetween_gap > optimal_inbetween_size);
   2706       mhd_assert (top_gap + shift_size >= mhd_DTBL_ENTRY_INFO_SIZE);
   2707 
   2708       dtbl_move_strs_down (dyn,
   2709                            dtbl_get_pos_oldest (dyn),
   2710                            shift_size);
   2711     }
   2712     else if (inbetween_gap < entry_strs_size)
   2713     {
   2714       /* Not enough space to add new entry strings */
   2715       /* Grow in-between space to the standard step */
   2716       const dtbl_size_ft shift_size = optimal_inbetween_size - inbetween_gap;
   2717 
   2718       mhd_assert (inbetween_gap < optimal_inbetween_size);
   2719       mhd_assert (top_gap - shift_size >= mhd_DTBL_ENTRY_INFO_SIZE);
   2720 
   2721       dtbl_move_strs_up (dyn,
   2722                          dtbl_get_pos_oldest (dyn),
   2723                          shift_size);
   2724     }
   2725   }
   2726 
   2727   /* The new entry must be inserted either between two entries or at zero
   2728      location position. The inserted entry is not at the edge (is followed by
   2729      another entry). */
   2730   /* Insertion to the empty table and insertion at the edge are handled
   2731      earlier. */
   2732   dtbl_insert_next_new_entry (dyn,
   2733                               name_len,
   2734                               name,
   2735                               val_len,
   2736                               val);
   2737 }
   2738 
   2739 
   2740 /**
   2741  * Evict the oldest entries as needed and add a new entry.
   2742  *
   2743  * The formal size of the new entry must be less than or equal to the table
   2744  * maximum formal size.
   2745  * The table must NOT have enough free space to add a new entry without
   2746  * eviction.
   2747  *
   2748  * Behaviour is undefined if table's internal data is not consistent.
   2749  * @param dyn the pointer to the dynamic table structure
   2750  * @param name_len the length of the @a name
   2751  * @param name the name of the header, does NOT need to be zero-terminated
   2752  * @param val_len the length of the @a val
   2753  * @param val the value of the header, does NOT need to be zero terminated
   2754  */
   2755 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) void
   2756 dtbl_evict_add_entry (struct mhd_HpackDTblContext *restrict dyn,
   2757                       const dtbl_size_ft name_len,
   2758                       const char *restrict name,
   2759                       const dtbl_size_ft val_len,
   2760                       const char *restrict val)
   2761 {
   2762   /** The total size of the strings of the new entry */
   2763   const dtbl_size_ft entry_strs_size = name_len + val_len;
   2764   /** The starting eviction position */
   2765   const dtbl_idx_ft eviction_start =
   2766     dtbl_get_pos_oldest (dyn);
   2767   /** The final (inclusive) eviction entry */
   2768   dtbl_idx_ft eviction_end;
   2769   const dtbl_size_ft needed_evict_min =
   2770     dtbl_new_entry_size_formal (name_len, val_len) - dtbl_get_free_formal (dyn);
   2771   dtbl_size_ft evicted_size;
   2772   /** The total number of entries to evict */
   2773   dtbl_idx_ft num_to_evict;
   2774 
   2775   mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   2776   mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   2777   mhd_assert (0u != dyn->cur_size);
   2778   mhd_assert (!dtbl_is_empty (dyn));
   2779   mhd_assert (mhd_DTBL_VALUE_FITS (entry_strs_size));
   2780   mhd_assert (entry_strs_size >= name_len);
   2781   mhd_assert (entry_strs_size >= val_len);
   2782   mhd_assert (dtbl_get_free_formal (dyn) < \
   2783               dtbl_new_entry_size_formal (name_len, val_len));
   2784   mhd_assert (dtbl_get_size_max_formal (dyn) >= \
   2785               dtbl_new_entry_size_formal (name_len, val_len));
   2786   mhd_assert (0u != needed_evict_min);
   2787   mhd_assert (needed_evict_min <= dyn->cur_size);
   2788   dtbl_check_internals (dyn);
   2789 
   2790   eviction_end = eviction_start;
   2791   evicted_size = dtbl_pos_size_formal (dyn,
   2792                                        eviction_end);
   2793 
   2794   while (needed_evict_min > evicted_size)
   2795   {
   2796     eviction_end = dtbl_get_pos_next (dyn,
   2797                                       eviction_end);
   2798 
   2799     mhd_assert (eviction_start != eviction_end);
   2800 
   2801     evicted_size += dtbl_pos_size_formal (dyn,
   2802                                           eviction_end);
   2803   }
   2804   mhd_assert (needed_evict_min <= evicted_size);
   2805 #ifdef MHD_USE_CODE_HARDENING
   2806   if (eviction_start > eviction_end)
   2807     num_to_evict =
   2808       eviction_end + dtbl_get_num_entries (dyn) - eviction_start + 1u;
   2809   else
   2810     num_to_evict = eviction_end - eviction_start + 1u;
   2811 #else  /* ! MHD_USE_CODE_HARDENING */
   2812   num_to_evict =
   2813     ((dtbl_get_num_entries (dyn) + eviction_end
   2814       - eviction_start) % dtbl_get_num_entries (dyn)) + 1u;
   2815 #endif /* ! MHD_USE_CODE_HARDENING */
   2816   mhd_assert (0u != num_to_evict);
   2817   mhd_assert (dtbl_get_num_entries (dyn) >= num_to_evict);
   2818 
   2819   if (mhd_COND_ALMOST_NEVER (dtbl_get_num_entries (dyn) == num_to_evict))
   2820   {
   2821     /* Simplest situation: evicted all existing entries completely */
   2822     /* Processing:
   2823        + reset the table,
   2824        + add the new first entry */
   2825     dtbl_reset (dyn);
   2826     dtbl_add_first_entry (dyn,
   2827                           name_len,
   2828                           name,
   2829                           val_len,
   2830                           val);
   2831     return;
   2832   }
   2833   else if (dtbl_get_pos_edge (dyn) == eviction_end)
   2834   {
   2835     /* Eviction area ends at the edge, at least one entry is not evicted. */
   2836     /* Processing:
   2837        + reduce the number of entries in the table (evicted entries become
   2838          ignored),
   2839        + reduce the official size of the table,
   2840        + add the new entry at the edge.
   2841        No need to move the data in the table's buffer. */
   2842     mhd_assert (eviction_end >= eviction_start);
   2843     mhd_assert ((0u == dtbl_pos_strs_start (dyn, 0u)) \
   2844                 && "An extra gap is allowed only between the newest and the " \
   2845                 "oldest entries. The newest entry was not the edge entry " \
   2846                 "before the eviction.");
   2847     mhd_assert (dtbl_get_pos_newest (dyn) == (eviction_start - 1u));
   2848 
   2849     dyn->cur_size -= (dtbl_size_t)evicted_size;
   2850     dyn->num_entries = (dtbl_idx_t)eviction_start;
   2851 
   2852     dtbl_add_new_entry_at_new_edge (dyn,
   2853                                     name_len,
   2854                                     name,
   2855                                     val_len,
   2856                                     val);
   2857     return;
   2858   }
   2859   else if ((0u != eviction_start)
   2860            && (eviction_end >= eviction_start))
   2861   {
   2862     /* Entries are evicted in between other entries, at least two entries
   2863        are not evicted (at the start and at the edge). */
   2864     /* Processing:
   2865        + set strings size of the first evicted entry to zero (will be replaced
   2866          with new entry strings),
   2867        + remove other evicted entries information data (if any) by moving
   2868          higher numbered entries,
   2869        + reduce the official size of the table,
   2870        + move strings data in the buffer (if needed),
   2871        + replace the first evicted entry with the new entry. */
   2872     struct mhd_HpackDTblEntryInfo *replace_entry_ptr =
   2873       dtbl_pos_entry_info (dyn,
   2874                            eviction_start);
   2875     /** The last entry to keep before the evicted entries */
   2876     dtbl_idx_ft last_entry_keep = dtbl_get_pos_prev (dyn,
   2877                                                      eviction_start);
   2878     /** The first entry to keep after the evicted entries */
   2879     dtbl_idx_ft first_entry_keep = dtbl_get_pos_next (dyn,
   2880                                                       eviction_end);
   2881     /** Number of entries to keep at the edge (after evicted entries) */
   2882     dtbl_idx_ft num_keep_at_edge =
   2883       dtbl_get_pos_edge (dyn) - first_entry_keep + 1u;
   2884     /** The position of the start of the space for the new entry strings */
   2885     const dtbl_size_ft space_start = dtbl_pos_strs_end_min (dyn,
   2886                                                             last_entry_keep);
   2887     /** The position of the end of the space for the new entry strings */
   2888     dtbl_size_ft space_end = dtbl_pos_strs_start (dyn,
   2889                                                   first_entry_keep);
   2890     dtbl_size_ft space_size = space_end - space_start;
   2891     struct mhd_HpackDTblEntryInfo new_entry;
   2892 
   2893     mhd_assert (first_entry_keep > last_entry_keep);
   2894     mhd_assert (dtbl_get_num_entries (dyn) - num_to_evict >= 2u);
   2895     mhd_assert (dtbl_get_pos_edge (dyn) >= first_entry_keep);
   2896     mhd_assert (0u != num_keep_at_edge);
   2897     mhd_assert (dtbl_get_num_entries (dyn) > num_keep_at_edge);
   2898     mhd_assert (space_end >= space_start);
   2899     mhd_assert (dyn->buf_alloc_size > space_size);
   2900 
   2901     replace_entry_ptr->name_len = 0u;
   2902     replace_entry_ptr->val_len = 0u;
   2903     /* Keep the entry to be replaced and move not evicted entries at the edge */
   2904     dtbl_move_infos_pos (dyn,
   2905                          first_entry_keep,
   2906                          dtbl_get_pos_edge (dyn),
   2907                          eviction_start + 1u);
   2908     /* Keep the standard overhead of the entry being replaced */
   2909     dyn->cur_size -= (dtbl_size_t)(evicted_size - mhd_dtbl_entry_overhead);
   2910     dyn->num_entries -= (dtbl_idx_t)(num_to_evict - 1u);
   2911 
   2912     if (space_size < entry_strs_size)
   2913     {
   2914       /* No space to put the new entry strings.
   2915        * Need to move strings in the buffer. */
   2916       const dtbl_size_ft shift_size =
   2917         mhd_dtbl_entry_slack + entry_strs_size + mhd_dtbl_entry_slack
   2918         - space_size;
   2919       mhd_assert (dtbl_edge_gap (dyn) > shift_size);
   2920       dtbl_move_strs_up (dyn,
   2921                          eviction_start + 1u,
   2922                          shift_size);
   2923       space_size =
   2924         mhd_dtbl_entry_slack + entry_strs_size + mhd_dtbl_entry_slack;
   2925     }
   2926 
   2927     mhd_assert (space_size >= entry_strs_size);
   2928 
   2929     new_entry.name_len = (dtbl_size_t)name_len;
   2930     new_entry.val_len = (dtbl_size_t)val_len;
   2931     new_entry.offset = dtbl_choose_strs_offset_for_size (space_start,
   2932                                                          space_size,
   2933                                                          entry_strs_size);
   2934 
   2935     mhd_assert (dtbl_get_num_entries (dyn) > (eviction_start + 1u));
   2936     mhd_assert (dtbl_entr_strs_end_min (&new_entry) <= \
   2937                 dtbl_pos_strs_start (dyn, eviction_start + 1u));
   2938 
   2939     dtbl_new_entry_copy_entr_strs (dyn,
   2940                                    name,
   2941                                    val,
   2942                                    &new_entry);
   2943     *replace_entry_ptr = new_entry;
   2944     /* Keep the standard overhead of the entry being replaced  */
   2945     dyn->cur_size += (dtbl_size_t)entry_strs_size;
   2946     mhd_assert ((dyn->newest_pos + 1u) == eviction_start);
   2947     dyn->newest_pos = (dtbl_idx_t)eviction_start;
   2948 
   2949     return;
   2950   }
   2951   else
   2952   {
   2953     /* Eviction area includes zero position entry, at least one entry is not
   2954        evicted.
   2955        The most complex case: some free space is at the bottom of the
   2956        buffer and some free space can be at the edge of the buffer.
   2957        The code should choose where to insert a new entry: at the bottom or
   2958        at the edge. */
   2959     /* Processing:
   2960        + if bottom area is large enough insert at the bottom (no need to move
   2961          strings (typically large), only entries info data may need to be
   2962          moved (fast as it is typically smaller and is always aligned),
   2963        + otherwise remove evicted info data with low numbers, move strings
   2964          in the buffer (if needed) and add the new entry at the edge. */
   2965     /** The first entry to keep */
   2966     dtbl_idx_ft first_entry_keep = dtbl_get_pos_next (dyn,
   2967                                                       eviction_end);
   2968     /** The last entry to keep */
   2969     dtbl_idx_ft last_entry_keep = dtbl_get_pos_prev (dyn,
   2970                                                      eviction_start);
   2971     dtbl_idx_ft num_to_keep =
   2972       ((dtbl_idx_ft)(last_entry_keep - first_entry_keep) + 1u);
   2973     /** The available space at the bottom of the strings buffer after
   2974         eviction of the entries */
   2975     dtbl_size_ft new_bottom_gap = dtbl_pos_strs_start (dyn,
   2976                                                        first_entry_keep);
   2977 
   2978     mhd_assert (dtbl_get_pos_edge (dyn) != eviction_end);
   2979     mhd_assert (last_entry_keep >= first_entry_keep);
   2980     mhd_assert (0u != num_to_keep);
   2981     mhd_assert (dtbl_get_num_entries (dyn) > num_to_keep);
   2982     mhd_assert (num_to_keep + num_to_evict == dtbl_get_num_entries (dyn));
   2983 
   2984     if (new_bottom_gap >= entry_strs_size)
   2985     {
   2986       /* Enough space at the bottom to put the new entry strings */
   2987       /* No need to check the space for the entries information data as
   2988          new entry replaces evicted zero position entry. */
   2989       struct mhd_HpackDTblEntryInfo *replace_entry_ptr =
   2990         dtbl_zero_entry_info (dyn);
   2991       struct mhd_HpackDTblEntryInfo new_entry;
   2992 
   2993       /* Keep data correct and asserts quite */
   2994       replace_entry_ptr->name_len = 0u;
   2995       replace_entry_ptr->val_len = 0u;
   2996       /* Move entries information data if needed,
   2997          the zero position entry information will be overwritten with
   2998          a new data. */
   2999       dtbl_move_infos_pos (dyn,
   3000                            first_entry_keep,
   3001                            last_entry_keep,
   3002                            1u);
   3003       /* Keep the standard overhead of the entry being replaced */
   3004       dyn->cur_size -= (dtbl_size_t)(evicted_size - mhd_dtbl_entry_overhead);
   3005       dyn->num_entries = (dtbl_idx_t)num_to_keep + 1u;  /* Plus replaced zero position */
   3006 
   3007       new_entry.name_len = (dtbl_size_t)name_len;
   3008       new_entry.val_len = (dtbl_size_t)val_len;
   3009       new_entry.offset = 0u;
   3010 
   3011       mhd_assert (dtbl_entr_strs_end_min (&new_entry) <= \
   3012                   dtbl_pos_strs_start (dyn, 1u));
   3013 
   3014       dtbl_new_entry_copy_entr_strs (dyn,
   3015                                      name,
   3016                                      val,
   3017                                      &new_entry);
   3018       *replace_entry_ptr = new_entry;
   3019       /* Keep the standard overhead of the entry being replaced  */
   3020       dyn->cur_size += (dtbl_size_t)entry_strs_size;
   3021       dyn->newest_pos = 0u;
   3022 
   3023       dtbl_zeroout_strs_slack_pos (dyn,
   3024                                    dtbl_get_pos_newest (dyn));
   3025 
   3026       return;
   3027     }
   3028     else
   3029     {
   3030       /* Not enough space at zero position in the buffer */
   3031       /* The new entry will be added at the edge of the buffer after
   3032          eviction */
   3033       /** The available space at the top of the strings buffer after moving
   3034           entries information data */
   3035       const dtbl_size_ft new_top_gap =
   3036         dtbl_pos_as_edge_get_gap (dyn,
   3037                                   last_entry_keep) /* The gap after the last kept entry */
   3038         + (first_entry_keep * mhd_DTBL_ENTRY_INFO_SIZE); /* 'first_entry_keep' will be evicted at zero position */
   3039 
   3040       mhd_assert (1u <= first_entry_keep);
   3041       mhd_assert (new_top_gap + new_bottom_gap >= \
   3042                   entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE);
   3043       mhd_assert ((new_top_gap + new_bottom_gap >= \
   3044                    mhd_dtbl_entry_slack + entry_strs_size
   3045                    + mhd_dtbl_entry_slack + mhd_DTBL_ENTRY_INFO_SIZE) \
   3046                   && "This is not required for the insertion of the entry " \
   3047                   "but this is guaranteed by the checking the overall size " \
   3048                   "of the buffer before the insertion, so this is a check " \
   3049                   "for the overall handling logic.");
   3050 
   3051       /* Move entries information data first to free some space */
   3052       /* No slot kept in evicted entries as the new entry will be added
   3053          at the edge */
   3054       dtbl_move_infos_pos (dyn,
   3055                            first_entry_keep,
   3056                            last_entry_keep,
   3057                            0u);
   3058       /* Keep the table internal data correct */
   3059       dyn->num_entries = (dtbl_idx_t)num_to_keep;
   3060       dyn->newest_pos = (dtbl_idx_t)(num_to_keep - 1u);
   3061       dyn->cur_size -= (dtbl_size_t)evicted_size;
   3062 
   3063       mhd_assert (new_top_gap == dtbl_edge_gap (dyn));
   3064 
   3065       if (new_top_gap < (entry_strs_size + mhd_DTBL_ENTRY_INFO_SIZE))
   3066       {
   3067         /* Not enough space on the top of the buffer (checked earlier),
   3068            not enough space at the bottom of the buffer.
   3069            The strings in the buffer need to be moved.
   3070            Eliminate all space at the bottom. */
   3071         const dtbl_size_ft shift_size = new_bottom_gap;
   3072         mhd_assert (0u != new_bottom_gap);
   3073         mhd_assert (new_bottom_gap == dtbl_bottom_gap (dyn));
   3074 
   3075         dtbl_move_strs_down (dyn,
   3076                              0u,
   3077                              shift_size);
   3078         mhd_assert (0u == dtbl_bottom_gap (dyn));
   3079         mhd_assert (new_top_gap + shift_size == dtbl_edge_gap (dyn));
   3080         mhd_assert (dtbl_edge_gap (dyn) >= \
   3081                     mhd_dtbl_entry_slack \
   3082                     + dtbl_new_entry_strs_size_formal (entry_strs_size) \
   3083                     && "All strings have been compacted, the free space must " \
   3084                     "be enough for the previous entry slack and for " \
   3085                     "a complete new entry, including slack and info data.");
   3086       }
   3087 
   3088       /* The entries have been evicted.
   3089          The edge of the buffer (top of the strings buffer) has enough space
   3090          for the new strings and the new entry info */
   3091       dtbl_add_new_entry_at_new_edge (dyn,
   3092                                       name_len,
   3093                                       name,
   3094                                       val_len,
   3095                                       val);
   3096 
   3097       return;
   3098     }
   3099   }
   3100 }
   3101 
   3102 
   3103 /**
   3104  * Evict entries to reach the specified final formal table size.
   3105  *
   3106  * The function evicts the oldest entries until the formal used size is less
   3107  * than or equal to @a final_formal_size.
   3108  *
   3109  * The table must not be empty.
   3110  * Behaviour is undefined if @a final_formal_size is not less than the current
   3111  * formal used size.
   3112  * @param dyn the pointer to the dynamic table structure
   3113  * @param max_used_final the target formal size of data in the table
   3114  */
   3115 static void
   3116 dtbl_evict_to_size (struct mhd_HpackDTblContext *restrict dyn,
   3117                     dtbl_size_ft max_used_final)
   3118 {
   3119   const dtbl_size_ft needed_evict_min =
   3120     dtbl_get_used_formal (dyn) - max_used_final;
   3121   /** The starting eviction position */
   3122   const dtbl_idx_ft eviction_start =
   3123     dtbl_get_pos_oldest (dyn);
   3124   /** The final (inclusive) eviction entry */
   3125   dtbl_idx_ft eviction_end;
   3126 
   3127   dtbl_size_ft evicted_size;
   3128   /** The total number of entries to evict */
   3129   dtbl_idx_ft num_to_evict;
   3130 
   3131   mhd_assert (dtbl_get_used_formal (dyn) > max_used_final);
   3132   mhd_assert (0u != dyn->cur_size);
   3133   mhd_assert (!dtbl_is_empty (dyn));
   3134   mhd_assert (0u != needed_evict_min);
   3135   mhd_assert (needed_evict_min <= dyn->cur_size);
   3136 
   3137   eviction_end = eviction_start;
   3138   evicted_size = dtbl_pos_size_formal (dyn,
   3139                                        eviction_end);
   3140 
   3141   while (needed_evict_min > evicted_size)
   3142   {
   3143     eviction_end = dtbl_get_pos_next (dyn,
   3144                                       eviction_end);
   3145 
   3146     mhd_assert (eviction_start != eviction_end);
   3147 
   3148     evicted_size += dtbl_pos_size_formal (dyn,
   3149                                           eviction_end);
   3150   }
   3151 
   3152   mhd_assert (needed_evict_min <= evicted_size);
   3153   num_to_evict =
   3154     (dtbl_get_num_entries (dyn) + eviction_end
   3155      - eviction_start) % dtbl_get_num_entries (dyn) + 1u;
   3156   mhd_assert (0u != num_to_evict);
   3157   mhd_assert (dtbl_get_num_entries (dyn) >= num_to_evict);
   3158 
   3159   if (mhd_COND_ALMOST_NEVER (dtbl_get_num_entries (dyn) == num_to_evict))
   3160   {
   3161     /* Simplest situation: evicted all existing entries completely */
   3162     dtbl_reset (dyn);
   3163     return;
   3164   }
   3165   else if (dtbl_get_pos_edge (dyn) == eviction_end)
   3166   {
   3167     /* Eviction area ends at the edge, at least one entry is not evicted. */
   3168     mhd_assert (eviction_end >= eviction_start);
   3169     mhd_assert (dtbl_get_pos_newest (dyn) == (eviction_start - 1u));
   3170 
   3171     dyn->cur_size -= (dtbl_size_t)evicted_size;
   3172     dyn->num_entries = (dtbl_idx_t)eviction_start;
   3173 
   3174     return;
   3175   }
   3176   else if ((0u != eviction_start)
   3177            && (eviction_end >= eviction_start))
   3178   {
   3179     /* Entries are evicted in-between of other entries, at least two entries
   3180        are not evicted (at the start and at the edge). */
   3181     /** The last entry to keep before the evicted entries */
   3182     dtbl_idx_ft last_entry_keep = dtbl_get_pos_prev (dyn,
   3183                                                      eviction_start);
   3184     /** The first entry to keep after the evicted entries */
   3185     dtbl_idx_ft first_entry_keep = dtbl_get_pos_next (dyn,
   3186                                                       eviction_end);
   3187 
   3188     mhd_assert (first_entry_keep > last_entry_keep);
   3189     mhd_assert (dtbl_get_num_entries (dyn) - num_to_evict >= 2u);
   3190     mhd_assert (dtbl_get_pos_edge (dyn) >= first_entry_keep);
   3191 
   3192     /* Move not evicted entries at the edge */
   3193     dtbl_move_infos_pos (dyn,
   3194                          first_entry_keep,
   3195                          dtbl_get_pos_edge (dyn),
   3196                          eviction_start);
   3197     dyn->cur_size -= (dtbl_size_t)evicted_size;
   3198     dyn->num_entries -= (dtbl_idx_t)num_to_evict;
   3199     mhd_assert (dtbl_get_pos_edge (dyn) >= dtbl_get_pos_newest (dyn));
   3200 
   3201     return;
   3202   }
   3203   else
   3204   {
   3205     /* Eviction area includes zero position entry, at least one entry is not
   3206        evicted. */
   3207     /** The first entry to keep */
   3208     dtbl_idx_ft first_entry_keep = dtbl_get_pos_next (dyn,
   3209                                                       eviction_end);
   3210     /** The last entry to keep */
   3211     dtbl_idx_ft last_entry_keep = dtbl_get_pos_prev (dyn,
   3212                                                      eviction_start);
   3213     dtbl_idx_ft num_to_keep =
   3214       ((dtbl_idx_ft)(last_entry_keep - first_entry_keep) + 1u);
   3215 
   3216     mhd_assert (dtbl_get_pos_edge (dyn) != eviction_end);
   3217     mhd_assert (0u != num_to_keep);
   3218     mhd_assert (dtbl_get_num_entries (dyn) > num_to_keep);
   3219     mhd_assert (num_to_keep + num_to_evict == dtbl_get_num_entries (dyn));
   3220 
   3221     dtbl_move_infos_pos (dyn,
   3222                          first_entry_keep,
   3223                          last_entry_keep,
   3224                          0u);
   3225     dyn->cur_size -= (dtbl_size_t)evicted_size;
   3226     dyn->num_entries = (dtbl_idx_t)num_to_keep;
   3227     dyn->newest_pos = dtbl_get_pos_edge (dyn);
   3228 
   3229     return;
   3230   }
   3231 }
   3232 
   3233 
   3234 /**
   3235  * Adapt the in-memory layout to a new allocation and/or formal size.
   3236  *
   3237  * The function updates @a dyn to match @a new_alloc_size and
   3238  * @a new_formal_size, moving entries information data as needed.
   3239  *
   3240  * The @a new_formal_size must be larger than or equal to the current formal
   3241  * size of the entries in the table.
   3242  * The table must not be empty.
   3243  * @param dyn the pointer to the dynamic table structure
   3244  * @param new_alloc_size the new size of the shared buffer allocation
   3245  * @param new_formal_size the new formal HPACK table size limit
   3246  */
   3247 static void
   3248 dtbl_perform_resize (struct mhd_HpackDTblContext *restrict dyn,
   3249                      const dtbl_size_ft new_alloc_size,
   3250                      const dtbl_size_ft new_formal_size)
   3251 {
   3252   /* Obtain the data from the old table state */
   3253   const struct mhd_HpackDTblEntryInfo *const infos_old_ptr =
   3254     dtbl_edge_entry_infoc (dyn);
   3255   struct mhd_HpackDTblEntryInfo *infos_new_ptr;
   3256   const dtbl_size_ft entries_total_size =
   3257     dtbl_get_num_entries (dyn) * mhd_DTBL_ENTRY_INFO_SIZE;
   3258 
   3259   mhd_assert (!dtbl_is_empty (dyn));
   3260   mhd_assert (mhd_DTBL_VALUE_FITS (new_alloc_size));
   3261   mhd_assert (mhd_DTBL_VALUE_FITS (new_formal_size));
   3262   mhd_assert (new_formal_size <= mhd_DTBL_MAX_SIZE);
   3263   mhd_assert (new_formal_size < new_alloc_size);
   3264   mhd_assert (dtbl_get_used_formal (dyn) <= new_formal_size);
   3265 
   3266   if (dyn->buf_alloc_size > new_alloc_size)
   3267   {
   3268     /* Shrinking the buffer */
   3269     mhd_assert (dtbl_get_size_max_formal (dyn) > new_formal_size);
   3270     mhd_assert (((dyn->buf_alloc_size - new_alloc_size) \
   3271                  % mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo)) == 0);
   3272 
   3273     if (dtbl_edge_gap (dyn) < (dyn->buf_alloc_size - new_alloc_size))
   3274       dtbl_compact_strs (dyn);
   3275 
   3276     mhd_assert (dtbl_edge_gap (dyn) >= (dyn->buf_alloc_size - new_alloc_size));
   3277 
   3278   }
   3279   else if (mhd_COND_ALMOST_NEVER (new_alloc_size == dyn->buf_alloc_size))
   3280   {
   3281     dyn->size_limit = (dtbl_size_t)new_formal_size;
   3282     return; /* Just update the formal size */
   3283   }
   3284   else
   3285   {
   3286     /* Growing the buffer */
   3287     mhd_assert (dtbl_get_size_max_formal (dyn) < new_formal_size);
   3288     mhd_assert (((new_alloc_size - dyn->buf_alloc_size) \
   3289                  % mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo)) == 0);
   3290   }
   3291 
   3292   /* Set the new table size */
   3293   dyn->size_limit = (dtbl_size_t)new_formal_size;
   3294   dyn->buf_alloc_size = (dtbl_size_t)new_alloc_size;
   3295 
   3296   /* Get the data location based on the new table size */
   3297   infos_new_ptr = dtbl_edge_entry_info (dyn);
   3298   memmove (infos_new_ptr,
   3299            infos_old_ptr,
   3300            (size_t)entries_total_size);
   3301 }
   3302 
   3303 
   3304 /**
   3305  * Adapt the in-memory layout to a new allocation and/or formal size.
   3306  *
   3307  * The function updates @a dyn to match @a new_alloc_size and
   3308  * @a new_formal_size, moving entries information data as needed.
   3309  *
   3310  * The @a new_formal_size must be larger than or equal to the current formal
   3311  * size of the entries in the table.
   3312  * @param dyn the pointer to the dynamic table structure
   3313  * @param new_alloc_size the new size of the shared buffer allocation
   3314  * @param new_formal_size the new formal HPACK table size limit
   3315  */
   3316 static void
   3317 dtbl_adapt_to_new_size (struct mhd_HpackDTblContext *restrict dyn,
   3318                         const dtbl_size_ft new_alloc_size,
   3319                         const dtbl_size_ft new_formal_size)
   3320 {
   3321   mhd_assert (mhd_DTBL_VALUE_FITS (new_alloc_size));
   3322   mhd_assert (mhd_DTBL_VALUE_FITS (new_formal_size));
   3323   mhd_assert (new_formal_size <= mhd_DTBL_MAX_SIZE);
   3324   mhd_assert (new_formal_size < new_alloc_size);
   3325 
   3326   if (!dtbl_is_empty (dyn))
   3327   {
   3328     dtbl_perform_resize (dyn,
   3329                          new_alloc_size,
   3330                          new_formal_size);
   3331     return; /* Internal structure has been fully updated */
   3332   }
   3333 
   3334   /* Just set the new table size */
   3335   dyn->size_limit = (dtbl_size_t)new_formal_size;
   3336   dyn->buf_alloc_size = (dtbl_size_t)new_alloc_size;
   3337 
   3338 }
   3339 
   3340 
   3341 /* ** Allocation helpers ** */
   3342 
   3343 /**
   3344  * Calculate the buffer allocation size from the requested formal table size.
   3345  *
   3346  * The returned size includes additional slack to reduce the need for frequent
   3347  * compaction and is rounded up to alignment suitable for entry information
   3348  * data. The size accounts for the alignment difference between the context
   3349  * structure and the entry information data.
   3350  *
   3351  * @param formal_size the requested formal HPACK table size
   3352  * @return the allocation size for the strings/infos shared buffer
   3353  */
   3354 mhd_static_inline dtbl_size_t
   3355 dtbl_calc_alloc_size (dtbl_size_ft formal_size)
   3356 {
   3357   dtbl_size_ft dyn_table_alloc_size;
   3358 
   3359   mhd_assert (mhd_DTBL_VALUE_FITS (formal_size));
   3360 
   3361   dyn_table_alloc_size = formal_size;
   3362   /* Add some slack to lower the need for the buffer compaction */
   3363   dyn_table_alloc_size += formal_size / 64;
   3364   dyn_table_alloc_size += 2 * mhd_DTBL_ENTRY_INFO_SIZE;
   3365   /* Round up to alignment of the entry info data, which is placed at the
   3366      end of the buffer. */
   3367   dyn_table_alloc_size =
   3368     ((dyn_table_alloc_size + mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo) - 1u)
   3369      / mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo))
   3370     * mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo);
   3371   /* Adjust the size of the allocation in case the alignment of
   3372      mhd_HpackDTblEntryInfo is stricter than that of mhd_HpackDTblContext */
   3373   dyn_table_alloc_size +=
   3374     (mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo)
   3375      - (sizeof(struct mhd_HpackDTblContext)
   3376         % mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo)))
   3377     % mhd_ALIGNOF (struct mhd_HpackDTblEntryInfo);
   3378 
   3379   mhd_assert (mhd_DTBL_VALUE_FITS (dyn_table_alloc_size));
   3380 
   3381   return (dtbl_size_t)dyn_table_alloc_size;
   3382 }
   3383 
   3384 
   3385 /* ** Entries finders ** */
   3386 
   3387 /**
   3388  * Find an entry in the dynamic table that exactly matches the given
   3389  * name and value.
   3390  *
   3391  * The @a name and @a val do not need to be zero-terminated.
   3392  * The table must not be empty.
   3393  *
   3394  * @param dyn const pointer to the dynamic table structure
   3395  * @param name_len length of @a name in bytes
   3396  * @param name pointer to the header field name
   3397  * @param val_len length of @a val in bytes
   3398  * @param val pointer to the header field value
   3399  * @return the HPACK index (> #mhd_HPACK_STBL_LAST_IDX) of the matching entry,
   3400  *         or 0 if not found
   3401  */
   3402 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) dtbl_idx_t
   3403 dtbl_find_entry (const struct mhd_HpackDTblContext *restrict dyn,
   3404                  dtbl_size_ft name_len,
   3405                  const char *restrict name,
   3406                  dtbl_size_ft val_len,
   3407                  const char *restrict val)
   3408 {
   3409   /* The table must not be empty */
   3410   const struct mhd_HpackDTblEntryInfo *entries =
   3411     dtbl_get_infos_as_arrayc (dyn);
   3412   dtbl_idx_ft i;
   3413   for (i = 0u; i < dtbl_get_num_entries (dyn); ++i)
   3414   {
   3415     const struct mhd_HpackDTblEntryInfo *const entry = entries + i;
   3416 
   3417     if (name_len != entry->name_len)
   3418       continue;
   3419     if (val_len != entry->val_len)
   3420       continue;
   3421     if (((0u == name_len)
   3422          || (0 == memcmp (name,
   3423                           dtbl_entr_strs_ptr_namec (dyn,
   3424                                                     entry),
   3425                           name_len)))
   3426         &&
   3427         ((0u == val_len)
   3428          || (0 == memcmp (val,
   3429                           dtbl_entr_strs_ptr_valuec (dyn,
   3430                                                      entry),
   3431                           val_len))))
   3432     { /* Found the entry */
   3433       return dtbl_get_hpack_idx_from_pos (dyn,
   3434                                           dtbl_get_pos_edge (dyn) - i);
   3435     }
   3436   }
   3437   return 0u; /* Not found */
   3438 }
   3439 
   3440 
   3441 /**
   3442  * Find an entry in the dynamic table whose name exactly matches @a name.
   3443  *
   3444  * The @a name does not need to be zero-terminated.
   3445  * The table must not be empty.
   3446  *
   3447  * @param dyn const pointer to the dynamic table structure
   3448  * @param name_len length of @a name in bytes
   3449  * @param name pointer to the header field name
   3450  * @return the HPACK index (> #mhd_HPACK_STBL_LAST_IDX) of the matching entry,
   3451  *         or 0 if not found
   3452  */
   3453 static MHD_FN_PAR_IN_SIZE_ (3, 2) dtbl_idx_t
   3454 dtbl_find_name (const struct mhd_HpackDTblContext *restrict dyn,
   3455                 dtbl_size_ft name_len,
   3456                 const char *restrict name)
   3457 {
   3458   /* The table must not be empty */
   3459   const struct mhd_HpackDTblEntryInfo *entries =
   3460     dtbl_get_infos_as_arrayc (dyn);
   3461   dtbl_idx_ft i;
   3462   for (i = 0u; i < dtbl_get_num_entries (dyn); ++i)
   3463   {
   3464     const struct mhd_HpackDTblEntryInfo *const entry = entries + i;
   3465 
   3466     if (name_len != entry->name_len)
   3467       continue;
   3468     if ((0u == name_len)
   3469         || (0 == memcmp (name,
   3470                          dtbl_entr_strs_ptr_namec (dyn,
   3471                                                    entry),
   3472                          name_len)))
   3473     { /* Found the entry */
   3474       return dtbl_get_hpack_idx_from_pos (dyn,
   3475                                           dtbl_get_pos_edge (dyn) - i);
   3476     }
   3477   }
   3478   return 0u; /* Not found */
   3479 }
   3480 
   3481 
   3482 /* **** ________________ End of dynamic table helpers _________________ **** */
   3483 
   3484 /* ****** ------------------- Dynamic table API --------------------- ****** */
   3485 
   3486 /*
   3487  * The API is designed to be used by one thread only.
   3488  * If any thread is modifying the data in the dynamic table, then any access
   3489  * in any other thread at the same time is not safe!
   3490  */
   3491 
   3492 
   3493 /**
   3494  * Create a dynamic HPACK table context with the specified formal size limit.
   3495  *
   3496  * The allocation includes the context and a shared buffer. The table is
   3497  * initialised to an empty state. The function allocates slightly more than
   3498  * @a dyn_table_size due to the internal overhead.
   3499  *
   3500  * @param dyn_table_size the requested formal HPACK table size limit
   3501  * @return pointer to the newly created context on success,
   3502  *         NULL on allocation failure
   3503  */
   3504 static mhd_FN_RET_UNALIASED
   3505 struct mhd_HpackDTblContext *
   3506 mhd_dtbl_create (size_t dyn_table_size)
   3507 {
   3508   struct mhd_HpackDTblContext *dyn;
   3509   dtbl_size_ft alloc_size;
   3510   mhd_assert (mhd_DTBL_MAX_SIZE >= dyn_table_size);
   3511   mhd_assert (mhd_DTBL_VALUE_FITS (dyn_table_size));
   3512 
   3513   alloc_size = dtbl_calc_alloc_size ((dtbl_size_ft)dyn_table_size);
   3514 
   3515   dyn = (struct mhd_HpackDTblContext *)malloc (sizeof(*dyn)
   3516                                                + (size_t)alloc_size);
   3517   if (NULL == dyn)
   3518     return NULL; /* Failure exit point */
   3519 
   3520   dyn->buf_alloc_size = (dtbl_size_t)alloc_size;
   3521   dyn->size_limit = (dtbl_size_t)dyn_table_size;
   3522   dtbl_reset (dyn);
   3523 
   3524   dtbl_check_internals (dyn);
   3525 
   3526   return dyn;
   3527 }
   3528 
   3529 
   3530 /**
   3531  * Destroy a dynamic HPACK table context and free all associated memory.
   3532  *
   3533  * @param dyn the pointer to the dynamic table structure to destroy
   3534  */
   3535 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void
   3536 mhd_dtbl_destroy (struct mhd_HpackDTblContext *dyn)
   3537 {
   3538   dtbl_check_internals (dyn);
   3539   /* Everything is in a single memory allocation, just free it */
   3540   free (dyn);
   3541 }
   3542 
   3543 
   3544 /**
   3545  * Get the current formal maximum table size (the HPACK size limit).
   3546  * @param dyn the pointer to the dynamic table structure
   3547  * @return the formal maximum size of the table
   3548  */
   3549 static MHD_FN_PURE_ size_t
   3550 mhd_dtbl_get_table_max_size (const struct mhd_HpackDTblContext *dyn)
   3551 {
   3552   return (size_t)dtbl_get_size_max_formal (dyn);
   3553 }
   3554 
   3555 
   3556 /**
   3557  * Get the current amount of formal used space in the table.
   3558  * @param dyn the pointer to the dynamic table structure
   3559  * @return the formal used space in the table
   3560  */
   3561 static MHD_FN_PURE_ size_t
   3562 mhd_dtbl_get_table_used (const struct mhd_HpackDTblContext *dyn)
   3563 {
   3564   return (size_t)dtbl_get_used_formal (dyn);
   3565 }
   3566 
   3567 
   3568 /**
   3569  * Get the current number of entries in the table.
   3570  * @param dyn the pointer to the dynamic table structure
   3571  * @return the number of entries in the table
   3572  */
   3573 static MHD_FN_PURE_ size_t
   3574 mhd_dtbl_get_num_entries (const struct mhd_HpackDTblContext *dyn)
   3575 {
   3576   return (size_t)dtbl_get_num_entries (dyn);
   3577 }
   3578 
   3579 
   3580 /**
   3581  * Evict the oldest dynamic-table entries until the formal (HPACK) used size
   3582  * becomes less than or equal to the requested value.
   3583  *
   3584  * If the table is already within the limit, nothing is changed.
   3585  *
   3586  * The function does not change the formal maximum table size and does not
   3587  * allocate memory.
   3588  *
   3589  * @param dyn the pointer to the dynamic table structure
   3590  * @param max_used_formal the target upper bound (in bytes) for the formal
   3591  *                        used size after eviction
   3592  */
   3593 static void
   3594 mhd_dtbl_evict_to_size (struct mhd_HpackDTblContext *dyn,
   3595                         size_t max_used_formal)
   3596 {
   3597   if (dtbl_is_empty (dyn))
   3598     return;
   3599   else if (0u == max_used_formal)
   3600     dtbl_reset (dyn);
   3601   else if (dtbl_get_used_formal (dyn) <= max_used_formal)
   3602     return;
   3603   else
   3604     dtbl_evict_to_size (dyn,
   3605                         (dtbl_size_t)max_used_formal);
   3606 
   3607   dtbl_check_internals (dyn);
   3608 }
   3609 
   3610 
   3611 /**
   3612  * Resize the dynamic HPACK table.
   3613  *
   3614  * On allocation failure when growing, the original table is unchanged.
   3615  * The shrinking of the table never fails.
   3616  *
   3617  * @param dyn_pp the pointer to the variable holding the pointer dynamic
   3618  *               table structure, the value of the variable could be updated
   3619  * @param dyn_table_size the new formal HPACK table size limit
   3620  * @return 'true' on success (the variable pointer by @a dyn_pp could be
   3621  *         updated),
   3622  *         'false' if growing failed (the dynamic table remains valid, but
   3623  *         not resized)
   3624  */
   3625 static bool
   3626 mhd_dtbl_resize (struct mhd_HpackDTblContext **const dyn_pp,
   3627                  size_t dyn_table_size)
   3628 {
   3629   const dtbl_size_ft old_official_size = dtbl_get_size_max_formal (*dyn_pp);
   3630   dtbl_size_ft new_alloc_size;
   3631   struct mhd_HpackDTblContext *new_dyn;
   3632   mhd_assert (mhd_DTBL_MAX_SIZE >= dyn_table_size);
   3633   mhd_assert (mhd_DTBL_VALUE_FITS (dyn_table_size));
   3634 
   3635   if (old_official_size == dyn_table_size)
   3636     return true; /* Do nothing */
   3637 
   3638   new_alloc_size = dtbl_calc_alloc_size ((dtbl_size_ft)dyn_table_size);
   3639 
   3640   if (old_official_size < dyn_table_size)
   3641   {
   3642     /* Growing table size */
   3643     /* No need to evict */
   3644     new_dyn = (struct mhd_HpackDTblContext *)
   3645               realloc (*dyn_pp,
   3646                        sizeof(**dyn_pp) + (size_t)new_alloc_size);
   3647     if (NULL == new_dyn)
   3648       return false; /* No table resize */
   3649     *dyn_pp = new_dyn;
   3650 
   3651     /* Adapt the table data to the larger size */
   3652     dtbl_adapt_to_new_size (new_dyn,
   3653                             new_alloc_size,
   3654                             (dtbl_size_ft)dyn_table_size);
   3655   }
   3656   else
   3657   {
   3658     /* Shrinking table size */
   3659     mhd_dtbl_evict_to_size (*dyn_pp,
   3660                             (dtbl_size_ft)dyn_table_size);
   3661 
   3662     /* Adapt table data before resizing */
   3663     dtbl_adapt_to_new_size (*dyn_pp,
   3664                             new_alloc_size,
   3665                             (dtbl_size_ft)dyn_table_size);
   3666 
   3667     /* Try to reduce the allocated memory */
   3668     new_dyn = (struct mhd_HpackDTblContext *)
   3669               realloc (*dyn_pp,
   3670                        sizeof(**dyn_pp) + (size_t)new_alloc_size);
   3671 
   3672     /* If realloc() failed, just use the previous allocation.
   3673        The table will use the new (reduced) size anyway, while the allocation
   3674        will be kept larger than needed. */
   3675     if (mhd_COND_VIRTUALLY_ALWAYS (NULL != new_dyn))
   3676       *dyn_pp = new_dyn;
   3677   }
   3678 
   3679   dtbl_check_internals (new_dyn);
   3680 
   3681   return true;
   3682 }
   3683 
   3684 
   3685 /**
   3686  * Check whether the new entry may fit the dynamic table
   3687  * @param dyn the pointer to the dynamic table structure
   3688  * @param name_len the length of the name of the new entry
   3689  * @param val_len the length of the value of the new entry
   3690  * @return 'true' if the new entry may be stored in the @a dyn dynamic table,
   3691  *         'false' if the new entry formal size is larger than @a dyn may hold.
   3692  */
   3693 static bool
   3694 mhd_dtbl_check_entry_fit (struct mhd_HpackDTblContext *restrict dyn,
   3695                           size_t name_len,
   3696                           size_t val_len)
   3697 {
   3698   size_t entry_size;
   3699   /* Carefully check the values, taking into account possible type overflow
   3700      when performing calculations */
   3701   entry_size = name_len + val_len;
   3702   if (mhd_COND_HARDLY_EVER (entry_size < val_len))
   3703     return false;
   3704   entry_size += mhd_dtbl_entry_overhead;
   3705   if (mhd_COND_HARDLY_EVER (entry_size < mhd_dtbl_entry_overhead))
   3706     return false;
   3707 
   3708   return (dtbl_get_size_max_formal (dyn) >= entry_size);
   3709 }
   3710 
   3711 
   3712 /**
   3713  * Add a new entry to the dynamic table.
   3714  *
   3715  * If the entry cannot fit the table size limit, the table is reset to the
   3716  * empty state and the entry is discarded.
   3717  * If there is enough formal free space, the entry is inserted. Otherwise, the
   3718  * oldest entries are evicted and the new entry is inserted.
   3719  *
   3720  * The function copies the provided strings into the table's buffer.
   3721  * @param dyn the pointer to the dynamic table structure
   3722  * @param name_len the length of the @a name, must fit #mhd_HPACK_DTBL_BITS bits
   3723  * @param name the name of the header, does NOT need to be zero-terminated
   3724  * @param val_len the length of the @a val, must fit #mhd_HPACK_DTBL_BITS bits
   3725  * @param val the value of the header, does NOT need to be zero terminated
   3726  */
   3727 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) void
   3728 mhd_dtbl_new_entry (struct mhd_HpackDTblContext *restrict dyn,
   3729                     size_t name_len,
   3730                     const char *restrict name,
   3731                     size_t val_len,
   3732                     const char *restrict val)
   3733 {
   3734   if (mhd_COND_ALMOST_NEVER (!mhd_dtbl_check_entry_fit (dyn,
   3735                                                         name_len,
   3736                                                         val_len)))
   3737   {
   3738     /* The entry cannot fit the table.
   3739      * Reset table to empty state (need to evict all entries). */
   3740     dtbl_reset (dyn);
   3741 
   3742   }
   3743   else if (dtbl_get_free_formal (dyn)
   3744            >= dtbl_new_entry_size_formal ((dtbl_size_ft)name_len,
   3745                                           (dtbl_size_ft)val_len))
   3746   {
   3747     /* Enough space. Insert new entry. */
   3748     mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   3749     mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   3750     dtbl_extend_with_entry (dyn,
   3751                             (dtbl_size_ft)name_len,
   3752                             name,
   3753                             (dtbl_size_ft)val_len,
   3754                             val);
   3755   }
   3756   else
   3757   {
   3758     /* Not enough free space, but the new entry fit the table after eviction.
   3759      * Evict some entries and add a new one. */
   3760     mhd_assert (mhd_DTBL_VALUE_FITS (name_len));
   3761     mhd_assert (mhd_DTBL_VALUE_FITS (val_len));
   3762     dtbl_evict_add_entry (dyn,
   3763                           (dtbl_size_ft)name_len,
   3764                           name,
   3765                           (dtbl_size_ft)val_len,
   3766                           val);
   3767   }
   3768 
   3769   dtbl_check_internals (dyn);
   3770 }
   3771 
   3772 
   3773 /**
   3774  * Get a dynamic-table entry by HPACK index.
   3775  *
   3776  * The HPACK index must refer to the dynamic table (greater than the number
   3777  * of entries in the static table). On success, the function returns pointers
   3778  * to the non-zero-terminated name and value buffers inside the table and
   3779  * their lengths.
   3780  *
   3781  * The strings returned (on success) in @a name_out and @a value_out must be
   3782  * used/processed before any other actions with the dynamic table. Any change
   3783  * in the dynamic table may invalidate pointers in @a name_out and
   3784  * @a value_out.
   3785  *
   3786  * Behaviour is undefined if @a idx is less or equal to #mhd_HPACK_STBL_LAST_IDX
   3787  *
   3788  * @param dyn const pointer to the dynamic table structure
   3789  * @param idx the HPACK index of the requested entry, must be strictly larger
   3790  *            than #mhd_HPACK_STBL_LAST_IDX
   3791  * @param[out] name_out the output buffer for the header name,
   3792  *                      the result is NOT zero-terminated
   3793  * @param[out] value_out the output buffer for the header value,
   3794  *                       the result is NOT zero-terminated
   3795  * @return 'true' if the entry exists and output buffers are set,
   3796  *         'false' otherwise
   3797  */
   3798 static MHD_FN_PAR_OUT_ (3) MHD_FN_PAR_OUT_ (4) bool
   3799 mhd_dtbl_get_entry (const struct mhd_HpackDTblContext *restrict dyn,
   3800                     dtbl_idx_ft idx,
   3801                     struct mhd_BufferConst *restrict name_out,
   3802                     struct mhd_BufferConst *restrict value_out)
   3803 {
   3804   const struct mhd_HpackDTblEntryInfo *entry;
   3805   mhd_assert (mhd_HPACK_STBL_LAST_IDX < idx);
   3806   if (dtbl_is_empty (dyn))
   3807     return false;
   3808   if (dtbl_get_pos_edge (dyn) < (idx - mhd_dtbl_hpack_idx_offset))
   3809     return false;
   3810 
   3811   entry = dtbl_pos_entry_infoc (dyn,
   3812                                 dtbl_get_pos_from_hpack_idx (dyn,
   3813                                                              idx));
   3814   name_out->size = (size_t)entry->name_len;
   3815   name_out->data = dtbl_entr_strs_ptr_startc (dyn,
   3816                                               entry);
   3817   value_out->size = (size_t)entry->val_len;
   3818   value_out->data = name_out->data + name_out->size;
   3819 
   3820   return true;
   3821 }
   3822 
   3823 
   3824 /**
   3825  * Look up a dynamic-table entry equal to the provided name and value.
   3826  *
   3827  * If the table is empty or no exact match is found, 0 is returned.
   3828  * The input strings do not need to be zero-terminated.
   3829  *
   3830  * @param dyn const pointer to the dynamic table structure
   3831  * @param name_len length of @a name in bytes
   3832  * @param name pointer to the header field name,
   3833  *             does NOT need to be zero-terminated
   3834  * @param val_len length of @a val in bytes
   3835  * @param val pointer to the header field value,
   3836  *            does NOT need to be zero-terminated
   3837  * @return the HPACK index (> #mhd_HPACK_STBL_LAST_IDX) of the matching entry,
   3838  *         or 0 if not found
   3839  */
   3840 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) dtbl_idx_t
   3841 mhd_dtbl_find_entry (const struct mhd_HpackDTblContext *restrict dyn,
   3842                      size_t name_len,
   3843                      const char *restrict name,
   3844                      size_t val_len,
   3845                      const char *restrict val)
   3846 {
   3847   if (dtbl_is_empty (dyn))
   3848     return 0u;
   3849 
   3850   if (mhd_COND_HARDLY_EVER (!mhd_DTBL_VALUE_FITS (name_len)))
   3851     return 0u;
   3852   if (mhd_COND_HARDLY_EVER (!mhd_DTBL_VALUE_FITS (val_len)))
   3853     return 0u;
   3854 
   3855   return dtbl_find_entry (dyn,
   3856                           (dtbl_size_ft)name_len,
   3857                           name,
   3858                           (dtbl_size_ft)val_len,
   3859                           val);
   3860 }
   3861 
   3862 
   3863 /**
   3864  * Look up a dynamic-table entry whose name equals @a name.
   3865  *
   3866  * If the table is empty or no match is found, 0 is returned.
   3867  * The input string does not need to be zero-terminated.
   3868  *
   3869  * @param dyn const pointer to the dynamic table structure
   3870  * @param name_len length of @a name in bytes
   3871  * @param name pointer to the header field name,
   3872  *             does NOT need to be zero-terminated
   3873  * @return the HPACK index (> #mhd_HPACK_STBL_LAST_IDX) of the matching entry,
   3874  *         or 0 if not found
   3875  */
   3876 static MHD_FN_PAR_IN_SIZE_ (3, 2) dtbl_idx_t
   3877 mhd_dtbl_find_name (const struct mhd_HpackDTblContext *restrict dyn,
   3878                     size_t name_len,
   3879                     const char *restrict name)
   3880 {
   3881   if (dtbl_is_empty (dyn))
   3882     return 0u;
   3883 
   3884   if (mhd_COND_HARDLY_EVER (!mhd_DTBL_VALUE_FITS (name_len)))
   3885     return 0u;
   3886 
   3887   return dtbl_find_name (dyn,
   3888                          (dtbl_size_ft)name_len,
   3889                          name);
   3890 }
   3891 
   3892 
   3893 /* ****** ----------------- Static table handling ----------------- ****** */
   3894 /* ========================================================================
   3895  *
   3896  *  The static table data should be accessed only by mhd_* functions.
   3897  *
   3898  *  All functions prefixed with stbl_* are internal helpers and should not
   3899  *  be used directly.
   3900  *
   3901  * ========================================================================
   3902  */
   3903 
   3904 /**
   3905  * HPACK static table element
   3906  */
   3907 struct mhd_HpackStaticEntry
   3908 {
   3909   /**
   3910    * The name of the header field
   3911    */
   3912   const struct MHD_String name;
   3913   /**
   3914    * The value of the header field.
   3915    */
   3916   const struct MHD_String value;
   3917 };
   3918 
   3919 /* The next variable cannot be declared as 'mhd_constexpr' as it contains
   3920    pointers to the strings */
   3921 /**
   3922  * HPACK static table.
   3923  * Add 1 to the array index to obtain the HPACK index.
   3924  *
   3925  * This table is extracted (and transformed) from RFC 7541.
   3926  * See https://datatracker.ietf.org/doc/html/rfc7541#appendix-A
   3927  */
   3928 static const struct mhd_HpackStaticEntry
   3929   mhd_hpack_static[mhd_HPACK_STBL_ENTRIES] = {
   3930   /* 1  */ { mhd_MSTR_INIT (":authority"), mhd_MSTR_INIT ("") },
   3931   /* 2  */ { mhd_MSTR_INIT (":method"), mhd_MSTR_INIT ("GET") },
   3932   /* 3  */ { mhd_MSTR_INIT (":method"), mhd_MSTR_INIT ("POST") },
   3933   /* 4  */ { mhd_MSTR_INIT (":path"), mhd_MSTR_INIT ("/") },
   3934   /* 5  */ { mhd_MSTR_INIT (":path"), mhd_MSTR_INIT ("/index.html") },
   3935   /* 6  */ { mhd_MSTR_INIT (":scheme"), mhd_MSTR_INIT ("http") },
   3936   /* 7  */ { mhd_MSTR_INIT (":scheme"), mhd_MSTR_INIT ("https") },
   3937   /* 8  */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("200") },
   3938   /* 9  */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("204") },
   3939   /* 10 */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("206") },
   3940   /* 11 */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("304") },
   3941   /* 12 */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("400") },
   3942   /* 13 */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("404") },
   3943   /* 14 */ { mhd_MSTR_INIT (":status"), mhd_MSTR_INIT ("500") },
   3944   /* 15 */ { mhd_MSTR_INIT ("accept-charset"), mhd_MSTR_INIT ("") },
   3945   /* 16 */ { mhd_MSTR_INIT ("accept-encoding"),
   3946              mhd_MSTR_INIT ("gzip, deflate") },
   3947   /* 17 */ { mhd_MSTR_INIT ("accept-language"), mhd_MSTR_INIT ("") },
   3948   /* 18 */ { mhd_MSTR_INIT ("accept-ranges"), mhd_MSTR_INIT ("") },
   3949   /* 19 */ { mhd_MSTR_INIT ("accept"), mhd_MSTR_INIT ("") },
   3950   /* 20 */ { mhd_MSTR_INIT ("access-control-allow-origin"),
   3951              mhd_MSTR_INIT ("") },
   3952   /* 21 */ { mhd_MSTR_INIT ("age"), mhd_MSTR_INIT ("") },
   3953   /* 22 */ { mhd_MSTR_INIT ("allow"), mhd_MSTR_INIT ("") },
   3954   /* 23 */ { mhd_MSTR_INIT ("authorization"), mhd_MSTR_INIT ("") },
   3955   /* 24 */ { mhd_MSTR_INIT ("cache-control"), mhd_MSTR_INIT ("") },
   3956   /* 25 */ { mhd_MSTR_INIT ("content-disposition"), mhd_MSTR_INIT ("") },
   3957   /* 26 */ { mhd_MSTR_INIT ("content-encoding"), mhd_MSTR_INIT ("") },
   3958   /* 27 */ { mhd_MSTR_INIT ("content-language"), mhd_MSTR_INIT ("") },
   3959   /* 28 */ { mhd_MSTR_INIT ("content-length"), mhd_MSTR_INIT ("") },
   3960   /* 29 */ { mhd_MSTR_INIT ("content-location"), mhd_MSTR_INIT ("") },
   3961   /* 30 */ { mhd_MSTR_INIT ("content-range"), mhd_MSTR_INIT ("") },
   3962   /* 31 */ { mhd_MSTR_INIT ("content-type"), mhd_MSTR_INIT ("") },
   3963   /* 32 */ { mhd_MSTR_INIT ("cookie"), mhd_MSTR_INIT ("") },
   3964   /* 33 */ { mhd_MSTR_INIT ("date"), mhd_MSTR_INIT ("") },
   3965   /* 34 */ { mhd_MSTR_INIT ("etag"), mhd_MSTR_INIT ("") },
   3966   /* 35 */ { mhd_MSTR_INIT ("expect"), mhd_MSTR_INIT ("") },
   3967   /* 36 */ { mhd_MSTR_INIT ("expires"), mhd_MSTR_INIT ("") },
   3968   /* 37 */ { mhd_MSTR_INIT ("from"), mhd_MSTR_INIT ("") },
   3969   /* 38 */ { mhd_MSTR_INIT ("host"), mhd_MSTR_INIT ("") },
   3970   /* 39 */ { mhd_MSTR_INIT ("if-match"), mhd_MSTR_INIT ("") },
   3971   /* 40 */ { mhd_MSTR_INIT ("if-modified-since"), mhd_MSTR_INIT ("") },
   3972   /* 41 */ { mhd_MSTR_INIT ("if-none-match"), mhd_MSTR_INIT ("") },
   3973   /* 42 */ { mhd_MSTR_INIT ("if-range"), mhd_MSTR_INIT ("") },
   3974   /* 43 */ { mhd_MSTR_INIT ("if-unmodified-since"), mhd_MSTR_INIT ("") },
   3975   /* 44 */ { mhd_MSTR_INIT ("last-modified"), mhd_MSTR_INIT ("") },
   3976   /* 45 */ { mhd_MSTR_INIT ("link"), mhd_MSTR_INIT ("") },
   3977   /* 46 */ { mhd_MSTR_INIT ("location"), mhd_MSTR_INIT ("") },
   3978   /* 47 */ { mhd_MSTR_INIT ("max-forwards"), mhd_MSTR_INIT ("") },
   3979   /* 48 */ { mhd_MSTR_INIT ("proxy-authenticate"), mhd_MSTR_INIT ("") },
   3980   /* 49 */ { mhd_MSTR_INIT ("proxy-authorization"), mhd_MSTR_INIT ("") },
   3981   /* 50 */ { mhd_MSTR_INIT ("range"), mhd_MSTR_INIT ("") },
   3982   /* 51 */ { mhd_MSTR_INIT ("referer"), mhd_MSTR_INIT ("") },
   3983   /* 52 */ { mhd_MSTR_INIT ("refresh"), mhd_MSTR_INIT ("") },
   3984   /* 53 */ { mhd_MSTR_INIT ("retry-after"), mhd_MSTR_INIT ("") },
   3985   /* 54 */ { mhd_MSTR_INIT ("server"), mhd_MSTR_INIT ("") },
   3986   /* 55 */ { mhd_MSTR_INIT ("set-cookie"), mhd_MSTR_INIT ("") },
   3987   /* 56 */ { mhd_MSTR_INIT ("strict-transport-security"), mhd_MSTR_INIT ("") },
   3988   /* 57 */ { mhd_MSTR_INIT ("transfer-encoding"), mhd_MSTR_INIT ("") },
   3989   /* 58 */ { mhd_MSTR_INIT ("user-agent"), mhd_MSTR_INIT ("") },
   3990   /* 59 */ { mhd_MSTR_INIT ("vary"), mhd_MSTR_INIT ("") },
   3991   /* 60 */ { mhd_MSTR_INIT ("via"), mhd_MSTR_INIT ("") },
   3992   /* 61 */ { mhd_MSTR_INIT ("www-authenticate"), mhd_MSTR_INIT ("") }
   3993 };
   3994 
   3995 /**
   3996  * The position of the first ":status" pseud-header field in the
   3997  * @a mhd_hpack_static table
   3998  */
   3999 #define mhd_HPACK_STBL_PF_STATUS_START_POS         (8u)
   4000 
   4001 /**
   4002  * Convert an HPACK index (matching the static table) to a 0-based position in
   4003  * the static table data.
   4004  *
   4005  * Behaviour is undefined if @a hpack_idx is 0 or greater than
   4006  * #mhd_HPACK_STBL_LAST_IDX.
   4007  * @param hpack_idx the HPACK index of the static-table entry
   4008  *                  (1 .. #mhd_HPACK_STBL_LAST_IDX)
   4009  * @return the 0-based position corresponding to @a hpack_idx
   4010  */
   4011 MHD_FN_CONST_ mhd_static_inline dtbl_idx_t
   4012 stbl_get_pos_from_hpack_idx (dtbl_idx_ft hpack_idx)
   4013 {
   4014   mhd_assert (0u != hpack_idx);
   4015   mhd_assert (mhd_HPACK_STBL_LAST_IDX >= hpack_idx);
   4016   return (dtbl_idx_t)(hpack_idx - 1u);
   4017 }
   4018 
   4019 
   4020 /**
   4021  * Convert a 0-based static table position to the HPACK index.
   4022  *
   4023  * The returned index is in the range 1 .. #mhd_HPACK_STBL_LAST_IDX.
   4024  *
   4025  * Behaviour is undefined if @a loc_pos is not a valid static-table position,
   4026  * i.e. if it is greater than or equal to #mhd_HPACK_STBL_ENTRIES.
   4027  * @param loc_pos the 0-based position in the static table
   4028  * @return the HPACK index corresponding to @a loc_pos
   4029  */
   4030 MHD_FN_CONST_ mhd_static_inline dtbl_idx_t
   4031 stbl_get_hpack_idx_from_pos (dtbl_idx_ft loc_pos)
   4032 {
   4033   mhd_assert (mhd_HPACK_STBL_LAST_IDX > loc_pos);
   4034   return (dtbl_idx_t)(loc_pos + 1u);
   4035 }
   4036 
   4037 
   4038 /**
   4039  * Get a pointer to the static table entry by its 0-based position.
   4040  *
   4041  * Behaviour is undefined if @a loc_pos is not a valid static-table position,
   4042  * i.e. if it is greater than or equal to #mhd_HPACK_STBL_ENTRIES.
   4043  * @param loc_pos the 0-based position in the static table
   4044  * @return const pointer to the static entry descriptor
   4045  */
   4046 MHD_FN_CONST_ mhd_static_inline const struct mhd_HpackStaticEntry *
   4047 stbl_pos_entry_info (dtbl_idx_ft loc_pos)
   4048 {
   4049   mhd_STATIC_ASSERT_STMT (
   4050     sizeof(mhd_hpack_static) / sizeof(mhd_hpack_static[0]) \
   4051     == mhd_HPACK_STBL_ENTRIES,
   4052     "The HPACK static table size must match mhd_HPACK_STBL_ENTRIES");
   4053   mhd_assert (mhd_HPACK_STBL_ENTRIES > loc_pos);
   4054   return mhd_hpack_static + loc_pos;
   4055 }
   4056 
   4057 
   4058 /**
   4059  * Get a pointer to the static table entry by its HPACK index.
   4060  *
   4061  * Behaviour is undefined if @a hpack_idx is 0 or greater than
   4062  * #mhd_HPACK_STBL_LAST_IDX.
   4063  * @param hpack_idx the HPACK index of the entry
   4064  * @return const pointer to the static entry descriptor
   4065  */
   4066 MHD_FN_CONST_ mhd_static_inline const struct mhd_HpackStaticEntry *
   4067 stbl_idx_entry_info (dtbl_idx_ft hpack_idx)
   4068 {
   4069   return stbl_pos_entry_info (stbl_get_pos_from_hpack_idx (hpack_idx));
   4070 }
   4071 
   4072 
   4073 /* **** _____________ End of static table data helpers ______________ ****** */
   4074 
   4075 /* ****------------------- Static table data API ---------------------****** */
   4076 /**
   4077  * The position of the first real (non-pseudo) header in the
   4078  * @a mhd_hpack_static table
   4079  */
   4080 #define mhd_HPACK_STBL_NORM_START_POS         (14u)
   4081 
   4082 /**
   4083  * The position of the only real (non-pseudo) header with a non-empty value in
   4084  * the @a mhd_hpack_static table
   4085  */
   4086 #define mhd_HPACK_STBL_NORM_WITH_VALUE_POS         (15u)
   4087 
   4088 /**
   4089  * The index of the first real (non-pseudo) header
   4090  */
   4091 #define mhd_HPACK_STBL_NORM_START_IDX   (mhd_HPACK_STBL_NORM_START_POS + 1u)
   4092 
   4093 /**
   4094  * Get a static-table entry by HPACK index.
   4095  *
   4096  * The index @a idx must refer to the static table
   4097  * (i.e. 1 .. #mhd_HPACK_STBL_LAST_IDX).
   4098  * On return, @a name_out and @a value_out are set to point to the entry
   4099  * data and their lengths.
   4100  *
   4101  * Behaviour is undefined if @a idx is 0 or greater
   4102  * than #mhd_HPACK_STBL_LAST_IDX.
   4103  * @param idx the HPACK index within the static table
   4104  * @param[out] name_out output buffer for the header name
   4105  * @param[out] value_out output buffer for the header value
   4106  */
   4107 static MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_OUT_ (3) void
   4108 mhd_stbl_get_entry (dtbl_idx_ft idx,
   4109                     struct mhd_BufferConst *restrict name_out,
   4110                     struct mhd_BufferConst *restrict value_out)
   4111 {
   4112   const struct mhd_HpackStaticEntry *const entry = stbl_idx_entry_info (idx);
   4113 
   4114   name_out->size = entry->name.len;
   4115   name_out->data = entry->name.cstr;
   4116   value_out->size = entry->value.len;
   4117   value_out->data = entry->value.cstr;
   4118 }
   4119 
   4120 
   4121 /**
   4122  * Find a static-table entry among "real" (non-pseudo) headers that exactly
   4123  * matches the given name and value.
   4124  *
   4125  * The header name must not start with ':'.
   4126  * The input strings do not need to be zero-terminated.
   4127  *
   4128  * @param name_len length of @a name in bytes,
   4129  *                 must not be zero
   4130  * @param name pointer to the header field name,
   4131  *             does NOT need to be zero-terminated
   4132  * @param val_len length of @a val in bytes
   4133  * @param val pointer to the header field value,
   4134  *            does NOT need to be zero-terminated
   4135  * @return the HPACK index (<= #mhd_HPACK_STBL_LAST_IDX) of the matching
   4136  *         static entry, or 0 if not found
   4137  */
   4138 static MHD_FN_PAR_IN_SIZE_ (2, 1) MHD_FN_PAR_IN_SIZE_ (4, 3) dtbl_idx_t
   4139 mhd_stbl_find_entry_real (size_t name_len,
   4140                           const char *restrict name,
   4141                           size_t val_len,
   4142                           const char *restrict val)
   4143 {
   4144 #ifndef MHD_UNIT_TESTING /* Do not abort on a wrong name when unit-testing */
   4145   mhd_assert (0u != name_len);
   4146   mhd_assert (':' != name[0]);
   4147 #endif /* ! MHD_UNIT_TESTING */
   4148 #ifndef MHD_FAVOR_SMALL_CODE
   4149   if (mhd_COND_ALMOST_ALWAYS (0u != val_len))
   4150   { /* non-empty 'value' */
   4151     /* Process the only normal (real header) entry that has non-empty value */
   4152     mhd_constexpr dtbl_idx_ft i = mhd_HPACK_STBL_NORM_WITH_VALUE_POS;
   4153     do
   4154     {
   4155       const struct mhd_HpackStaticEntry *const entry = stbl_pos_entry_info (i);
   4156 
   4157       if (name_len != entry->name.len)
   4158         continue;
   4159       if (val_len != entry->value.len)
   4160         continue;
   4161 
   4162       mhd_assert (0u != entry->name.len);
   4163       mhd_assert (0u != entry->value.len);
   4164 
   4165       if (0 == memcmp (name,
   4166                        entry->name.cstr,
   4167                        name_len))
   4168       { /* 'name' matches */
   4169         if (0 == memcmp (val,
   4170                          entry->value.cstr,
   4171                          val_len))
   4172         { /* 'value' matches */
   4173           /* Full match found, return the HPACK index */
   4174           return stbl_get_hpack_idx_from_pos (i);
   4175         }
   4176       }
   4177 
   4178 
   4179     } while (0);
   4180   }
   4181   else
   4182   { /* (0u == val_len) */
   4183     /* empty 'value' */
   4184     dtbl_idx_ft i;
   4185     mhd_assert (0u == val_len);
   4186     for (i = mhd_HPACK_STBL_NORM_START_POS; i < mhd_HPACK_STBL_ENTRIES; ++i)
   4187     {
   4188       const struct mhd_HpackStaticEntry *const entry = stbl_pos_entry_info (i);
   4189 
   4190       if (mhd_HPACK_STBL_NORM_WITH_VALUE_POS == i)
   4191         continue;
   4192 
   4193       if (name_len != entry->name.len)
   4194         continue;
   4195       mhd_assert (0u != entry->name.len);
   4196       mhd_assert (0u == entry->value.len);
   4197       if (0 == memcmp (name,
   4198                        entry->name.cstr,
   4199                        name_len))
   4200       { /* 'name' matches (and 'value' is empty) */
   4201         /* Full match found, return the HPACK index */
   4202         return stbl_get_hpack_idx_from_pos (i);
   4203       }
   4204     }
   4205   }
   4206 #else  /* ! MHD_FAVOR_SMALL_CODE */
   4207   if (1)
   4208   {
   4209     dtbl_idx_ft i;
   4210     for (i = mhd_HPACK_STBL_NORM_START_POS; i < mhd_HPACK_STBL_ENTRIES; ++i)
   4211     {
   4212       const struct mhd_HpackStaticEntry *const entry = stbl_pos_entry_info (i);
   4213 
   4214       if (name_len != entry->name.len)
   4215         continue;
   4216       if (val_len != entry->value.len)
   4217         continue;
   4218 
   4219       mhd_assert (0u != entry->name.len);
   4220       mhd_assert ((0u != entry->value.len) \
   4221                   || (mhd_HPACK_STBL_NORM_WITH_VALUE_POS != i));
   4222 
   4223       if (0 == memcmp (name,
   4224                        entry->name.cstr,
   4225                        name_len))
   4226       { /* 'name' matches */
   4227         if ((0u == val_len)
   4228             || (0 == memcmp (val,
   4229                              entry->value.cstr,
   4230                              val_len)))
   4231         { /* 'value' matches (empty or identical) */
   4232           /* Full match found, return the HPACK index */
   4233           return stbl_get_hpack_idx_from_pos (i);
   4234         }
   4235       }
   4236     }
   4237   }
   4238 #endif /* !MHD_FAVOR_SMALL_CODE */
   4239 
   4240   return 0u; /* Not found */
   4241 }
   4242 
   4243 
   4244 /**
   4245  * Find a static-table entry among "real" (non-pseudo) headers whose name
   4246  * exactly matches @a name.
   4247  *
   4248  * The header name must not start with ':'.
   4249  * The input string does not need to be zero-terminated.
   4250  *
   4251  * @param name_len length of @a name in bytes,
   4252  *                 must NOT be zero
   4253  * @param name pointer to the header field name,
   4254  *             does NOT need to be zero-terminated
   4255  * @return the HPACK index (<= #mhd_HPACK_STBL_LAST_IDX) of the matching
   4256  *         static entry, or 0 if not found
   4257  */
   4258 static MHD_FN_PAR_IN_SIZE_ (2, 1) dtbl_idx_t
   4259 mhd_stbl_find_name_real (size_t name_len,
   4260                          const char *restrict name)
   4261 {
   4262   dtbl_idx_ft i;
   4263 #ifndef MHD_UNIT_TESTING /* Do not abort on a wrong name when unit-testing */
   4264   mhd_assert (0u != name_len);
   4265   mhd_assert (':' != name[0]);
   4266 #endif /* ! MHD_UNIT_TESTING */
   4267   for (i = mhd_HPACK_STBL_NORM_START_POS; i < mhd_HPACK_STBL_ENTRIES; ++i)
   4268   {
   4269     const struct mhd_HpackStaticEntry *const entry = stbl_pos_entry_info (i);
   4270 
   4271     if (name_len != entry->name.len)
   4272       continue;
   4273     mhd_assert (0u != entry->name.len);
   4274     if (0 == memcmp (name,
   4275                      entry->name.cstr,
   4276                      name_len))
   4277     { /* Found the entry, return the HPACK index */
   4278       return stbl_get_hpack_idx_from_pos (i);
   4279     }
   4280   }
   4281 
   4282   return 0u; /* Not found */
   4283 }
   4284 
   4285 
   4286 /* ****** -------------- HPACK header tables handling -------------- ****** */
   4287 /*
   4288  * mhd_htbl_ functions are handling combination of HPACK static and dynamic
   4289  * tables.
   4290  * Functions need a pointer to a dynamic table instance.
   4291  *
   4292  * These functions are just convenient wrappers for some operations; they are
   4293  * not designed to cover all operations with static and dynamic tables.
   4294  * Some operations must be performed directly on static or dynamic tables.
   4295  */
   4296 /**
   4297  * Get a header-table entry (static or dynamic) by HPACK index.
   4298  *
   4299  * On success, @a name_out and @a value_out are set to point to the entry
   4300  * data and their lengths. The returned buffers are not guaranteed to be
   4301  * zero-terminated and must not be relied upon as C strings.
   4302  *
   4303  * @param dyn const pointer to the dynamic table context
   4304  * @param idx the HPACK index (static or dynamic)
   4305  * @param[out] name_out output buffer for the header name
   4306  * @param[out] value_out output buffer for the header value
   4307  * @return 'true' if the entry exists and outputs are set,
   4308  *         'false' otherwise
   4309  */
   4310 static MHD_FN_PAR_OUT_ (3) MHD_FN_PAR_OUT_ (4) bool
   4311 mhd_htbl_get_entry (const struct mhd_HpackDTblContext *restrict dyn,
   4312                     dtbl_idx_ft idx,
   4313                     struct mhd_BufferConst *restrict name_out,
   4314                     struct mhd_BufferConst *restrict value_out)
   4315 {
   4316   if (mhd_COND_HARDLY_EVER (0u == idx))
   4317     return false;
   4318   if (mhd_HPACK_STBL_LAST_IDX >= idx)
   4319   {
   4320     mhd_stbl_get_entry (idx,
   4321                         name_out,
   4322                         value_out);
   4323     return true;
   4324   }
   4325 
   4326   return mhd_dtbl_get_entry (dyn,
   4327                              idx,
   4328                              name_out,
   4329                              value_out);
   4330 }
   4331 
   4332 
   4333 /**
   4334  * Look up a header-table entry (static "real" headers first, then dynamic)
   4335  * that exactly matches the given name and value.
   4336  *
   4337  * Pseudo-headers (names starting with ':') are not searched. The input
   4338  * strings do not need to be zero-terminated.
   4339  *
   4340  * @param dyn const pointer to the dynamic table context
   4341  * @param name_len length of @a name in bytes
   4342  * @param name pointer to the header field name, must not start with ':',
   4343  *             does NOT need to be zero-terminated
   4344  * @param val_len length of @a val in bytes
   4345  * @param val pointer to the header field value,
   4346  *            does NOT need to be zero-terminated
   4347  * @return the HPACK index of the matching entry (either static or dynamic),
   4348  *         or 0 if not found
   4349  */
   4350 static MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) dtbl_idx_t
   4351 mhd_htbl_find_entry_real (const struct mhd_HpackDTblContext *restrict dyn,
   4352                           size_t name_len,
   4353                           const char *restrict name,
   4354                           size_t val_len,
   4355                           const char *restrict val)
   4356 {
   4357   dtbl_idx_ft idx;
   4358 #ifndef MHD_UNIT_TESTING /* Do not abort on a wrong name when unit-testing */
   4359   mhd_assert ((0u == name_len) || (':' != name[0]));
   4360 #endif /* ! MHD_UNIT_TESTING */
   4361 
   4362   if (0u != name_len)
   4363     idx = mhd_stbl_find_entry_real (name_len,
   4364                                     name,
   4365                                     val_len,
   4366                                     val);
   4367   else
   4368     idx = 0u;
   4369 
   4370   if (0u == idx)
   4371     idx = mhd_dtbl_find_entry (dyn,
   4372                                name_len,
   4373                                name,
   4374                                val_len,
   4375                                val);
   4376 
   4377   return (dtbl_idx_t)idx;
   4378 }
   4379 
   4380 
   4381 /**
   4382  * Look up a header-table entry (static "real" headers first, then dynamic)
   4383  * whose name exactly matches @a name.
   4384  *
   4385  * Pseudo-headers (names starting with ':') are not searched. The input
   4386  * string does not need to be zero-terminated.
   4387  *
   4388  * @param dyn const pointer to the dynamic table context
   4389  * @param name_len length of @a name in bytes
   4390  * @param name pointer to the header field name, must not start with ':',
   4391  *             does NOT need to be zero-terminated
   4392  * @return the HPACK index of the matching entry (either static or dynamic),
   4393  *         or 0 if not found
   4394  */
   4395 static MHD_FN_PAR_IN_SIZE_ (3, 2) dtbl_idx_t
   4396 mhd_htbl_find_name_real (const struct mhd_HpackDTblContext *restrict dyn,
   4397                          size_t name_len,
   4398                          const char *restrict name)
   4399 {
   4400   dtbl_idx_ft idx;
   4401 #ifndef MHD_UNIT_TESTING /* Do not abort on a wrong name when unit-testing */
   4402   mhd_assert ((0u == name_len) || (':' != name[0]));
   4403 #endif /* ! MHD_UNIT_TESTING */
   4404 
   4405   if (0u != name_len)
   4406     idx = mhd_stbl_find_name_real (name_len,
   4407                                    name);
   4408   else
   4409     idx = 0u;
   4410 
   4411   if (0u == idx)
   4412     idx = mhd_dtbl_find_name (dyn,
   4413                               name_len,
   4414                               name);
   4415 
   4416   return (dtbl_idx_t)idx;
   4417 }
   4418 
   4419 
   4420 /* **** ___________ End of HPACK header tables handling ____________ ****** */
   4421 
   4422 /**
   4423  * H2 HPACK default maximum size of the dynamic table
   4424  */
   4425 mhd_constexpr size_t mhd_hpack_def_dyn_table_size = 4096u;
   4426 
   4427 #if !defined(mhd_HPACK_TESTING_TABLES_ONLY) || !defined(MHD_UNIT_TESTING)
   4428 
   4429 /**
   4430  * Exactly eight bits all set (to one).
   4431  */
   4432 mhd_constexpr uint8_t b8ones = 0xFFu;
   4433 
   4434 /**
   4435  * The maximum number of bytes allowed to encode numbers in HPACK.
   4436  *
   4437  * Current implementation supports only 32-bit numbers for strings and indices,
   4438  * but extra zeros at the end of the encoded numbers can be safely processed.
   4439  * This value limits the number of extra zero bytes at the end to a reasonable
   4440  * value. It is enough to process the output of some weak encoder which may
   4441  * encode numbers always as 64-bit-long values with some extra zero bytes at
   4442  * the end of the encoded form.
   4443  */
   4444 mhd_constexpr uint_fast8_t mhd_hpack_num_max_bytes = 12u;
   4445 
   4446 /* ****** ----------------- HPACK headers decoding ----------------- ****** */
   4447 
   4448 /**
   4449  * Result of hpack_dec_number()
   4450  */
   4451 enum MHD_FIXED_ENUM_ mhd_HpackGetNumResult
   4452 {
   4453   mhd_HPACK_GET_NUM_RES_NO_ERROR,  /**< Success */
   4454   mhd_HPACK_GET_NUM_RES_INCOMPLETE,/**< Not enough data in the input buffer */
   4455   mhd_HPACK_GET_NUM_RES_TOO_LARGE, /**< The decoded integer is too large for 32-bit */
   4456   mhd_HPACK_GET_NUM_RES_TOO_LONG   /**< The tail of the encoded number has too many extra zero bytes */
   4457 };
   4458 
   4459 /**
   4460  * Decode an HPACK integer number from the input buffer using a prefix in
   4461  * the first byte.
   4462  * @param first_byte_prefix_bits number of prefix bits in the first byte (1..7)
   4463  * @param buf_size the size of @a buf
   4464  * @param buf the input buffer
   4465  * @param[out] num_out where to store the decoded value (fits into 32-bit range)
   4466  * @param[out] bytes_decoded where to store the number of decoded bytes
   4467  * @return #mhd_HPACK_GET_NUM_RES_NO_ERROR on success,
   4468  *         error code otherwise
   4469  */
   4470 static MHD_FN_PAR_NONNULL_ALL_
   4471 MHD_FN_PAR_IN_SIZE_ (3, 2)
   4472 MHD_FN_PAR_OUT_ (4) MHD_FN_PAR_OUT_ (5) enum mhd_HpackGetNumResult
   4473 hpack_dec_number (uint_fast8_t first_byte_prefix_bits,
   4474                   const size_t buf_size,
   4475                   const uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
   4476                   uint_fast32_t *restrict num_out,
   4477                   size_t *restrict bytes_decoded)
   4478 {
   4479   /** The maximum value of the first byte. Also the mask for the first byte. */
   4480   const uint_fast8_t first_byte_val_max =
   4481     (uint_fast8_t)(b8ones >> first_byte_prefix_bits);
   4482   uint_fast8_t first_byte;
   4483   uint_fast32_t dec_num;
   4484   uint_fast8_t i;
   4485 
   4486   mhd_assert (0 != first_byte_prefix_bits);
   4487   mhd_assert (8 > first_byte_prefix_bits);
   4488 
   4489   first_byte = (buf[0] & first_byte_val_max);
   4490   if (first_byte_val_max != first_byte)
   4491   {
   4492     *num_out = (uint_fast32_t)first_byte;
   4493     *bytes_decoded = 1u;
   4494     return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4495   }
   4496   dec_num = first_byte;
   4497 
   4498 #  ifndef MHD_FAVOR_SMALL_CODE
   4499   /* Unrolled loop */
   4500   i = 1u;
   4501   if (buf_size == i)
   4502     return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4503   else
   4504   {
   4505     const uint_fast8_t cur_byte = buf[i];
   4506     const bool is_final = (0u == (cur_byte & 0x80u));
   4507     const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4508     dec_num += (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)));
   4509     if (is_final)
   4510     {
   4511       *num_out = dec_num;
   4512       *bytes_decoded = (size_t)(i + 1u);
   4513       return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4514     }
   4515   }
   4516 
   4517   i = 2u;
   4518   if (buf_size == i)
   4519     return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4520   else
   4521   {
   4522     const uint_fast8_t cur_byte = buf[i];
   4523     const bool is_final = (0u == (cur_byte & 0x80u));
   4524     const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4525     dec_num += (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)));
   4526     if (is_final)
   4527     {
   4528       *num_out = dec_num;
   4529       *bytes_decoded = (size_t)(i + 1u);
   4530       return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4531     }
   4532   }
   4533 
   4534   i = 3u;
   4535   if (buf_size == i)
   4536     return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4537   else
   4538   {
   4539     const uint_fast8_t cur_byte = buf[i];
   4540     const bool is_final = (0u == (cur_byte & 0x80u));
   4541     const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4542     dec_num += (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)));
   4543     if (is_final)
   4544     {
   4545       *num_out = dec_num;
   4546       *bytes_decoded = (size_t)(i + 1u);
   4547       return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4548     }
   4549   }
   4550 
   4551   i = 4u;
   4552   if (buf_size == i)
   4553     return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4554   else
   4555   {
   4556     const uint_fast8_t cur_byte = buf[i];
   4557     const bool is_final = (0u == (cur_byte & 0x80u));
   4558     const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4559     dec_num += (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)));
   4560     if (is_final)
   4561     {
   4562       *num_out = dec_num;
   4563       *bytes_decoded = (size_t)(i + 1u);
   4564       return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4565     }
   4566   }
   4567 
   4568   i = 5u;
   4569 #  else  /* MHD_FAVOR_SMALL_CODE */
   4570   /* First four bytes cannot overflow the output */
   4571   for (i = 1u; 4u >= i; ++i)
   4572   {
   4573     if (buf_size == i)
   4574       return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4575     else
   4576     {
   4577       const uint_fast8_t cur_byte = buf[i];
   4578       const bool is_final = (0u == (cur_byte & 0x80u));
   4579       const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4580       dec_num += (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)))
   4581       ;
   4582       if (is_final)
   4583       {
   4584         *num_out = dec_num;
   4585         *bytes_decoded = (size_t)(i + 1u);
   4586         return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4587       }
   4588     }
   4589   }
   4590 #  endif /* MHD_FAVOR_SMALL_CODE */
   4591 
   4592   mhd_assert (0u == (dec_num >> 29u));
   4593   mhd_assert (5u == i);
   4594   if (buf_size == i)
   4595     return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4596   else
   4597   { /* Handle the fifth byte with overflow checks */
   4598     const uint_fast8_t cur_byte = buf[i];
   4599     const bool is_final = (0u == (cur_byte & 0x80u));
   4600     const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4601     const uint_fast32_t add_val =
   4602       (uint_fast32_t)(((uint_fast32_t)byte_val) << (7u * (i - 1u)));
   4603     if (byte_val != ((add_val & 0xFFFFFFFFu) >> (7u * (i - 1u))))
   4604       return mhd_HPACK_GET_NUM_RES_TOO_LARGE; /* Failure exit point */
   4605     dec_num += add_val;
   4606     if ((dec_num & 0xFFFFFFFFu) < add_val)
   4607       return mhd_HPACK_GET_NUM_RES_TOO_LARGE; /* Failure exit point */
   4608     else if (is_final)
   4609     {
   4610       *num_out = dec_num;
   4611       *bytes_decoded = (size_t)(i + 1u);
   4612       return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4613     }
   4614   }
   4615 
   4616   /* Process possible extra zero-valued tail bytes */
   4617   while (++i <= mhd_hpack_num_max_bytes)
   4618   {
   4619     if (buf_size == i)
   4620       return mhd_HPACK_GET_NUM_RES_INCOMPLETE; /* Failure exit point */
   4621     else
   4622     {
   4623       const uint_fast8_t cur_byte = buf[i];
   4624       const bool is_final = (0u == (cur_byte & 0x80u));
   4625       const uint_fast8_t byte_val = (uint_fast8_t)(cur_byte & 0x7Fu);
   4626       if (0u != byte_val)
   4627         return mhd_HPACK_GET_NUM_RES_TOO_LARGE; /* Failure exit point */
   4628       else if (is_final)
   4629       {
   4630         *num_out = dec_num;
   4631         *bytes_decoded = (size_t)(i + 1u);
   4632         return mhd_HPACK_GET_NUM_RES_NO_ERROR; /* Success exit point */
   4633       }
   4634     }
   4635   }
   4636 
   4637   return mhd_HPACK_GET_NUM_RES_TOO_LONG; /* Failure exit point */
   4638 }
   4639 
   4640 
   4641 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   4642 MHD_FN_PAR_OUT_ (1) bool
   4643 mhd_hpack_dec_init (struct mhd_HpackDecContext *hk_dec)
   4644 {
   4645   hk_dec->dyn = mhd_dtbl_create (mhd_hpack_def_dyn_table_size);
   4646 
   4647   if (NULL == hk_dec->dyn)
   4648     return false; /* Failure exit point */
   4649 
   4650   mhd_assert (mhd_hpack_def_dyn_table_size == \
   4651               mhd_dtbl_get_table_max_size (hk_dec->dyn));
   4652 
   4653   hk_dec->max_allowed_dyn_size = mhd_hpack_def_dyn_table_size;
   4654   hk_dec->last_remote_dyn_size = hk_dec->max_allowed_dyn_size;
   4655 
   4656   return true; /* Success exit point */
   4657 }
   4658 
   4659 
   4660 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   4661 MHD_FN_PAR_INOUT_ (1) void
   4662 mhd_hpack_dec_deinit (struct mhd_HpackDecContext *hk_dec)
   4663 {
   4664   if (NULL == hk_dec->dyn)
   4665     return; /* Nothing to de-initialise */
   4666 
   4667   mhd_dtbl_destroy (hk_dec->dyn);
   4668   hk_dec->dyn = NULL;
   4669 }
   4670 
   4671 
   4672 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   4673 MHD_FN_PAR_INOUT_ (1) void
   4674 mhd_hpack_dec_set_allowed_dyn_size (struct mhd_HpackDecContext *hk_dec,
   4675                                     size_t new_allowed_dyn_size)
   4676 {
   4677   mhd_assert (mhd_DTBL_MAX_SIZE >= new_allowed_dyn_size);
   4678   hk_dec->max_allowed_dyn_size = new_allowed_dyn_size;
   4679 }
   4680 
   4681 
   4682 /**
   4683  * Ensure that any pending dynamic table resize is applied before decoding
   4684  * fields.
   4685  * Also check for possible missing Dynamic Table Size Update messages (after
   4686  * reception of ACK for settings reducing the maximum table size).
   4687  * @param hk_dec pointer to the decoder context
   4688  * @return non-error decoder result on success;
   4689  *         an error code if resize is disallowed or memory allocation fails
   4690  */
   4691 static enum mhd_HpackDecResult
   4692 dec_check_resize_pending (struct mhd_HpackDecContext *restrict hk_dec)
   4693 {
   4694   mhd_assert (mhd_DTBL_MAX_SIZE >= hk_dec->last_remote_dyn_size);
   4695   if (hk_dec->max_allowed_dyn_size < hk_dec->last_remote_dyn_size)
   4696     return mhd_HPACK_DEC_RES_DYN_SIZE_UPD_MISSING; /* Failure exit point */
   4697 
   4698   if (mhd_dtbl_get_table_max_size (hk_dec->dyn) != hk_dec->last_remote_dyn_size)
   4699   {
   4700     /* Resize must be performed before processing any headers data */
   4701     if (!mhd_dtbl_resize (&(hk_dec->dyn),
   4702                           hk_dec->last_remote_dyn_size))
   4703       return mhd_HPACK_DEC_RES_ALLOC_ERR; /* Failure exit point */
   4704   }
   4705 
   4706   mhd_assert (mhd_dtbl_get_table_max_size (hk_dec->dyn) \
   4707               == hk_dec->last_remote_dyn_size);
   4708   return mhd_HPACK_DEC_RES_NEW_FIELD; /* Success, return any non-error code */
   4709 }
   4710 
   4711 
   4712 /**
   4713  * Decode an indexed header field and write "name\0value\0" to @a out_buff.
   4714  * @param hk_dec the decoder context
   4715  * @param enc_data_size the size of @a enc_data
   4716  * @param enc_data the encoded data
   4717  * @param out_buff_size the size of @a out_buff
   4718  * @param[out] out_buff the output buffer for "name\0value\0"
   4719  * @param[out] name_len set to the length of the name, not counting
   4720  *                      terminating zero
   4721  * @param[out] val_len set to the length of the value, not counting
   4722  *                     terminating zero
   4723  * @param[out] bytes_decoded set to the number of decoded bytes
   4724  * @return #mhd_HPACK_DEC_RES_NEW_FIELD on success or an error code
   4725  */
   4726 static MHD_FN_PAR_NONNULL_ALL_
   4727 MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_OUT_SIZE_ (5, 4)
   4728 MHD_FN_PAR_OUT_ (6) MHD_FN_PAR_OUT_ (7)
   4729 MHD_FN_PAR_OUT_ (8) enum mhd_HpackDecResult
   4730 hpack_dec_field_indexed (struct mhd_HpackDecContext *restrict hk_dec,
   4731                          size_t enc_data_size,
   4732                          const uint8_t *restrict enc_data,
   4733                          size_t out_buff_size,
   4734                          char *restrict out_buff,
   4735                          size_t *restrict name_len,
   4736                          size_t *restrict val_len,
   4737                          size_t *restrict bytes_decoded)
   4738 {
   4739   enum mhd_HpackDecResult res;
   4740   enum mhd_HpackGetNumResult dec_res;
   4741   size_t idx_enc_len;
   4742   uint_fast32_t field_idx;
   4743   struct mhd_BufferConst idx_name;
   4744   struct mhd_BufferConst idx_value;
   4745 
   4746   mhd_assert (1u == (enc_data[0] >> 7u));
   4747   mhd_assert (0u != out_buff_size);
   4748 
   4749   /* If any dynamic table resize is pending, it must be performed before
   4750      header strings processing. */
   4751   res = dec_check_resize_pending (hk_dec);
   4752   if (mhd_HPACK_DEC_RES_IS_ERR (res))
   4753     return res;
   4754 
   4755   dec_res = hpack_dec_number (1u,
   4756                               enc_data_size,
   4757                               enc_data,
   4758                               &field_idx,
   4759                               &idx_enc_len);
   4760   switch (dec_res)
   4761   {
   4762   case mhd_HPACK_GET_NUM_RES_INCOMPLETE:
   4763     return mhd_HPACK_DEC_RES_INCOMPLETE; /* Failure exit point */
   4764   case mhd_HPACK_GET_NUM_RES_TOO_LARGE:
   4765     return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   4766   case mhd_HPACK_GET_NUM_RES_TOO_LONG:
   4767     return mhd_HPACK_DEC_RES_NUMBER_TOO_LONG; /* Failure exit point */
   4768   case mhd_HPACK_GET_NUM_RES_NO_ERROR:
   4769     break;
   4770   default:
   4771     mhd_UNREACHABLE ();
   4772     return mhd_HPACK_DEC_RES_INTERNAL_ERR; /* Failure exit point */
   4773   }
   4774 
   4775   mhd_assert (0u != idx_enc_len);
   4776 
   4777   if (mhd_COND_HARDLY_EVER (mhd_HPACK_MAX_POSSIBLE_IDX < field_idx))
   4778     return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   4779 
   4780   if (!mhd_htbl_get_entry (hk_dec->dyn,
   4781                            (dtbl_idx_ft)field_idx,
   4782                            &idx_name,
   4783                            &idx_value))
   4784     return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   4785 
   4786   /* No math overflow check is needed here as both strings are already stored
   4787      in memory together with pointers. */
   4788   if (out_buff_size < (idx_name.size + idx_value.size + 2u))
   4789     return mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL; /* Failure exit point */
   4790 
   4791   memcpy (out_buff,
   4792           idx_name.data,
   4793           idx_name.size);
   4794   out_buff[idx_name.size] = '\0'; /* Zero-terminate field name */
   4795 
   4796   memcpy (out_buff + idx_name.size + 1u,
   4797           idx_value.data,
   4798           idx_value.size);
   4799   out_buff[idx_name.size + 1u + idx_value.size] = '\0'; /* Zero-terminate field value */
   4800 
   4801   *name_len = idx_name.size;
   4802   *val_len = idx_value.size;
   4803   *bytes_decoded = idx_enc_len;
   4804 
   4805   return mhd_HPACK_DEC_RES_NEW_FIELD;
   4806 }
   4807 
   4808 
   4809 /**
   4810  * Decode an HPACK string literal (with or without Huffman coding).
   4811  * The output string in @a out_buff is zero-terminated.
   4812  * @param enc_data_size the size of @a enc_data
   4813  * @param enc_data the pointer to the encoded data
   4814  * @param out_buff_size the size of @a out_buff
   4815  * @param[out] out_buff the output buffer for the decoded string,
   4816  *                      the output is zero-terminated
   4817  * @param[out] out_len set to the decoded string length,
   4818  *                     not counting zero-termination
   4819  * @param[out] bytes_decoded set to the number of decoded bytes
   4820  * @return #mhd_HPACK_DEC_RES_NEW_FIELD on success,
   4821  *        error code otherwise
   4822  */
   4823 static MHD_FN_PAR_NONNULL_ALL_
   4824 MHD_FN_PAR_IN_SIZE_ (2, 1) MHD_FN_PAR_OUT_SIZE_ (4, 3)
   4825 MHD_FN_PAR_OUT_ (5) MHD_FN_PAR_OUT_ (6) enum mhd_HpackDecResult
   4826 hpack_dec_string_literal (size_t enc_data_size,
   4827                           const uint8_t *restrict enc_data,
   4828                           size_t out_buff_size,
   4829                           char *restrict out_buff,
   4830                           size_t *restrict out_len,
   4831                           size_t *restrict bytes_decoded)
   4832 {
   4833   const bool is_huff_enc = (0u != (enc_data[0] & 0x80u));
   4834   uint_fast32_t enc_str_len;
   4835   enum mhd_HpackGetNumResult dec_res;
   4836   size_t enc_num_len;
   4837   size_t dec_str_len;
   4838 
   4839   mhd_assert (0u != enc_data_size);
   4840   mhd_assert (0u != out_buff_size);
   4841 
   4842   dec_res = hpack_dec_number (1u,
   4843                               enc_data_size,
   4844                               enc_data,
   4845                               &enc_str_len,
   4846                               &enc_num_len);
   4847   switch (dec_res)
   4848   {
   4849   case mhd_HPACK_GET_NUM_RES_INCOMPLETE:
   4850     return mhd_HPACK_DEC_RES_INCOMPLETE;/* Failure exit point */
   4851   case mhd_HPACK_GET_NUM_RES_TOO_LARGE:
   4852     return mhd_HPACK_DEC_RES_STRING_TOO_LONG; /* Failure exit point */
   4853   case mhd_HPACK_GET_NUM_RES_TOO_LONG:
   4854     return mhd_HPACK_DEC_RES_NUMBER_TOO_LONG; /* Failure exit point */
   4855   case mhd_HPACK_GET_NUM_RES_NO_ERROR:
   4856     break;
   4857   default:
   4858     mhd_UNREACHABLE ();
   4859     return mhd_HPACK_DEC_RES_INTERNAL_ERR; /* Failure exit point */
   4860   }
   4861 
   4862   mhd_assert (0u != enc_num_len);
   4863   mhd_assert (enc_num_len <= enc_data_size);
   4864 
   4865   if ((enc_data_size - enc_num_len) < enc_str_len)
   4866     return mhd_HPACK_DEC_RES_INCOMPLETE; /* Failure exit point */
   4867 
   4868   if (mhd_COND_HARDLY_EVER (0u == enc_str_len))
   4869     dec_str_len = 0; /* Zero length string, can be Huffman-encoded or not */
   4870   else if (is_huff_enc)
   4871   { /* String with Huffman encoding */
   4872     enum mhd_H2HuffDecodeRes huff_dec_res;
   4873 
   4874     /* mhd_h2_huffman_decode() will check whether the output buffer is large
   4875        enough. */
   4876     dec_str_len = mhd_h2_huffman_decode ((size_t)enc_str_len,
   4877                                          enc_data + enc_num_len,
   4878                                          out_buff_size - 1u, /* leave one byte for zero-termination */
   4879                                          out_buff,
   4880                                          &huff_dec_res);
   4881     switch (huff_dec_res)
   4882     {
   4883     case MHD_H2_HUFF_DEC_RES_NO_SPACE:
   4884       return mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL; /* Failure exit point */
   4885     case MHD_H2_HUFF_DEC_RES_BROKEN_DATA:
   4886       return mhd_HPACK_DEC_RES_HUFFMAN_ERR; /* Failure exit point */
   4887       break;
   4888     case MHD_H2_HUFF_DEC_RES_OK:
   4889       break;
   4890     default:
   4891       mhd_UNREACHABLE ();
   4892       return mhd_HPACK_DEC_RES_INTERNAL_ERR; /* Failure exit point */
   4893     }
   4894     mhd_assert (0u != dec_str_len);
   4895     mhd_assert (MHD_H2_HUFF_DEC_RES_OK == huff_dec_res);
   4896     mhd_assert (dec_str_len < out_buff_size);
   4897   }
   4898   else
   4899   { /* String without Huffman encoding */
   4900     if (out_buff_size <= enc_str_len) /* leave one byte for zero-termination */
   4901       return mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL; /* Failure exit point */
   4902 
   4903     dec_str_len = (size_t)enc_str_len;
   4904     memcpy (out_buff,
   4905             enc_data + enc_num_len,
   4906             dec_str_len);
   4907   }
   4908 
   4909   mhd_assert (out_buff_size > dec_str_len);
   4910 
   4911   out_buff[dec_str_len] = '\0'; /* Zero-terminate the result */
   4912   *out_len = dec_str_len;
   4913   *bytes_decoded = enc_num_len + (size_t)enc_str_len;
   4914   return mhd_HPACK_DEC_RES_NEW_FIELD; /* Return any non-error code */
   4915 }
   4916 
   4917 
   4918 /**
   4919  * Decode a literal header field (with or without indexing) and write
   4920  * "name\0value\0" to the output buffer @a out_buff.
   4921  * If @a with_indexing is 'true', the decoded field is inserted into the
   4922  * dynamic table.
   4923  * @param hk_dec the decoder context
   4924  * @param enc_data_size the size of @a enc_data
   4925  * @param enc_data the encoded data
   4926  * @param out_buff_size the size of @a out_buff
   4927  * @param with_indexing non-zero to insert the field into the dynamic table
   4928  * @param[out] out_buff output the buffer for the decoded strings
   4929  * @param[out] name_len set to the length of the name, not counting
   4930  *                      zero-terminating
   4931  * @param[out] val_len set to the length of the value, not counting
   4932  *                     zero-terminating
   4933  * @param[out] bytes_decoded set to the number of decoded bytes
   4934  * @return #mhd_HPACK_DEC_RES_NEW_FIELD on success,
   4935  *        error code otherwise
   4936  */
   4937 static MHD_FN_PAR_NONNULL_ALL_
   4938 MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_OUT_SIZE_ (6, 5)
   4939 MHD_FN_PAR_OUT_ (7) MHD_FN_PAR_OUT_ (8)
   4940 MHD_FN_PAR_OUT_ (9) enum mhd_HpackDecResult
   4941 hpack_dec_field_literal (struct mhd_HpackDecContext *restrict hk_dec,
   4942                          size_t enc_data_size,
   4943                          const uint8_t *restrict enc_data,
   4944                          bool with_indexing,
   4945                          size_t out_buff_size,
   4946                          char *restrict out_buff,
   4947                          size_t *restrict name_len,
   4948                          size_t *restrict val_len,
   4949                          size_t *restrict bytes_decoded)
   4950 {
   4951   const uint_fast8_t prfx_bits = (with_indexing ? 2u : 4u);
   4952   enum mhd_HpackDecResult res;
   4953   size_t pos;
   4954   size_t pos_incr;
   4955   uint_fast32_t name_idx;
   4956 
   4957   mhd_assert (with_indexing \
   4958               || (1u == (enc_data[0] >> 4u)) || (0u == (enc_data[0] >> 4u)));
   4959   mhd_assert (!with_indexing \
   4960               || (1u == (enc_data[0] >> 6u)));
   4961   mhd_assert (0u != enc_data_size);
   4962   mhd_assert (2u <= out_buff_size);
   4963 
   4964   /* If any dynamic table resize is pending, it must be performed before
   4965      headers strings processing. */
   4966   res = dec_check_resize_pending (hk_dec);
   4967   if (mhd_HPACK_DEC_RES_IS_ERR (res))
   4968     return res;
   4969 
   4970   pos = 0u;
   4971 #  ifndef MHD_FAVOR_SMALL_CODE
   4972   if (0u == (enc_data[0] & (b8ones >> prfx_bits)))
   4973   {
   4974     name_idx = 0u; /* Shortcut for frequent case */
   4975     pos_incr = 1u;
   4976   }
   4977   else
   4978 #  endif /* ! MHD_FAVOR_SMALL_CODE */
   4979   if (1)
   4980   {
   4981     enum mhd_HpackGetNumResult dec_res;
   4982     dec_res = hpack_dec_number (prfx_bits,
   4983                                 enc_data_size,
   4984                                 enc_data,
   4985                                 &name_idx,
   4986                                 &pos_incr);
   4987     switch (dec_res)
   4988     {
   4989     case mhd_HPACK_GET_NUM_RES_INCOMPLETE:
   4990       return mhd_HPACK_DEC_RES_INCOMPLETE; /* Failure exit point */
   4991     case mhd_HPACK_GET_NUM_RES_TOO_LARGE:
   4992       return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   4993     case mhd_HPACK_GET_NUM_RES_TOO_LONG:
   4994       return mhd_HPACK_DEC_RES_NUMBER_TOO_LONG; /* Failure exit point */
   4995     case mhd_HPACK_GET_NUM_RES_NO_ERROR:
   4996       break;
   4997     default:
   4998       mhd_UNREACHABLE ();
   4999       return mhd_HPACK_DEC_RES_INTERNAL_ERR; /* Failure exit point */
   5000     }
   5001 
   5002     mhd_assert (0u != pos_incr);
   5003 #  ifndef MHD_FAVOR_SMALL_CODE
   5004     mhd_assert (0u != name_idx);
   5005 #  endif /* ! MHD_FAVOR_SMALL_CODE */
   5006   }
   5007 
   5008   pos += pos_incr;
   5009   mhd_assert (0u != pos);
   5010 
   5011   if (enc_data_size == pos)
   5012     return mhd_HPACK_DEC_RES_INCOMPLETE; /* Failure exit point */
   5013 
   5014   if (0u == name_idx)
   5015   { /* Literal name */
   5016     mhd_assert (1u == pos);
   5017     pos = 1u; /* Help compiler to optimise */
   5018 
   5019     res = hpack_dec_string_literal (enc_data_size - pos,
   5020                                     enc_data + pos,
   5021                                     out_buff_size - 1u,      /* At least one char for the value string */
   5022                                     out_buff,
   5023                                     name_len,
   5024                                     &pos_incr);
   5025     if (mhd_HPACK_DEC_RES_IS_ERR (res))
   5026       return res; /* Failure exit point */
   5027   }
   5028   else
   5029   { /* Indexed name */
   5030     struct mhd_BufferConst idx_name;
   5031     struct mhd_BufferConst idx_value; /* extracted value is unused */
   5032 
   5033     if (mhd_COND_HARDLY_EVER (mhd_HPACK_MAX_POSSIBLE_IDX < name_idx))
   5034       return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   5035 
   5036     if (!mhd_htbl_get_entry (hk_dec->dyn,
   5037                              (dtbl_idx_ft)name_idx,
   5038                              &idx_name,
   5039                              &idx_value))
   5040       return mhd_HPACK_DEC_RES_HPACK_BAD_IDX; /* Failure exit point */
   5041 
   5042     if (idx_name.size >= (out_buff_size - 1u))
   5043       return mhd_HPACK_DEC_RES_BUFFER_TOO_SMALL; /* Failure exit point */
   5044 
   5045     memcpy (out_buff,
   5046             idx_name.data,
   5047             idx_name.size);
   5048     out_buff[idx_name.size] = '\0'; /* Zero-terminate resulting string */
   5049     *name_len = idx_name.size;
   5050 
   5051     pos_incr = 0u;
   5052   }
   5053   pos += pos_incr;
   5054 
   5055   if (enc_data_size == pos)
   5056     return mhd_HPACK_DEC_RES_INCOMPLETE; /* Failure exit point */
   5057 
   5058   mhd_assert (out_buff_size >= (*name_len + 2u));
   5059   res = hpack_dec_string_literal (enc_data_size - pos,
   5060                                   enc_data + pos,
   5061                                   out_buff_size - (*name_len + 1u),
   5062                                   out_buff + (*name_len + 1u),
   5063                                   val_len,
   5064                                   &pos_incr);
   5065   if (mhd_HPACK_DEC_RES_IS_ERR (res))
   5066     return res; /* Failure exit point */
   5067 
   5068   pos += pos_incr;
   5069   *bytes_decoded = pos;
   5070 
   5071   if (with_indexing)
   5072     mhd_dtbl_new_entry (hk_dec->dyn,
   5073                         *name_len,
   5074                         out_buff,
   5075                         *val_len,
   5076                         out_buff + (*name_len) + 1u);
   5077 
   5078   return mhd_HPACK_DEC_RES_NEW_FIELD;
   5079 }
   5080 
   5081 
   5082 /**
   5083  * Decode and apply a Dynamic Table Size Update.
   5084  * Performs eviction only; actual resize is deferred until before first header
   5085  * decoding.
   5086  * @param hk_dec the decoder context
   5087  * @param enc_data_size the size of @a enc_data
   5088  * @param enc_data the encoded data
   5089  * @param[out] bytes_decoded set to the number of decoded bytes
   5090  * @return #mhd_HPACK_DEC_RES_NO_NEW_FIELD on success,
   5091  *         error code otherwise
   5092  */
   5093 static MHD_FN_PAR_IN_SIZE_ (3, 2)
   5094 MHD_FN_PAR_OUT_ (4) enum mhd_HpackDecResult
   5095 dec_update_dyn_size (struct mhd_HpackDecContext *restrict hk_dec,
   5096                      const size_t enc_data_size,
   5097                      const uint8_t *restrict enc_data,
   5098                      size_t *restrict bytes_decoded)
   5099 {
   5100   uint_fast32_t new_dyn_size;
   5101   size_t used_bytes;
   5102   enum mhd_HpackGetNumResult dec_res;
   5103 
   5104   mhd_assert ((1u == (enc_data[0] >> 5u)) \
   5105               && "the first byte must be the dynamic table update signal");
   5106   dec_res = hpack_dec_number (3u,
   5107                               enc_data_size,
   5108                               enc_data,
   5109                               &new_dyn_size,
   5110                               &used_bytes);
   5111   switch (dec_res)
   5112   {
   5113   case mhd_HPACK_GET_NUM_RES_INCOMPLETE:
   5114     return mhd_HPACK_DEC_RES_INCOMPLETE;                /* Failure exit point */
   5115   case mhd_HPACK_GET_NUM_RES_TOO_LARGE:
   5116     return mhd_HPACK_DEC_RES_DYN_SIZE_UPD_TOO_LARGE;    /* Failure exit point */
   5117   case mhd_HPACK_GET_NUM_RES_TOO_LONG:
   5118     return mhd_HPACK_DEC_RES_NUMBER_TOO_LONG;           /* Failure exit point */
   5119   case mhd_HPACK_GET_NUM_RES_NO_ERROR:
   5120     break;
   5121   default:
   5122     mhd_UNREACHABLE ();
   5123     return mhd_HPACK_DEC_RES_INTERNAL_ERR;              /* Failure exit point */
   5124   }
   5125   mhd_assert (0u != used_bytes);
   5126 
   5127   if (hk_dec->max_allowed_dyn_size < new_dyn_size)
   5128     return mhd_HPACK_DEC_RES_DYN_SIZE_UPD_TOO_LARGE;    /* Failure exit point */
   5129 
   5130   mhd_assert (mhd_DTBL_MAX_SIZE >= new_dyn_size);
   5131 
   5132   /* Only evict here, no resize yet to avoid repetitive realloc() calls if
   5133      remote sends multiple table size updates in a row. */
   5134   mhd_dtbl_evict_to_size (hk_dec->dyn,
   5135                           (size_t)new_dyn_size);
   5136 
   5137   hk_dec->last_remote_dyn_size = (size_t)new_dyn_size;
   5138 
   5139   *bytes_decoded = used_bytes;
   5140   return mhd_HPACK_DEC_RES_NO_NEW_FIELD; /* Success exit point */
   5141 }
   5142 
   5143 
   5144 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   5145 MHD_FN_PAR_INOUT_ (1)
   5146 MHD_FN_PAR_IN_SIZE_ (3, 2)
   5147 MHD_FN_PAR_OUT_SIZE_ (5, 4)
   5148 MHD_FN_PAR_OUT_ (6) MHD_FN_PAR_OUT_ (7)
   5149 MHD_FN_PAR_OUT_ (8) enum mhd_HpackDecResult
   5150 mhd_hpack_dec_data (struct mhd_HpackDecContext *restrict hk_dec,
   5151                     size_t enc_data_size,
   5152                     const uint8_t *restrict enc_data,
   5153                     size_t out_buff_size,
   5154                     char *restrict out_buff,
   5155                     size_t *restrict name_len,
   5156                     size_t *restrict val_len,
   5157                     size_t *restrict bytes_decoded)
   5158 {
   5159   uint_fast8_t action_id;
   5160 
   5161   mhd_assert (0u != enc_data_size);
   5162   mhd_assert (2u <= out_buff_size);
   5163 
   5164   action_id = enc_data[0] >> 4u;
   5165 
   5166   switch (action_id)
   5167   {
   5168   case (1u << 3u) + 0u:
   5169   case (1u << 3u) + 1u:
   5170   case (1u << 3u) + 2u:
   5171   case (1u << 3u) + 3u:
   5172   case (1u << 3u) + 4u:
   5173   case (1u << 3u) + 5u:
   5174   case (1u << 3u) + 6u:
   5175   case (1u << 3u) + 7u:
   5176     /* Indexed field */
   5177     return hpack_dec_field_indexed (hk_dec,
   5178                                     enc_data_size,
   5179                                     enc_data,
   5180                                     out_buff_size,
   5181                                     out_buff,
   5182                                     name_len,
   5183                                     val_len,
   5184                                     bytes_decoded);
   5185   case (1u << 2u) + 0u:
   5186   case (1u << 2u) + 1u:
   5187   case (1u << 2u) + 2u:
   5188   case (1u << 2u) + 3u:
   5189     /* Literal field with indexing */
   5190     return hpack_dec_field_literal (hk_dec,
   5191                                     enc_data_size,
   5192                                     enc_data,
   5193                                     true,
   5194                                     out_buff_size,
   5195                                     out_buff,
   5196                                     name_len,
   5197                                     val_len,
   5198                                     bytes_decoded);
   5199   case 0u << 0u:
   5200     /* Literal field without indexing */
   5201     return hpack_dec_field_literal (hk_dec,
   5202                                     enc_data_size,
   5203                                     enc_data,
   5204                                     false,
   5205                                     out_buff_size,
   5206                                     out_buff,
   5207                                     name_len,
   5208                                     val_len,
   5209                                     bytes_decoded);
   5210   case 1u << 0u:
   5211     /* Literal field never indexed */
   5212     return hpack_dec_field_literal (hk_dec,
   5213                                     enc_data_size,
   5214                                     enc_data,
   5215                                     false,
   5216                                     out_buff_size,
   5217                                     out_buff,
   5218                                     name_len,
   5219                                     val_len,
   5220                                     bytes_decoded);
   5221   case (1u << 1u) + 0u:
   5222   case (1u << 1u) + 1u:
   5223     /* Dynamic table size update */
   5224     return dec_update_dyn_size (hk_dec,
   5225                                 enc_data_size,
   5226                                 enc_data,
   5227                                 bytes_decoded);
   5228   default:
   5229     break;
   5230   }
   5231   mhd_UNREACHABLE ();
   5232   return mhd_HPACK_DEC_RES_INTERNAL_ERR;
   5233 }
   5234 
   5235 
   5236 /* ****** _____________ End of HPACK headers decoding ______________ ****** */
   5237 
   5238 /* ****** ----------------- HPACK headers encoding ----------------- ****** */
   5239 
   5240 /**
   5241  * Compute the number of bytes required to encode an HPACK integer.
   5242  *
   5243  * Implements the integer encoding algorithm from RFC 7541, Section 5.1.
   5244  * The @a prefix_bits parameter specifies the count of fixed most-significant
   5245  * bits in the first byte (e.g., 1 for "1xxxxxxx", 2 for "01xxxxxx",
   5246  * 3 for "001xxxxx", 4 for "0000xxxx"/"0001xxxx").
   5247  * The number of value bits available in the first byte is (8 - @a prefix_bits).
   5248  *
   5249  * @param[in] prefix_bits the count of fixed high-order bits in the first byte;
   5250  *                        must be greater than zero and less than 8
   5251  * @param[in]  number the value to encode, must fit 32 bits
   5252  * @return the total number of bytes needed (always non-zero)
   5253  */
   5254 static size_t
   5255 hpack_number_len (uint_fast8_t prefix_bits,
   5256                   uint_fast32_t number)
   5257 {
   5258   const uint_fast8_t first_byte_val_max =
   5259     (uint_fast8_t)(b8ones >> prefix_bits);
   5260   uint_least32_t val_for_next_bytes;
   5261 
   5262   mhd_assert (0u != prefix_bits);
   5263   mhd_assert (8u > prefix_bits);
   5264   mhd_assert ((number & 0xFFFFFFFFu) == number);
   5265 
   5266   if (first_byte_val_max > number) /* the number must be strictly less than */
   5267     return 1u;
   5268   val_for_next_bytes = (uint_least32_t)(number - first_byte_val_max);
   5269   if (0 == val_for_next_bytes)
   5270     return 2u;
   5271   return (uint_fast8_t) \
   5272          ((mhd_BIT_WIDTH32NZ (val_for_next_bytes) + 6u) / 7u) + 1u;
   5273 }
   5274 
   5275 
   5276 /**
   5277  * Encode an HPACK integer into the provided output buffer.
   5278  *
   5279  * Encodes @a number according to RFC 7541, Section 5.1 using the given
   5280  * first-byte prefix. The @a first_byte_prefix must have its lowest
   5281  * (8 - @a first_byte_prefix_bits) bits cleared; these bits will be filled
   5282  * with the encoded value.
   5283  *
   5284  * @param[in]  first_byte_prefix the first byte with fixed MSB pattern set;
   5285  *                                   lower value bits must be zero
   5286  * @param[in]  first_byte_prefix_bits the count of fixed MSBs in the first byte
   5287  *                                    (1 for 1xxxxxxx, 2 for 01xxxxxx, etc.)
   5288  * @param[in]  number the value to encode, must fit 32 bits
   5289  * @param[in]  buf_size the size of @a buf in bytes,
   5290  *                      must not be zero
   5291  * @param[out] buf the output buffer to write the encoded bytes
   5292  * @return the number of bytes written on success;
   5293  *         zero if output buffer is too small to fit the number encoded
   5294  */
   5295 static MHD_FN_PAR_OUT_SIZE_ (5, 4) size_t
   5296 hpack_put_number_to_buf (uint_fast8_t first_byte_prefix,
   5297                          uint_fast8_t first_byte_prefix_bits,
   5298                          uint_fast32_t number,
   5299                          size_t buf_size,
   5300                          uint8_t buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)])
   5301 {
   5302   const uint_fast8_t first_byte_val_max =
   5303     (uint_fast8_t)(b8ones >> first_byte_prefix_bits);
   5304   uint_fast32_t number_left;
   5305   uint_fast8_t i;
   5306 
   5307   mhd_assert (0u == (first_byte_prefix & first_byte_val_max));
   5308   mhd_assert (0u == ((first_byte_prefix >> 4u) >> 4u));
   5309   mhd_assert (0u != first_byte_prefix_bits);
   5310   mhd_assert (8u > first_byte_prefix_bits);
   5311   mhd_assert ((number & 0xFFFFFFFFu) == number);
   5312   mhd_assert (0u != buf_size);
   5313 
   5314   if (first_byte_val_max > number) /* the number must be strictly less than */
   5315   {
   5316     buf[0] = (uint8_t)(first_byte_prefix | (uint8_t)number);
   5317     return 1u;
   5318   }
   5319   buf[0] = (uint8_t)(first_byte_prefix | first_byte_val_max);
   5320   number_left = number - first_byte_val_max;
   5321   for (i = 1u; mhd_COND_PREDOMINANTLY (i < buf_size); ++i)
   5322   {
   5323     const uint8_t cur_byte = (uint8_t)(number_left & 0x7Fu);
   5324     number_left >>= 7u;
   5325     if (0 == number_left)
   5326     {
   5327       mhd_assert (0u == (cur_byte & 0x80u));
   5328       buf[i] = cur_byte;
   5329       return i + 1u; /* Success exit point */
   5330     }
   5331     buf[i] = (uint8_t)(cur_byte | 0x80u);
   5332     mhd_assert (6u > i);
   5333   }
   5334   return 0u; /* Not enough space */
   5335 }
   5336 
   5337 
   5338 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   5339 MHD_FN_PAR_OUT_ (1) bool
   5340 mhd_hpack_enc_init (struct mhd_HpackEncContext *hk_enc)
   5341 {
   5342   hk_enc->dyn = mhd_dtbl_create (mhd_hpack_def_dyn_table_size);
   5343 
   5344   if (NULL == hk_enc->dyn)
   5345     return false; /* Failure exit point */
   5346 
   5347   mhd_assert (mhd_hpack_def_dyn_table_size == \
   5348               mhd_dtbl_get_table_max_size (hk_enc->dyn));
   5349 
   5350   /* Set all sizes to the same initial value */
   5351   hk_enc->dyn_size_peer = mhd_hpack_def_dyn_table_size;
   5352   hk_enc->dyn_size_new = hk_enc->dyn_size_peer;
   5353   hk_enc->dyn_size_smallest = hk_enc->dyn_size_peer;
   5354 
   5355   return true; /* Success exit point */
   5356 }
   5357 
   5358 
   5359 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   5360 MHD_FN_PAR_INOUT_ (1) void
   5361 mhd_hpack_enc_deinit (struct mhd_HpackEncContext *hk_enc)
   5362 {
   5363   if (NULL == hk_enc->dyn)
   5364     return; /* Nothing to deinit */
   5365 
   5366   mhd_dtbl_destroy (hk_enc->dyn);
   5367   hk_enc->dyn = NULL;
   5368 }
   5369 
   5370 
   5371 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   5372 MHD_FN_PAR_INOUT_ (1) void
   5373 mhd_hpack_enc_set_dyn_size (struct mhd_HpackEncContext *hk_enc,
   5374                             size_t new_dyn_size)
   5375 {
   5376   mhd_assert (mhd_DTBL_MAX_SIZE >= new_dyn_size);
   5377   if (hk_enc->dyn_size_smallest > new_dyn_size)
   5378     hk_enc->dyn_size_smallest = new_dyn_size;
   5379 
   5380   /* Postpone actual table resize to avoid several realloc() calls if
   5381      multiple table resizes are performed. */
   5382   hk_enc->dyn_size_new = new_dyn_size;
   5383 }
   5384 
   5385 
   5386 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   5387 MHD_FN_PAR_INOUT_ (1) bool
   5388 mhd_hpack_enc_dyn_resize (struct mhd_HpackEncContext *hk_enc)
   5389 {
   5390   mhd_assert (hk_enc->dyn_size_new >= hk_enc->dyn_size_smallest);
   5391 
   5392   if (mhd_dtbl_get_table_max_size (hk_enc->dyn) != hk_enc->dyn_size_new)
   5393   {
   5394 #  ifndef MHD_FAVOR_SMALL_CODE
   5395     /* This is just an optimisation to simplify eviction later */
   5396     mhd_dtbl_evict_to_size (hk_enc->dyn,
   5397                             hk_enc->dyn_size_smallest);
   5398 #  endif /* ! MHD_FAVOR_SMALL_CODE */
   5399 
   5400     if (mhd_COND_HARDLY_EVER (!mhd_dtbl_resize (&(hk_enc->dyn), \
   5401                                                 hk_enc->dyn_size_new)))
   5402       return false;
   5403 
   5404     mhd_assert (mhd_dtbl_get_table_max_size (hk_enc->dyn) == \
   5405                 hk_enc->dyn_size_new);
   5406   }
   5407 
   5408   return true;
   5409 }
   5410 
   5411 
   5412 /**
   5413  * Encode an indexed field representation (RFC 7541, Section 6.1).
   5414  *
   5415  * @param[in]  idx the 1-based field index, must be non-zero
   5416  * @param[in]  out_buff_size the size of @a out_buff in bytes,
   5417  *                           must not be zero
   5418  * @param[out] out_buff the output buffer to write the encoded field
   5419  * @param[out] bytes_encoded to be set to the number of bytes written to the
   5420  *                           @a out_buff
   5421  * @return 'true' on success;
   5422  *         'false' if the output buffer is too small
   5423  */
   5424 static MHD_FN_PAR_NONNULL_ALL_
   5425 MHD_FN_PAR_OUT_SIZE_ (3, 2) MHD_FN_PAR_OUT_ (4) bool
   5426 hpack_enc_field_indexed (dtbl_idx_ft idx,
   5427                          const size_t out_buff_size,
   5428                          uint8_t *restrict out_buff,
   5429                          size_t *restrict bytes_encoded)
   5430 {
   5431   mhd_constexpr uint_fast8_t field_indexed_prfx = (uint_fast8_t)(1u << 7u);
   5432   mhd_constexpr uint_fast8_t field_indexed_prfx_bits = 1u;
   5433   size_t pos;
   5434 
   5435   mhd_assert (0u != idx);
   5436   mhd_assert (mhd_HPACK_MAX_POSSIBLE_IDX >= idx);
   5437   mhd_assert (0u != out_buff_size);
   5438 
   5439   pos = hpack_put_number_to_buf (field_indexed_prfx,
   5440                                  field_indexed_prfx_bits,
   5441                                  idx,
   5442                                  out_buff_size,
   5443                                  out_buff);
   5444 
   5445   if (0u == pos)
   5446     return false; /* Not enough space in the output buffer */
   5447 
   5448   *bytes_encoded = pos;
   5449   return true;
   5450 }
   5451 
   5452 
   5453 /**
   5454  * Literal header indexing type for HPACK literal representations.
   5455  *
   5456  * Selects which literal form to use (RFC 7541, Sections 6.2.1-6.2.3).
   5457  */
   5458 enum MHD_FIXED_ENUM_ mhd_HpackEncLitIndexingType
   5459 {
   5460   /**
   5461    * "Literal Header Field with Incremental Indexing"
   5462    * RFC 7541, Section 6.2.1.
   5463    */
   5464   mhd_HPACK_ENC_LIT_IDX_TYPE_INDEXING,
   5465   /**
   5466    * "Literal Header Field without Indexing"
   5467    * RFC 7541, Section 6.2.2.
   5468    */
   5469   mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   5470   /**
   5471    * "Literal Header Field Never Indexed"
   5472    * RFC 7541, Section 6.2.3.
   5473    */
   5474   mhd_HPACK_ENC_LIT_IDX_TYPE_NEVER_INDEXING
   5475 };
   5476 
   5477 
   5478 /**
   5479  * Encode a string literal with optional Huffman coding (RFC 7541, Section 5.2).
   5480  *
   5481  * @param[in,out] hk_enc the encoder context
   5482  * @param[in]     str_data the field string to encode
   5483  * @param[in]     huffman_allowed set to 'true' to allow Huffman encoding
   5484  * @param[in]     out_buff_size the size of @a out_buff in bytes, could be zero
   5485  * @param[out]    out_buff the output buffer
   5486  * @param[out]    bytes_encoded to be set to the size of the encoded data
   5487  *                              written to the @a out_buff
   5488  * @return 'true' on success;
   5489  *         'false' if the output buffer is too small
   5490  */
   5491 static MHD_FN_PAR_NONNULL_ALL_
   5492 MHD_FN_PAR_IN_ (1)
   5493 MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool
   5494 hpack_enc_string_literal (const struct mhd_BufferConst *restrict str_data,
   5495                           bool huffman_allowed,
   5496                           const size_t out_buff_size,
   5497                           uint8_t *restrict out_buff,
   5498                           size_t *restrict bytes_encoded)
   5499 {
   5500   /** The prefix for Huffman-encoded string */
   5501   mhd_constexpr uint8_t huff_on_prfx = (uint8_t)(1u << 7u);
   5502   /** The prefix for literal string without Huffman encoding */
   5503   mhd_constexpr uint8_t huff_off_prfx = (uint8_t)(0u << 7u);
   5504   mhd_constexpr uint8_t huff_prfx_bits = 1u;
   5505   size_t enc_size;
   5506   size_t enc_size_enc_len;
   5507 
   5508   mhd_assert ((str_data->size & 0xFFFFFFFFu) == str_data->size);
   5509 
   5510   if (mhd_COND_ALMOST_NEVER (0u == str_data->size))
   5511   {
   5512     if (0u == out_buff_size)
   5513       return false;
   5514     /* If Huffman is allowed, encode zero size as "Huffman encoded" for
   5515        consistency. */
   5516     out_buff[0] = (huffman_allowed ? huff_on_prfx : huff_off_prfx);
   5517     *bytes_encoded = 1u;
   5518     return true;
   5519   }
   5520 
   5521   if (huffman_allowed)
   5522   {
   5523     uint_fast32_t est_enc_size;
   5524     size_t est_enc_size_enc_len;
   5525     bool is_limited_by_buff_size;
   5526 
   5527     est_enc_size =
   5528       mhd_h2_huffman_est_avg_size ((uint_fast32_t)str_data->size);
   5529     est_enc_size_enc_len = hpack_number_len (huff_prfx_bits,
   5530                                              est_enc_size);
   5531     if ((out_buff_size <= est_enc_size_enc_len)
   5532         || ((out_buff_size - est_enc_size_enc_len) < est_enc_size))
   5533     {
   5534       /* Probably the buffer is not large enough to encode the string */
   5535       /* Try as if the string were compressible to a minimal size */
   5536       est_enc_size =
   5537         mhd_h2_huffman_est_min_size ((uint_fast32_t)str_data->size);
   5538       est_enc_size_enc_len = hpack_number_len (huff_prfx_bits,
   5539                                                est_enc_size);
   5540       if (out_buff_size < (est_enc_size_enc_len + est_enc_size))
   5541         return false; /* The output buffer is not large enough */
   5542       is_limited_by_buff_size = true;
   5543     }
   5544     else
   5545       is_limited_by_buff_size =
   5546         ((out_buff_size - est_enc_size_enc_len) < str_data->size);
   5547 
   5548     mhd_assert (out_buff_size > est_enc_size_enc_len);
   5549     mhd_assert ((out_buff_size - est_enc_size_enc_len) \
   5550                 >= est_enc_size);
   5551     mhd_assert (is_limited_by_buff_size \
   5552                 || ((out_buff_size - est_enc_size_enc_len) >= str_data->size));
   5553 
   5554     /* Limit the size of the buffer for the encoded string to the size of
   5555        the original (not encoded) string or the size of the buffer (whatever
   5556        is smaller). By limiting the size of the buffer to the size of the
   5557        original string, Huffman encoding that grows larger than the original
   5558        is aborted early. */
   5559     enc_size =
   5560       mhd_h2_huffman_encode (str_data->size,
   5561                              str_data->data,
   5562                              (size_t)
   5563                              (is_limited_by_buff_size ?
   5564                               (out_buff_size - est_enc_size_enc_len) :
   5565                               str_data->size),
   5566                              out_buff + est_enc_size_enc_len);
   5567 
   5568     mhd_assert (out_buff_size - est_enc_size_enc_len >= enc_size);
   5569 
   5570     if (0u != enc_size)
   5571     {
   5572       /* Successfully Huffman-encoded the string */
   5573       enc_size_enc_len =
   5574         hpack_put_number_to_buf (huff_on_prfx,
   5575                                  huff_prfx_bits,
   5576                                  (uint_fast32_t)enc_size,
   5577                                  est_enc_size_enc_len,
   5578                                  out_buff);
   5579       if (mhd_COND_ALMOST_NEVER (0u == enc_size_enc_len))
   5580       {
   5581         /* The actual encoded size is larger than estimated */
   5582         size_t calc_enc_size_enc_len;
   5583 
   5584         mhd_assert (est_enc_size < enc_size);
   5585 
   5586         calc_enc_size_enc_len = hpack_number_len (huff_prfx_bits,
   5587                                                   (uint_fast32_t)enc_size);
   5588         if ((out_buff_size - enc_size) < calc_enc_size_enc_len)
   5589           return false; /* The output buffer is not large enough */
   5590 
   5591         memmove (out_buff + calc_enc_size_enc_len,
   5592                  out_buff + est_enc_size_enc_len,
   5593                  enc_size);
   5594 
   5595         enc_size_enc_len =
   5596           hpack_put_number_to_buf (huff_on_prfx,
   5597                                    huff_prfx_bits,
   5598                                    (uint_fast32_t)enc_size,
   5599                                    calc_enc_size_enc_len,
   5600                                    out_buff);
   5601         mhd_assert (calc_enc_size_enc_len == enc_size_enc_len);
   5602       }
   5603       else if (est_enc_size_enc_len != enc_size_enc_len)
   5604       {
   5605         mhd_assert (est_enc_size_enc_len > enc_size_enc_len);
   5606         memmove (out_buff + enc_size_enc_len,
   5607                  out_buff + est_enc_size_enc_len,
   5608                  enc_size);
   5609       }
   5610 
   5611       *bytes_encoded = (enc_size_enc_len + enc_size);
   5612       return true; /* Success exit point */
   5613     }
   5614     else /* 0u == enc_size */
   5615     {
   5616       /* Huffman-encoded version needs more space than provided */
   5617       /* If available space was less than needed to put the string without
   5618          Huffman encoding, then return failure here. */
   5619       if (is_limited_by_buff_size)
   5620         return false;
   5621     }
   5622     /* Retry without Huffman encoding */
   5623   }
   5624 
   5625   /* Put string without Huffman encoding */
   5626   enc_size = str_data->size;
   5627 
   5628   if (enc_size >= out_buff_size)
   5629     return false; /* The output buffer is not large enough */
   5630 
   5631   enc_size_enc_len =
   5632     hpack_put_number_to_buf (huff_off_prfx,
   5633                              huff_prfx_bits,
   5634                              (uint_fast32_t)enc_size,
   5635                              out_buff_size - enc_size,
   5636                              out_buff);
   5637 
   5638   if (0u == enc_size_enc_len)
   5639     return false; /* The output buffer is not large enough */
   5640 
   5641   mhd_assert ((out_buff_size - enc_size_enc_len) >= enc_size);
   5642 
   5643   memcpy (out_buff + enc_size_enc_len,
   5644           str_data->data,
   5645           enc_size);
   5646 
   5647   *bytes_encoded = (enc_size_enc_len + enc_size);
   5648   return true; /* Success exit point */
   5649 }
   5650 
   5651 
   5652 /**
   5653  * Encode a literal field (name by index reference or literal; value
   5654  * always literal).
   5655  *
   5656  * Produces one of the literal field representations (RFC 7541,
   5657  * Sections 6.2.1-6.2.3).
   5658  * The name may be encoded by index reference (if allowed) or literally; the
   5659  * value is always encoded literally.
   5660  * String representations may use Huffman coding if permitted.
   5661  *
   5662  * @param[in]  hk_enc the encoder context
   5663  * @param[in]  name the field name bytes and size
   5664  * @param[in]  name_idx the field name index if known,
   5665  *                      zero if index is not known or indexed name is not
   5666  *                      allowed, zero must not be used for pseudo-header
   5667  *                      names (names starting with ':'),
   5668  *                      when non-zero the name is encoded by index reference
   5669  *                      if any of @p name_idx_stat_allowed or
   5670  *                      @p name_idx_dyn_allowed is 'true'
   5671  * @param[in]  value the field value bytes and size
   5672  * @param[in]  msg_type the literal representation kind to use
   5673  * @param[in]  name_idx_stat_allowed allow name lookup in static table (or
   5674  *                                   use @p name_idx if provided) and encode
   5675  *                                   the name as a reference
   5676  * @param[in]  name_idx_dyn_allowed allow name lookup in dynamic table (or
   5677  *                                  use @p name_idx if provided) and encode
   5678  *                                  the name as a reference
   5679  * @param[in]  huffman_allowed set to 'true' if Huffman coding is allowed
   5680  * @param[in]  out_buff_size the size of @p out_buff in bytes,
   5681  *                           could be zero (the function will always fail
   5682  *                           if it is less than two)
   5683  * @param[out] out_buff the output buffer for the encoded field
   5684  * @param[out] bytes_encoded to be set to the number of bytes written to
   5685  *                              the @p out_buff
   5686  * @return 'true' on success;
   5687  *         'false' if the output buffer is too small
   5688  */
   5689 static MHD_FN_PAR_NONNULL_ALL_
   5690 MHD_FN_PAR_IN_ (1)
   5691 MHD_FN_PAR_IN_ (2) MHD_FN_PAR_IN_ (4)
   5692 MHD_FN_PAR_OUT_SIZE_ (10, 9) MHD_FN_PAR_OUT_ (11) bool
   5693 hpack_enc_field_literal (const struct mhd_HpackEncContext *restrict hk_enc,
   5694                          const struct mhd_BufferConst *restrict name,
   5695                          dtbl_idx_ft name_idx,
   5696                          const struct mhd_BufferConst *restrict value,
   5697                          enum mhd_HpackEncLitIndexingType msg_type,
   5698                          bool name_idx_stat_allowed,
   5699                          bool name_idx_dyn_allowed,
   5700                          bool huffman_allowed,
   5701                          const size_t out_buff_size,
   5702                          uint8_t *restrict out_buff,
   5703                          size_t *restrict bytes_encoded)
   5704 {
   5705   mhd_constexpr uint_fast8_t field_indexing_prfx = (uint_fast8_t)(1u << 6u);
   5706   mhd_constexpr uint_fast8_t field_indexing_prfx_bits = 2u;
   5707   mhd_constexpr uint_fast8_t field_not_idxng_prfx = (uint_fast8_t)(0u << 4u);
   5708   mhd_constexpr uint_fast8_t field_not_idxng_prfx_bits = 4u;
   5709   mhd_constexpr uint_fast8_t field_never_idxng_prfx = (uint_fast8_t)(1u << 4u);
   5710   mhd_constexpr uint_fast8_t field_never_idxng_prfx_bits = 4u;
   5711   struct mhd_HpackDTblContext const *restrict dyn = hk_enc->dyn;
   5712   dtbl_idx_ft name_idx_enc;
   5713   uint_fast8_t first_byte_prefix;
   5714   uint_fast8_t first_byte_prefix_bits;
   5715   size_t pos;
   5716   size_t pos_incr;
   5717 
   5718   mhd_assert ((0u == name->size)
   5719               || (':' != name->data[0])
   5720               || (0u != name_idx));
   5721   mhd_assert ((0u == name->size)
   5722               || (':' != name->data[0])
   5723               || (mhd_HPACK_STBL_NORM_START_IDX > name_idx));
   5724   mhd_assert ((0u == name->size)
   5725               || (':' != name->data[0])
   5726               || name_idx_stat_allowed
   5727               || !name_idx_dyn_allowed);
   5728 
   5729   if (2u > out_buff_size)
   5730     return false; /* No space even for the minimal field */
   5731 
   5732   switch (msg_type)
   5733   {
   5734   case mhd_HPACK_ENC_LIT_IDX_TYPE_INDEXING:
   5735     first_byte_prefix = field_indexing_prfx;
   5736     first_byte_prefix_bits = field_indexing_prfx_bits;
   5737     break;
   5738   case mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING:
   5739     first_byte_prefix = field_not_idxng_prfx;
   5740     first_byte_prefix_bits = field_not_idxng_prfx_bits;
   5741     break;
   5742   case mhd_HPACK_ENC_LIT_IDX_TYPE_NEVER_INDEXING:
   5743     first_byte_prefix = field_never_idxng_prfx;
   5744     first_byte_prefix_bits = field_never_idxng_prfx_bits;
   5745     break;
   5746   default:
   5747     mhd_UNREACHABLE ();
   5748     return false;
   5749   }
   5750 
   5751   name_idx_enc = 0u;
   5752   if (0u == name_idx)
   5753   {
   5754     if (name_idx_stat_allowed && name_idx_dyn_allowed)
   5755       name_idx_enc = mhd_htbl_find_name_real (dyn,
   5756                                               name->size,
   5757                                               name->data);
   5758     else if (name_idx_stat_allowed && (0u != name->size))
   5759       name_idx_enc = mhd_stbl_find_name_real (name->size,
   5760                                               name->data);
   5761     else if (mhd_COND_ALMOST_NEVER (name_idx_dyn_allowed))
   5762       name_idx_enc = mhd_dtbl_find_name (dyn,
   5763                                          name->size,
   5764                                          name->data);
   5765   }
   5766   else
   5767   {
   5768 #  if 0 /* This optimisation could be used if more requirements added to the caller side */
   5769     mhd_assert (name_idx_stat_allowed \
   5770                 || (mhd_HPACK_STBL_LAST_IDX < name_idx));
   5771     mhd_assert (name_idx_dyn_allowed \
   5772                 || (mhd_HPACK_STBL_LAST_IDX >= name_idx));
   5773 #  endif /* 0 */
   5774 #  ifndef NDEBUG
   5775     if (1)
   5776     {
   5777       struct mhd_BufferConst chk_name;
   5778       struct mhd_BufferConst chk_value;
   5779       mhd_assert (mhd_htbl_get_entry (dyn,
   5780                                       name_idx,
   5781                                       &chk_name,
   5782                                       &chk_value));
   5783       mhd_assert (name->size == chk_name.size);
   5784       mhd_assert (0 == memcmp (name->data, chk_name.data, name->size));
   5785     }
   5786 #  endif /* !NDEBUG */
   5787 
   5788     if (name_idx_stat_allowed || name_idx_dyn_allowed)
   5789       name_idx_enc = name_idx;
   5790   }
   5791 
   5792   pos = 0u;
   5793 
   5794   if (0u != name_idx_enc)
   5795   {
   5796     /* Add name as a reference */
   5797     mhd_assert (name_idx_dyn_allowed || name_idx_stat_allowed);
   5798     pos_incr = hpack_put_number_to_buf (first_byte_prefix,
   5799                                         first_byte_prefix_bits,
   5800                                         name_idx_enc,
   5801                                         out_buff_size - pos - 1u, /* Reserve one byte for the field value */
   5802                                         out_buff + pos);
   5803     if (0u == pos_incr)
   5804       return false; /* Not enough space */
   5805     pos += pos_incr;
   5806   }
   5807   else
   5808   {
   5809     /* Add name literally */
   5810 
   5811     /* Use 'zero' index to indicate literal name */
   5812     out_buff[pos++] = (uint8_t)first_byte_prefix;
   5813 
   5814     /* The buffer has at least one byte (or more) available;
   5815        the next call will fail if only one byte is available. */
   5816     if (!hpack_enc_string_literal (name,
   5817                                    huffman_allowed,
   5818                                    out_buff_size - pos - 1u,       /* Reserve one byte for the field value */
   5819                                    out_buff + pos,
   5820                                    &pos_incr))
   5821       return false; /* Not enough space */
   5822 
   5823     pos += pos_incr;
   5824   }
   5825 
   5826   /* The output buffer should have at least one byte of space available */
   5827   mhd_assert (out_buff_size > pos);
   5828 
   5829   /* Add value literally */
   5830 
   5831   if (!hpack_enc_string_literal (value,
   5832                                  huffman_allowed,
   5833                                  out_buff_size - pos,
   5834                                  out_buff + pos,
   5835                                  &pos_incr))
   5836     return false; /* Not enough space */
   5837 
   5838   pos += pos_incr;
   5839   mhd_assert (out_buff_size >= pos);
   5840 
   5841   *bytes_encoded = pos;
   5842   return true;
   5843 }
   5844 
   5845 
   5846 /**
   5847  * Internal per-field encoding result.
   5848  */
   5849 enum MHD_FIXED_ENUM_ mhd_HpackEncResultInternal
   5850 {
   5851   /**
   5852    * The output buffer is too small
   5853    */
   5854   mhd_ENC_RESULT_INT_NO_SPACE = 0,
   5855   /**
   5856    * The field is encoded successfully, do not add the field to the dynamic
   5857    * table
   5858    */
   5859   mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN,
   5860   /**
   5861    * The field is encoded successfully, add the field to the dynamic table
   5862    */
   5863   mhd_ENC_RESULT_INT_OK_ADD_TO_DYN
   5864 };
   5865 
   5866 /**
   5867  * Encode one field according to the requested indexing policy.
   5868  *
   5869  * Chooses between indexed and literal representations based on table contents
   5870  * and the @a enc_pol policy, and decides whether to add the field to the
   5871  * dynamic table (using simple size-based heuristics when not explicitly
   5872  * forced).
   5873  *
   5874  * @param[in,out] hk_enc the encoder context
   5875  * @param[in]     name the header name
   5876  * @param[in]     value the header value
   5877  * @param[in]     enc_pol the encoding policy to apply
   5878  * @param[in]     out_buff_size the size of @a out_buff in bytes,
   5879  *                              must not be zero
   5880  * @param[out]    out_buff the output buffer
   5881  * @param[out]    bytes_encoded to be set to the number of bytes written to
   5882  *                              the @a out_buff
   5883  * @return #mhd_ENC_RESULT_INT_NO_SPACE on insufficient buffer;
   5884  *         #mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN or
   5885  *         #mhd_ENC_RESULT_INT_OK_ADD_TO_DYN on success
   5886  */
   5887 static MHD_FN_PAR_NONNULL_ALL_
   5888 MHD_FN_PAR_INOUT_ (1)
   5889 MHD_FN_PAR_IN_ (2) MHD_FN_PAR_IN_ (3)
   5890 MHD_FN_PAR_OUT_SIZE_ (6, 5) MHD_FN_PAR_OUT_ (7) enum mhd_HpackEncResultInternal
   5891 hpack_enc_field (struct mhd_HpackEncContext *restrict hk_enc,
   5892                  const struct mhd_BufferConst *restrict name,
   5893                  const struct mhd_BufferConst *restrict value,
   5894                  enum mhd_HpackEncPolicy enc_pol,
   5895                  const size_t out_buff_size,
   5896                  uint8_t *restrict out_buff,
   5897                  size_t *restrict bytes_encoded)
   5898 {
   5899   mhd_assert (0u != out_buff_size);
   5900   mhd_assert ((name->size & 0xFFFFFFFFu) == name->size);
   5901   mhd_assert ((value->size & 0xFFFFFFFFu) == value->size);
   5902 
   5903   /* Check the enum values order */
   5904   mhd_STATIC_ASSERT_STMT (
   5905     mhd_HPACK_ENC_POL_FORCED_NEW_IDX < mhd_HPACK_ENC_POL_FORCED,
   5906     "The HPACK encoding policy values must be ordered");
   5907   mhd_STATIC_ASSERT_STMT (
   5908     mhd_HPACK_ENC_POL_ALWAYS_IF_FIT < mhd_HPACK_ENC_POL_NOT_INDEXED,
   5909     "The HPACK encoding policy values must be ordered");
   5910   mhd_STATIC_ASSERT_STMT (
   5911     mhd_HPACK_ENC_POL_ALWAYS_IF_FIT < mhd_HPACK_ENC_POL_DESIRABLE,
   5912     "The HPACK encoding policy values must be ordered");
   5913   mhd_STATIC_ASSERT_STMT (
   5914     mhd_HPACK_ENC_POL_DESIRABLE < mhd_HPACK_ENC_POL_LOWEST_PRIO,
   5915     "The HPACK encoding policy values must be ordered");
   5916   mhd_STATIC_ASSERT_STMT (
   5917     mhd_HPACK_ENC_POL_LOWEST_PRIO < mhd_HPACK_ENC_POL_AVOID_NEW_IDX,
   5918     "The HPACK encoding policy values must be ordered");
   5919   mhd_STATIC_ASSERT_STMT (
   5920     mhd_HPACK_ENC_POL_AVOID_NEW_IDX < mhd_HPACK_ENC_POL_NOT_INDEXED,
   5921     "The HPACK encoding policy values must be ordered");
   5922   mhd_STATIC_ASSERT_STMT (
   5923     mhd_HPACK_ENC_POL_NOT_INDEXED < mhd_HPACK_ENC_POL_NEVER_W_NAME_IDX,
   5924     "The HPACK encoding policy values must be ordered");
   5925   mhd_STATIC_ASSERT_STMT (
   5926     mhd_HPACK_ENC_POL_NEVER_W_NAME_IDX < \
   5927     mhd_HPACK_ENC_POL_NEVER_W_NAME_LIT_NO_HUFFMAN,
   5928     "The HPACK encoding policy values must be ordered");
   5929 
   5930   if ((mhd_HPACK_ENC_POL_FORCED <= enc_pol)
   5931       && (mhd_HPACK_ENC_POL_AVOID_NEW_IDX >= enc_pol))
   5932   {
   5933     const dtbl_idx_ft field_idx =
   5934       mhd_htbl_find_entry_real (hk_enc->dyn,
   5935                                 name->size,
   5936                                 name->data,
   5937                                 value->size,
   5938                                 value->data);
   5939 
   5940     if (0u != field_idx)
   5941     {
   5942       if (!hpack_enc_field_indexed (field_idx,
   5943                                     out_buff_size,
   5944                                     out_buff,
   5945                                     bytes_encoded))
   5946         return mhd_ENC_RESULT_INT_NO_SPACE;
   5947 
   5948       return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   5949     }
   5950   }
   5951 
   5952   /* The field is not in the tables or should not be added as an indexed
   5953      field */
   5954 
   5955   /* Add the field literally */
   5956 
   5957   if (mhd_HPACK_ENC_POL_NEVER_W_NAME_IDX <= enc_pol)
   5958   {
   5959     /* Add field literally as "never indexed" */
   5960     const bool name_idx_stat_allowed =
   5961       (mhd_HPACK_ENC_POL_NEVER_W_NAME_IDX_STATIC >= enc_pol);
   5962     const bool name_idx_dyn_allowed =
   5963       (mhd_HPACK_ENC_POL_NEVER_W_NAME_IDX_STATIC > enc_pol);
   5964     const bool huffman_allowed =
   5965       (mhd_HPACK_ENC_POL_NEVER_W_NAME_LIT_NO_HUFFMAN > enc_pol);
   5966     if (!hpack_enc_field_literal (hk_enc,
   5967                                   name,
   5968                                   0u,
   5969                                   value,
   5970                                   mhd_HPACK_ENC_LIT_IDX_TYPE_NEVER_INDEXING,
   5971                                   name_idx_stat_allowed,
   5972                                   name_idx_dyn_allowed,
   5973                                   huffman_allowed,
   5974                                   out_buff_size,
   5975                                   out_buff,
   5976                                   bytes_encoded))
   5977       return mhd_ENC_RESULT_INT_NO_SPACE;
   5978 
   5979     return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   5980   }
   5981 
   5982   if (mhd_HPACK_ENC_POL_AVOID_NEW_IDX <= enc_pol)
   5983   {
   5984     /* Adding to the tables is not allowed */
   5985     mhd_assert (mhd_HPACK_ENC_POL_NOT_INDEXED >= enc_pol);
   5986 
   5987     if (!hpack_enc_field_literal (hk_enc,
   5988                                   name,
   5989                                   0u,
   5990                                   value,
   5991                                   mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   5992                                   true,
   5993                                   true,
   5994                                   true,
   5995                                   out_buff_size,
   5996                                   out_buff,
   5997                                   bytes_encoded))
   5998       return mhd_ENC_RESULT_INT_NO_SPACE;
   5999 
   6000     return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6001   }
   6002 
   6003   if (mhd_HPACK_ENC_POL_ALWAYS_IF_FIT >= enc_pol)
   6004   {
   6005     bool add_to_idx;
   6006     if ((mhd_HPACK_ENC_POL_FORCED == enc_pol)
   6007         || (mhd_HPACK_ENC_POL_FORCED_NEW_IDX == enc_pol))
   6008       add_to_idx = true;
   6009     else
   6010       add_to_idx = mhd_dtbl_check_entry_fit (hk_enc->dyn,
   6011                                              name->size,
   6012                                              value->size);
   6013 
   6014     if (!hpack_enc_field_literal (hk_enc,
   6015                                   name,
   6016                                   0u,
   6017                                   value,
   6018                                   add_to_idx ?
   6019                                   mhd_HPACK_ENC_LIT_IDX_TYPE_INDEXING :
   6020                                   mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   6021                                   true,
   6022                                   true,
   6023                                   true,
   6024                                   out_buff_size,
   6025                                   out_buff,
   6026                                   bytes_encoded))
   6027       return mhd_ENC_RESULT_INT_NO_SPACE;
   6028 
   6029     return add_to_idx ?
   6030            mhd_ENC_RESULT_INT_OK_ADD_TO_DYN :
   6031            mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6032   }
   6033 
   6034   /* Indexing or not indexing is not forced.
   6035      Need to decide whether to add the field to the index based on some
   6036      heuristics.
   6037      Use only field size and buffer data when deciding. Do not analyse the
   6038      field name or value (it should be performed by caller). */
   6039 
   6040   mhd_assert (mhd_HPACK_ENC_POL_DESIRABLE <= enc_pol);
   6041   mhd_assert (mhd_HPACK_ENC_POL_LOWEST_PRIO >= enc_pol);
   6042 
   6043   if (1) /* For local scope */
   6044   {
   6045     enum mhd_Tristate add_to_idx;
   6046 
   6047     add_to_idx =
   6048       mhd_dtbl_check_entry_fit (hk_enc->dyn,
   6049                                 name->size,
   6050                                 value->size) ? mhd_T_MAYBE : mhd_T_NO;
   6051 
   6052     /* The following algorithm is simplified and can be improved */
   6053 
   6054     if (mhd_T_IS_MAYBE (add_to_idx))
   6055     {
   6056       const size_t field_size =
   6057         name->size + value->size + mhd_dtbl_entry_overhead;
   6058       const size_t dyn_size = hk_enc->dyn_size_new;
   6059       const size_t dyn_used = mhd_dtbl_get_table_used (hk_enc->dyn);
   6060       const size_t dyn_free = dyn_size - dyn_used;
   6061       const size_t num_entries = mhd_dtbl_get_num_entries (hk_enc->dyn);
   6062 
   6063       mhd_assert (dyn_size >= dyn_used);
   6064 
   6065       if (512u > dyn_size)
   6066       {
   6067         /* Very small table, use very basic logic */
   6068         add_to_idx =
   6069           (mhd_HPACK_ENC_POL_NEUTRAL >= enc_pol) ? mhd_T_YES : mhd_T_NO;
   6070       }
   6071       else if (mhd_HPACK_ENC_POL_DESIRABLE >= enc_pol)
   6072       {
   6073         mhd_assert (mhd_HPACK_ENC_POL_DESIRABLE == enc_pol);
   6074         if (field_size <= dyn_free)
   6075           add_to_idx = mhd_T_YES;
   6076         else if (field_size <= (dyn_size - dyn_size / 4))
   6077           add_to_idx = mhd_T_YES;
   6078         else if (2u >= num_entries)
   6079           add_to_idx = mhd_T_YES;
   6080         else
   6081           add_to_idx = mhd_T_NO;
   6082       }
   6083       else if (mhd_HPACK_ENC_POL_NEUTRAL == enc_pol)
   6084       {
   6085         if (field_size <= dyn_free / 4)
   6086           add_to_idx = mhd_T_YES;
   6087         else if (field_size <= dyn_size / 32)
   6088           add_to_idx = mhd_T_YES;
   6089         else if ((field_size <= dyn_size / 4)
   6090                  && ((field_size / 2) >= (dyn_used / num_entries)))
   6091           add_to_idx = mhd_T_YES;
   6092         else
   6093           add_to_idx = mhd_T_NO;
   6094       }
   6095       else if (mhd_HPACK_ENC_POL_LOW_PRIO == enc_pol)
   6096       {
   6097         if (field_size <= dyn_free / 16)
   6098           add_to_idx = mhd_T_YES;
   6099         else if (field_size <= dyn_size / 128)
   6100           add_to_idx = mhd_T_YES;
   6101         else
   6102           add_to_idx = mhd_T_NO;
   6103       }
   6104       else if (mhd_HPACK_ENC_POL_LOWEST_PRIO == enc_pol)
   6105       {
   6106         if (field_size <= dyn_free / 64)
   6107           add_to_idx = mhd_T_YES;
   6108         else if (field_size <= dyn_size / 512)
   6109           add_to_idx = mhd_T_YES;
   6110         else
   6111           add_to_idx = mhd_T_NO;
   6112       }
   6113       else
   6114       {
   6115         mhd_UNREACHABLE ();
   6116         add_to_idx = mhd_T_NO;
   6117       }
   6118     }
   6119     mhd_assert (mhd_T_IS_NOT_MAYBE (add_to_idx));
   6120 
   6121     if (mhd_T_IS_YES (add_to_idx))
   6122     {
   6123       if (!hpack_enc_field_literal (hk_enc,
   6124                                     name,
   6125                                     0u,
   6126                                     value,
   6127                                     mhd_HPACK_ENC_LIT_IDX_TYPE_INDEXING,
   6128                                     true,
   6129                                     true,
   6130                                     true,
   6131                                     out_buff_size,
   6132                                     out_buff,
   6133                                     bytes_encoded))
   6134         return mhd_ENC_RESULT_INT_NO_SPACE;
   6135 
   6136       return mhd_ENC_RESULT_INT_OK_ADD_TO_DYN;
   6137     }
   6138   }
   6139 
   6140   if (!hpack_enc_field_literal (hk_enc,
   6141                                 name,
   6142                                 0u,
   6143                                 value,
   6144                                 mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   6145                                 true,
   6146                                 true,
   6147                                 true,
   6148                                 out_buff_size,
   6149                                 out_buff,
   6150                                 bytes_encoded))
   6151     return mhd_ENC_RESULT_INT_NO_SPACE;
   6152 
   6153   return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6154 }
   6155 
   6156 
   6157 /**
   6158  * Emit Dynamic Table Size Update representation(s) if needed.
   6159  *
   6160  * If the current dynamic table size differs from the pending minimal/final
   6161  * sizes accumulated in @a hk_enc, this function encodes one or two size
   6162  * updates, and performs local eviction down to the minimal size for
   6163  * consistency.
   6164  *
   6165  * @param[in,out] hk_enc the encoder context
   6166  * @param[in]     out_buff_size the size of @a out_buff in bytes,
   6167  *                              could be zero
   6168  * @param[out]    out_buff the output buffer to write encoded messages
   6169  * @param[out] bytes_encoded the output variable to be set to the number of
   6170  *                           bytes written
   6171  * @return 'true' on success;
   6172  *         'false' if the output buffer is too small
   6173  */
   6174 static MHD_FN_PAR_OUT_SIZE_ (3, 2) MHD_FN_PAR_OUT_ (4) bool
   6175 hpack_enc_check_dyn_size_update (
   6176   struct mhd_HpackEncContext *restrict hk_enc,
   6177   size_t out_buff_size,
   6178   uint8_t *restrict out_buff,
   6179   size_t *restrict bytes_encoded)
   6180 {
   6181   /** The prefix for Dynamic Table Size Update message */
   6182   mhd_constexpr uint_fast8_t dyn_size_upd_msg_prfx = (uint_fast8_t)(1u << 5u);
   6183   mhd_constexpr uint_fast8_t dyn_size_upd_msg_prfx_bits = 3u;
   6184   size_t pos;
   6185   size_t pos_incr;
   6186   struct mhd_HpackDTblContext *restrict const dyn = hk_enc->dyn;
   6187 
   6188   mhd_assert (mhd_DTBL_MAX_SIZE >= hk_enc->dyn_size_smallest);
   6189   mhd_assert (mhd_DTBL_MAX_SIZE >= hk_enc->dyn_size_new);
   6190   mhd_assert (hk_enc->dyn_size_peer >= hk_enc->dyn_size_smallest);
   6191   mhd_assert (hk_enc->dyn_size_new >= hk_enc->dyn_size_smallest);
   6192   mhd_assert (mhd_dtbl_get_table_max_size (dyn) \
   6193               >= hk_enc->dyn_size_smallest);
   6194 
   6195   if (mhd_dtbl_get_table_max_size (dyn) != hk_enc->dyn_size_smallest)
   6196     mhd_dtbl_evict_to_size (dyn,
   6197                             hk_enc->dyn_size_smallest);
   6198 
   6199   if ((hk_enc->dyn_size_smallest == hk_enc->dyn_size_peer)
   6200       && (hk_enc->dyn_size_new == hk_enc->dyn_size_peer))
   6201   {
   6202     *bytes_encoded = 0u;
   6203     return true; /* No resize signal needed */
   6204   }
   6205 
   6206   /* Need to create a "Dynamic Table Size Update" signal */
   6207   if (0u == out_buff_size)
   6208     return false; /* Not enough space */
   6209 
   6210   pos = 0u;
   6211 
   6212   if (hk_enc->dyn_size_peer != hk_enc->dyn_size_smallest)
   6213   {
   6214     /* Signal the minimal size so the peer evicts entries */
   6215     pos_incr =
   6216       hpack_put_number_to_buf (dyn_size_upd_msg_prfx,
   6217                                dyn_size_upd_msg_prfx_bits,
   6218                                (uint_fast32_t)hk_enc->dyn_size_smallest,
   6219                                out_buff_size,
   6220                                out_buff);
   6221 
   6222     if (0u == pos_incr)
   6223       return false; /* Not enough space */
   6224 
   6225     pos += pos_incr;
   6226   }
   6227 
   6228   if (hk_enc->dyn_size_new != hk_enc->dyn_size_smallest)
   6229   {
   6230     if (pos == out_buff_size)
   6231       return false; /* Not enough space for the second resize message */
   6232 
   6233     /* Signal the final dynamic table size */
   6234     pos_incr =
   6235       hpack_put_number_to_buf (dyn_size_upd_msg_prfx,
   6236                                dyn_size_upd_msg_prfx_bits,
   6237                                (uint_fast32_t)hk_enc->dyn_size_new,
   6238                                out_buff_size - pos,
   6239                                out_buff + pos);
   6240 
   6241     if (0u == pos_incr)
   6242       return false; /* Not enough space */
   6243 
   6244     pos += pos_incr;
   6245   }
   6246 
   6247   mhd_assert (0u != pos);
   6248   *bytes_encoded = pos;
   6249   return true;
   6250 }
   6251 
   6252 
   6253 /**
   6254  * Apply a pending Dynamic Table Size Update for the encoder.
   6255  *
   6256  * Resizes the dynamic table to @a hk_enc->new_dyn_size if needed and updates
   6257  * hk_enc data accordingly.
   6258  *
   6259  * @param[in,out] hk_enc the encoder context
   6260  * @return 'true' on success;
   6261  *         'false' on allocation error
   6262  */
   6263 static bool
   6264 hpack_enc_perform_dyn_size_update (struct mhd_HpackEncContext *restrict hk_enc)
   6265 {
   6266   mhd_assert (mhd_dtbl_get_table_used (hk_enc->dyn)
   6267               <= hk_enc->dyn_size_smallest);
   6268   if (mhd_dtbl_get_table_max_size (hk_enc->dyn) != hk_enc->dyn_size_new)
   6269   {
   6270     if (mhd_COND_HARDLY_EVER (!mhd_dtbl_resize (&(hk_enc->dyn), \
   6271                                                 hk_enc->dyn_size_new)))
   6272       return false;
   6273 
   6274     mhd_assert (mhd_dtbl_get_table_max_size (hk_enc->dyn) == \
   6275                 hk_enc->dyn_size_new);
   6276   }
   6277 
   6278   hk_enc->dyn_size_smallest = hk_enc->dyn_size_new;
   6279   hk_enc->dyn_size_peer = hk_enc->dyn_size_new;
   6280 
   6281   return true;
   6282 }
   6283 
   6284 
   6285 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   6286 MHD_FN_PAR_INOUT_ (1)
   6287 MHD_FN_PAR_IN_ (2) MHD_FN_PAR_IN_ (3)
   6288 MHD_FN_PAR_OUT_SIZE_ (6, 5) MHD_FN_PAR_OUT_ (7) enum mhd_HpackEncResult
   6289 mhd_hpack_enc_field (struct mhd_HpackEncContext *restrict hk_enc,
   6290                      const struct mhd_BufferConst *restrict name,
   6291                      const struct mhd_BufferConst *restrict value,
   6292                      enum mhd_HpackEncPolicy enc_pol,
   6293                      const size_t out_buff_size,
   6294                      uint8_t *restrict out_buff,
   6295                      size_t *restrict bytes_encoded)
   6296 {
   6297   size_t pos;
   6298   size_t pos_incr;
   6299   enum mhd_HpackEncResultInternal enc_field_res;
   6300 
   6301   mhd_assert ((name->size & 0xFFFFFFFFu) == name->size);
   6302   mhd_assert ((value->size & 0xFFFFFFFFu) == value->size);
   6303   mhd_assert ((0u == name->size) || (':' != name->data[0]));
   6304 
   6305   if (0u == out_buff_size)
   6306     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6307 
   6308   pos = 0u;
   6309 
   6310   /* Add Dynamic Table Size Update message if needed */
   6311   if (!hpack_enc_check_dyn_size_update (hk_enc,
   6312                                         out_buff_size - 1u,  /* Reserve one byte for minimal field size */
   6313                                         out_buff,
   6314                                         &pos_incr))
   6315     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6316 
   6317   pos += pos_incr;
   6318   mhd_assert (pos < out_buff_size);
   6319 
   6320   enc_field_res =
   6321     hpack_enc_field (hk_enc,
   6322                      name,
   6323                      value,
   6324                      enc_pol,
   6325                      out_buff_size - pos,
   6326                      out_buff + pos,
   6327                      &pos_incr);
   6328 
   6329   if (mhd_ENC_RESULT_INT_NO_SPACE == enc_field_res)
   6330     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6331 
   6332   pos += pos_incr;
   6333 
   6334   /* Finally resize the dynamic table (if resize is pending) */
   6335   if (!hpack_enc_perform_dyn_size_update (hk_enc))
   6336     return mhd_HPACK_ENC_RES_ALLOC_ERR;
   6337 
   6338   /* Add the field (if needed) only after dynamic table resizing (if any) */
   6339   if (mhd_ENC_RESULT_INT_OK_ADD_TO_DYN == enc_field_res)
   6340     mhd_dtbl_new_entry (hk_enc->dyn,
   6341                         name->size,
   6342                         name->data,
   6343                         value->size,
   6344                         value->data);
   6345   else
   6346     mhd_assert (mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN == enc_field_res);
   6347 
   6348   mhd_assert (out_buff_size >= pos);
   6349   *bytes_encoded = pos;
   6350   return mhd_HPACK_ENC_RES_OK;
   6351 }
   6352 
   6353 
   6354 /**
   6355  * Convert an HTTP status @a code to a three-character decimal string.
   6356  *
   6357  * @param code the status code; must be >= 100 and <= 699
   6358  * @param[out] code_str destination buffer of exactly 3 bytes;
   6359  *                      receives the decimal digits of @a code
   6360  */
   6361 mhd_static_inline
   6362 MHD_FN_PAR_OUT_ (2) void
   6363 status_to_str (uint_fast16_t code,
   6364                char code_str[3])
   6365 {
   6366   mhd_assert (100u <= code);
   6367   mhd_assert (699u >= code);
   6368 
   6369   code_str[0] = (char)('0' + (char)(uint8_t)((code / 100u) % 10));
   6370   code_str[1] = (char)('0' + (char)(uint8_t)((code /  10u) % 10));
   6371   code_str[2] = (char)('0' + (char)(uint8_t)((code /   1u) % 10));
   6372 }
   6373 
   6374 
   6375 /**
   6376  * Pseudo-header ":status" name in the string form
   6377  */
   6378 static const struct mhd_BufferConst pf_status_str = mhd_MSTR_INIT (":status");
   6379 
   6380 /**
   6381  * Encode one pseudo-header ":status" according to the requested indexing
   6382  * policy.
   6383  *
   6384  * Chooses between indexed and literal representations based on table contents
   6385  * and the @a enc_pol policy, and decides whether to add the field to the
   6386  * dynamic table (using simple size-based heuristics when not explicitly
   6387  * forced).
   6388  *
   6389  * @param[in,out] hk_enc the encoder context
   6390  * @param[in]     code the status code, must be >= 100 and <= 699
   6391  * @param[in]     enc_pol the encoding policy to apply
   6392  * @param[out]    code_str where the string representation of the @a code
   6393  *                         to be written if literal encoding is used
   6394  * @param[in]     out_buff_size the size of @a out_buff in bytes,
   6395  *                              must not be zero
   6396  * @param[out]    out_buff the output buffer
   6397  * @param[out]    bytes_encoded to be set to the number of bytes written to
   6398  *                              the @a out_buff
   6399  * @return #mhd_ENC_RESULT_INT_NO_SPACE on insufficient buffer;
   6400  *         #mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN or
   6401  *         #mhd_ENC_RESULT_INT_OK_ADD_TO_DYN on success
   6402  */
   6403 static MHD_FN_PAR_NONNULL_ALL_
   6404 MHD_FN_PAR_INOUT_ (1)
   6405 MHD_FN_PAR_OUT_ (4)
   6406 MHD_FN_PAR_OUT_SIZE_ (6, 5) MHD_FN_PAR_OUT_ (7) enum mhd_HpackEncResultInternal
   6407 hpack_enc_pf_status (struct mhd_HpackEncContext *restrict hk_enc,
   6408                      uint_fast16_t code,
   6409                      enum mhd_HpackEncPFieldStatusPolicy enc_pol,
   6410                      char code_str[3],
   6411                      const size_t out_buff_size,
   6412                      uint8_t *restrict out_buff,
   6413                      size_t *restrict bytes_encoded)
   6414 {
   6415   mhd_constexpr dtbl_idx_ft pf_status_first_idx =
   6416     mhd_HPACK_STBL_PF_STATUS_START_POS;
   6417   mhd_constexpr dtbl_idx_ft pf_status_200_idx = pf_status_first_idx + 0u;
   6418   mhd_constexpr dtbl_idx_ft pf_status_204_idx = pf_status_first_idx + 1u;
   6419   mhd_constexpr dtbl_idx_ft pf_status_206_idx = pf_status_first_idx + 2u;
   6420   mhd_constexpr dtbl_idx_ft pf_status_304_idx = pf_status_first_idx + 3u;
   6421   mhd_constexpr dtbl_idx_ft pf_status_400_idx = pf_status_first_idx + 4u;
   6422   mhd_constexpr dtbl_idx_ft pf_status_404_idx = pf_status_first_idx + 5u;
   6423   mhd_constexpr dtbl_idx_ft pf_status_500_idx = pf_status_first_idx + 6u;
   6424   struct mhd_BufferConst code_val;
   6425 
   6426   mhd_assert (14u == pf_status_500_idx);
   6427 
   6428   mhd_assert (0u != out_buff_size);
   6429 
   6430   /* Check the enum values order */
   6431   mhd_STATIC_ASSERT_STMT (
   6432     mhd_HPACK_ENC_PFS_POL_ALWAYS_NEW_IDX_IF_FIT < \
   6433     mhd_HPACK_ENC_PFS_POL_NORMAL,
   6434     "The HPACK status-field policy values must be ordered");
   6435   mhd_STATIC_ASSERT_STMT (
   6436     mhd_HPACK_ENC_PFS_POL_NORMAL < mhd_HPACK_ENC_PFS_POL_AVOID_NEW_IDX,
   6437     "The HPACK status-field policy values must be ordered");
   6438   mhd_STATIC_ASSERT_STMT (
   6439     mhd_HPACK_ENC_PFS_POL_AVOID_NEW_IDX < mhd_HPACK_ENC_PFS_POL_STATIC_IDX,
   6440     "The HPACK status-field policy values must be ordered");
   6441   mhd_STATIC_ASSERT_STMT (
   6442     mhd_HPACK_ENC_PFS_POL_STATIC_IDX < mhd_HPACK_ENC_PFS_POL_NOT_INDEXED,
   6443     "The HPACK status-field policy values must be ordered");
   6444   mhd_STATIC_ASSERT_STMT (
   6445     mhd_HPACK_ENC_PFS_POL_NOT_INDEXED < \
   6446     mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_IDX,
   6447     "The HPACK status-field policy values must be ordered");
   6448   mhd_STATIC_ASSERT_STMT (
   6449     mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_IDX < \
   6450     mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_LIT_FORCED,
   6451     "The HPACK status-field policy values must be ordered");
   6452   mhd_STATIC_ASSERT_STMT (
   6453     mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_LIT_FORCED < \
   6454     mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_LIT_NO_HUFFMAN,
   6455     "The HPACK status-field policy values must be ordered");
   6456 
   6457 
   6458   if ((mhd_HPACK_ENC_PFS_POL_NORMAL <= enc_pol)
   6459       && (mhd_HPACK_ENC_PFS_POL_STATIC_IDX >= enc_pol))
   6460   {
   6461     dtbl_idx_ft field_idx;
   6462     switch (code)
   6463     {
   6464     case 200u:
   6465       field_idx = pf_status_200_idx;
   6466       break;
   6467     case 204u:
   6468       field_idx = pf_status_204_idx;
   6469       break;
   6470     case 206u:
   6471       field_idx = pf_status_206_idx;
   6472       break;
   6473     case 304u:
   6474       field_idx = pf_status_304_idx;
   6475       break;
   6476     case 400u:
   6477       field_idx = pf_status_400_idx;
   6478       break;
   6479     case 404u:
   6480       field_idx = pf_status_404_idx;
   6481       break;
   6482     case 500u:
   6483       field_idx = pf_status_500_idx;
   6484       break;
   6485     default:
   6486       field_idx = 0u;
   6487       break;
   6488     }
   6489 
   6490     if (0u != field_idx)
   6491     {
   6492       if (!hpack_enc_field_indexed (field_idx,
   6493                                     out_buff_size,
   6494                                     out_buff,
   6495                                     bytes_encoded))
   6496         return mhd_ENC_RESULT_INT_NO_SPACE;
   6497 
   6498       return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6499     }
   6500   }
   6501 
   6502   /* The pseudo-header is not in the static table or should not be added as an
   6503      indexed field */
   6504 
   6505   /* Create a string representation of the code */
   6506   status_to_str (code, code_str);
   6507   code_val.data = code_str;
   6508   code_val.size = 3u;
   6509 
   6510   if ((mhd_HPACK_ENC_PFS_POL_NORMAL <= enc_pol)
   6511       && (mhd_HPACK_ENC_PFS_POL_AVOID_NEW_IDX >= enc_pol))
   6512   {
   6513     const dtbl_idx_ft field_idx =
   6514       mhd_dtbl_find_entry (hk_enc->dyn,
   6515                            pf_status_str.size,
   6516                            pf_status_str.data,
   6517                            3u,
   6518                            code_str);
   6519 
   6520     if (0u != field_idx)
   6521     {
   6522       if (!hpack_enc_field_indexed (field_idx,
   6523                                     out_buff_size,
   6524                                     out_buff,
   6525                                     bytes_encoded))
   6526         return mhd_ENC_RESULT_INT_NO_SPACE;
   6527 
   6528       return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6529     }
   6530   }
   6531 
   6532   /* The field is not in the tables or should not be added as an indexed
   6533      field */
   6534 
   6535   /* Add the field literally */
   6536 
   6537   if (mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_IDX <= enc_pol)
   6538   {
   6539     /* Add field literally as "never indexed" */
   6540     const bool name_idx_stat_allowed =
   6541       (mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_IDX == enc_pol);
   6542     const bool huffman_allowed =
   6543       (mhd_HPACK_ENC_PFS_POL_NEVER_W_NAME_LIT_NO_HUFFMAN > enc_pol);
   6544     if (!hpack_enc_field_literal (hk_enc,
   6545                                   &pf_status_str,
   6546                                   pf_status_first_idx,
   6547                                   &code_val,
   6548                                   mhd_HPACK_ENC_LIT_IDX_TYPE_NEVER_INDEXING,
   6549                                   name_idx_stat_allowed,
   6550                                   false,
   6551                                   huffman_allowed,
   6552                                   out_buff_size,
   6553                                   out_buff,
   6554                                   bytes_encoded))
   6555       return mhd_ENC_RESULT_INT_NO_SPACE;
   6556 
   6557     return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6558   }
   6559 
   6560   if (mhd_HPACK_ENC_PFS_POL_AVOID_NEW_IDX <= enc_pol)
   6561   {
   6562     /* Adding to the tables is not allowed */
   6563     mhd_assert (mhd_HPACK_ENC_PFS_POL_NOT_INDEXED >= enc_pol);
   6564 
   6565     if (!hpack_enc_field_literal (hk_enc,
   6566                                   &pf_status_str,
   6567                                   pf_status_first_idx,
   6568                                   &code_val,
   6569                                   mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   6570                                   true,
   6571                                   false,
   6572                                   true,
   6573                                   out_buff_size,
   6574                                   out_buff,
   6575                                   bytes_encoded))
   6576       return mhd_ENC_RESULT_INT_NO_SPACE;
   6577 
   6578     return mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6579   }
   6580 
   6581   mhd_assert (mhd_HPACK_ENC_PFS_POL_ALWAYS_NEW_IDX_IF_FIT <= enc_pol);
   6582   mhd_assert (mhd_HPACK_ENC_PFS_POL_NORMAL >= enc_pol);
   6583 
   6584   if (1) /* For local scope */
   6585   {
   6586     const bool add_to_idx =
   6587       mhd_dtbl_check_entry_fit (hk_enc->dyn,
   6588                                 pf_status_str.size,
   6589                                 3u);
   6590 
   6591     if (hpack_enc_field_literal (hk_enc,
   6592                                  &pf_status_str,
   6593                                  pf_status_first_idx,
   6594                                  &code_val,
   6595                                  add_to_idx ?
   6596                                  mhd_HPACK_ENC_LIT_IDX_TYPE_INDEXING :
   6597                                  mhd_HPACK_ENC_LIT_IDX_TYPE_NOT_INDEXING,
   6598                                  true,
   6599                                  false,
   6600                                  true,
   6601                                  out_buff_size,
   6602                                  out_buff,
   6603                                  bytes_encoded))
   6604       return add_to_idx ?
   6605              mhd_ENC_RESULT_INT_OK_ADD_TO_DYN :
   6606              mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN;
   6607 
   6608   }
   6609 
   6610   return mhd_ENC_RESULT_INT_NO_SPACE;
   6611 }
   6612 
   6613 
   6614 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
   6615 MHD_FN_PAR_INOUT_ (1)
   6616 MHD_FN_PAR_OUT_SIZE_ (5, 4) MHD_FN_PAR_OUT_ (6) enum mhd_HpackEncResult
   6617 mhd_hpack_enc_ph_status (struct mhd_HpackEncContext *restrict hk_enc,
   6618                          uint_fast16_t code,
   6619                          enum mhd_HpackEncPFieldStatusPolicy enc_pol,
   6620                          const size_t out_buff_size,
   6621                          uint8_t *restrict out_buff,
   6622                          size_t *restrict bytes_encoded)
   6623 {
   6624   char code_str[3] = "";
   6625   size_t pos;
   6626   size_t pos_incr;
   6627   enum mhd_HpackEncResultInternal enc_field_res;
   6628 
   6629   mhd_assert (100u <= code);
   6630   mhd_assert (699u >= code);
   6631 
   6632   if (0u == out_buff_size)
   6633     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6634 
   6635   pos = 0u;
   6636 
   6637   /* Add Dynamic Table Size Update message if needed */
   6638   if (!hpack_enc_check_dyn_size_update (hk_enc,
   6639                                         out_buff_size - 1u,  /* Reserve one byte for minimal field size */
   6640                                         out_buff,
   6641                                         &pos_incr))
   6642     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6643 
   6644   pos += pos_incr;
   6645   mhd_assert (pos < out_buff_size);
   6646 
   6647   enc_field_res =
   6648     hpack_enc_pf_status (hk_enc,
   6649                          code,
   6650                          enc_pol,
   6651                          code_str,
   6652                          out_buff_size - pos,
   6653                          out_buff + pos,
   6654                          &pos_incr);
   6655 
   6656   if (mhd_ENC_RESULT_INT_NO_SPACE == enc_field_res)
   6657     return mhd_HPACK_ENC_BUFFER_TOO_SMALL;
   6658 
   6659   pos += pos_incr;
   6660 
   6661   /* Finally resize the dynamic table (if resize is pending) */
   6662   if (!hpack_enc_perform_dyn_size_update (hk_enc))
   6663     return mhd_HPACK_ENC_RES_ALLOC_ERR;
   6664 
   6665   /* Add the field (if needed) only after dynamic table resizing (if any) */
   6666   if (mhd_ENC_RESULT_INT_OK_ADD_TO_DYN == enc_field_res)
   6667   {
   6668     mhd_assert ('1' <= code_str[0]);
   6669     mhd_assert ('6' >= code_str[0]);
   6670     mhd_dtbl_new_entry (hk_enc->dyn,
   6671                         pf_status_str.size,
   6672                         pf_status_str.data,
   6673                         sizeof(code_str) / sizeof(char),
   6674                         code_str);
   6675   }
   6676   else
   6677     mhd_assert (mhd_ENC_RESULT_INT_OK_NO_ADD_TO_DYN == enc_field_res);
   6678 
   6679   mhd_assert (out_buff_size >= pos);
   6680   *bytes_encoded = pos;
   6681   return mhd_HPACK_ENC_RES_OK;
   6682 }
   6683 
   6684 
   6685 /* ****** _____________ End of HPACK headers encoding ______________ ****** */
   6686 
   6687 #endif /* ! mhd_HPACK_TESTING_TABLES_ONLY || ! MHD_UNIT_TESTING */