libmicrohttpd2

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

mhd_hpack_codec.c (242882B)


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