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