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 */