You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TASK-072: arena-allocated unescape on the warm path
Route the GET-argument unescape output through the per-connection
arena so the warm-path zero-global-allocation guarantee holds
unconditionally -- previously the v1 code path materialised the
unescape result in a std::string temporary, paying one global-heap
allocation per argument when the value exceeded std::string's SSO
threshold (TASK-018 acknowledged deferral).
build_request_args now allocates the destination pmr::string
directly in the arena domain and runs the standard %HH / '+' decode
in place via a TU-local http_unescape_raw helper. When a user
unescaper is registered the ABI-locked `void(std::string&)` callback
is fed a thread_local std::string scratch buffer whose capacity
amortises across requests on the same worker thread, so the
steady-state per-request global-heap cost is zero on the user path
too.
Tests pinned in test/unit/http_request_unescape_arena_test.cpp:
the headline pair use a global operator-new counter (RAII window
guard) to assert that build_request_args makes zero global-heap
allocations on the warm cycle for both the default and the
user-registered unescaper paths. Correctness + lifetime tests
cover %2F decode, invalid-hex passthrough, empty input,
view-outlives-subsequent-inserts, and the user-callback grow
path.
bench_warm_path gains two variants: (5) %2F-containing value
through the arena-routed unescape and (6) no-escape baseline. The
two medians land within noise (99 ns vs 102 ns on the debug build),
satisfying the "matches the no-unescape baseline within noise"
acceptance criterion.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: specs/architecture/04-components/http-request.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -19,6 +19,7 @@
19
19
20
20
**Key design notes:**
21
21
- The arena allocator is plumbed through `webserver_impl` → connection state → `http_request` constructor. The user does not see it; it is an internal optimization.
22
+
- GET-argument population (`http_request_impl::build_request_args`) writes the unescaped value directly into the per-connection arena (TASK-072). The standard `%HH` / `+`→space transformation runs in-place on the arena-backed `pmr::string`; when a user-registered unescaper is set, a per-thread reusable `std::string` scratch buffer adapts the ABI-locked `void(std::string&)` callback signature without a per-request global-heap allocation. Pinned by `test/unit/http_request_unescape_arena_test.cpp` (zero global `operator new` calls during the warm cycle) and exercised by `bench_warm_path` variants (5) and (6).
22
23
- Containers returned by `get_*()` reference impl-owned storage; the request must outlive any view derived from it. Documented as a lifetime contract.
23
24
-`gnutls_session_t` (raw GnuTLS handle) is not exposed publicly. Users wanting custom TLS introspection use the high-level `get_client_cert_*` accessors. The handle remains accessible via friend access from internal code.
Copy file name to clipboardExpand all lines: specs/tasks/M7-v2-cleanup/TASK-072.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,10 +8,10 @@
8
8
`src/detail/http_request_impl.cpp:202-206` is an acknowledged TASK-018 deferral: the warm-path "0-alloc" criterion explicitly does not apply when a user unescaper is registered, because the unescape call still allocates a `std::string`. Route the unescape output into the per-connection arena (TASK-016) so the criterion holds unconditionally.
9
9
10
10
**Action Items:**
11
-
-[] Extend the per-connection arena (from TASK-016) with an `unescape_into(string_view in, void* sink) -> string_view` helper, or have the arena expose a scratch-buffer API the unescape can write into.
12
-
-[] Refactor the unescape call site at `http_request_impl.cpp:202-206` to allocate into the arena. The returned `string_view` is valid for the lifetime of the request, matching the TASK-018 lifetime documentation.
13
-
-[] Update the inline TASK-018 deferral comment to reflect that arena unescape is now wired.
14
-
-[] Extend `test/bench_warm_path.cpp` (TASK-058) with an unescape variant: warm GET with `%2F` in the path. Pin "no allocations under heap profiler" as in TASK-058.
11
+
-[x] Extend the per-connection arena (from TASK-016) with an `unescape_into(string_view in, void* sink) -> string_view` helper, or have the arena expose a scratch-buffer API the unescape can write into.
12
+
-[x] Refactor the unescape call site at `http_request_impl.cpp:202-206` to allocate into the arena. The returned `string_view` is valid for the lifetime of the request, matching the TASK-018 lifetime documentation.
13
+
-[x] Update the inline TASK-018 deferral comment to reflect that arena unescape is now wired.
14
+
-[x] Extend `test/bench_warm_path.cpp` (TASK-058) with an unescape variant: warm GET with `%2F` in the path. Pin "no allocations under heap profiler" as in TASK-058.
0 commit comments