@@ -168,31 +168,61 @@ MHD_Result http_request_impl::build_request_header(void* cls, MHD_ValueKind kind
168168 return MHD_YES;
169169}
170170
171- http::header_view_map http_request_impl::get_headerlike_values (MHD_ValueKind kind) const {
172- http::header_view_map headers;
171+ const http::header_view_map& http_request_impl::ensure_headerlike_cache (MHD_ValueKind kind) const {
172+ // Pick the cache slot and build-flag matching `kind`. We resolve them
173+ // up front so the cold (build) and warm (return) paths share a single
174+ // reference without re-switching.
175+ http::header_view_map* cache = nullptr ;
176+ bool * built = nullptr ;
177+ const http::header_map* local_fallback = nullptr ;
178+ switch (kind) {
179+ case MHD_HEADER_KIND:
180+ cache = &headers_cached_;
181+ built = &headers_cache_built_;
182+ local_fallback = &headers_local;
183+ break ;
184+ case MHD_FOOTER_KIND:
185+ cache = &footers_cached_;
186+ built = &footers_cache_built_;
187+ local_fallback = &footers_local;
188+ break ;
189+ case MHD_COOKIE_KIND:
190+ cache = &cookies_cached_;
191+ built = &cookies_cache_built_;
192+ local_fallback = &cookies_local;
193+ break ;
194+ default :
195+ // Unsupported kind: hand back the headers cache (kept empty)
196+ // as a safe fallback; the public API never reaches here.
197+ cache = &headers_cached_;
198+ built = &headers_cache_built_;
199+ local_fallback = &headers_local;
200+ break ;
201+ }
202+
203+ if (*built) {
204+ return *cache;
205+ }
173206
174- // Test-request path: connection_ is null, build view map from local storage.
207+ // Test-request path: connection_ is null, build the view map from
208+ // local owning storage (the create_test_request builder populated it).
175209 if (connection_ == nullptr ) {
176- const auto * map = [&]() -> const http::header_map* {
177- switch (kind) {
178- case MHD_HEADER_KIND: return &headers_local;
179- case MHD_FOOTER_KIND: return &footers_local;
180- case MHD_COOKIE_KIND: return &cookies_local;
181- default : return nullptr ;
182- }
183- }();
184- if (map != nullptr ) {
185- for (const auto & [k, v] : *map) {
186- headers[k] = v;
210+ if (local_fallback != nullptr ) {
211+ for (const auto & [k, v] : *local_fallback) {
212+ (*cache)[k] = v;
187213 }
188214 }
189- return headers;
215+ *built = true ;
216+ return *cache;
190217 }
191218
219+ // Live-request path: ask MHD to enumerate values for this kind into
220+ // the cache. The string_view keys/values alias MHD-owned storage that
221+ // outlives the request handler.
192222 MHD_get_connection_values (connection_, kind, &http_request_impl::build_request_header,
193- reinterpret_cast <void *>(&headers ));
194-
195- return headers ;
223+ reinterpret_cast <void *>(cache ));
224+ *built = true ;
225+ return *cache ;
196226}
197227
198228MHD_Result http_request_impl::build_request_args (void * cls, MHD_ValueKind kind,
@@ -652,18 +682,21 @@ void http_request::set_method(const std::string& method) {
652682 this ->method = method;
653683}
654684
655- const std::vector<std::string> http_request::get_path_pieces () const {
685+ const std::vector<std::string>& http_request::get_path_pieces () const {
686+ // TASK-017: lazily populate the public-typed mirror cache from the
687+ // (already-built) pmr-backed `path_pieces` and return it by const&.
688+ // Two caches in lockstep -- the pmr one stays arena-friendly for any
689+ // future internal consumer; the public one is what the API exposes.
656690 impl_->ensure_path_pieces_cached (path);
657- // path_pieces is now pmr-backed; copy element-wise back into a default-
658- // allocator std::vector<std::string> for the public return type. The
659- // copy is intrinsic to the v1 API contract; TASK-017 narrows this to
660- // a const& return that aliases the impl-side storage.
661- std::vector<std::string> out;
662- out.reserve (impl_->path_pieces .size ());
663- for (const auto & p : impl_->path_pieces ) {
664- out.emplace_back (p.data (), p.size ());
691+ if (!impl_->path_pieces_public_built_ ) {
692+ impl_->path_pieces_public_ .clear ();
693+ impl_->path_pieces_public_ .reserve (impl_->path_pieces .size ());
694+ for (const auto & p : impl_->path_pieces ) {
695+ impl_->path_pieces_public_ .emplace_back (p.data (), p.size ());
696+ }
697+ impl_->path_pieces_public_built_ = true ;
665698 }
666- return out ;
699+ return impl_-> path_pieces_public_ ;
667700}
668701
669702const std::string http_request::get_path_piece (int index) const {
@@ -725,24 +758,24 @@ std::string_view http_request::get_header(std::string_view key) const {
725758 return impl_->get_connection_value (key, MHD_HEADER_KIND);
726759}
727760
728- const http::header_view_map http_request::get_headers () const {
729- return impl_->get_headerlike_values (MHD_HEADER_KIND);
761+ const http::header_view_map& http_request::get_headers () const {
762+ return impl_->ensure_headerlike_cache (MHD_HEADER_KIND);
730763}
731764
732765std::string_view http_request::get_footer (std::string_view key) const {
733766 return impl_->get_connection_value (key, MHD_FOOTER_KIND);
734767}
735768
736- const http::header_view_map http_request::get_footers () const {
737- return impl_->get_headerlike_values (MHD_FOOTER_KIND);
769+ const http::header_view_map& http_request::get_footers () const {
770+ return impl_->ensure_headerlike_cache (MHD_FOOTER_KIND);
738771}
739772
740773std::string_view http_request::get_cookie (std::string_view key) const {
741774 return impl_->get_connection_value (key, MHD_COOKIE_KIND);
742775}
743776
744- const http::header_view_map http_request::get_cookies () const {
745- return impl_->get_headerlike_values (MHD_COOKIE_KIND);
777+ const http::header_view_map& http_request::get_cookies () const {
778+ return impl_->ensure_headerlike_cache (MHD_COOKIE_KIND);
746779}
747780
748781http_arg_value http_request::get_arg (std::string_view key) const {
@@ -772,17 +805,25 @@ std::string_view http_request::get_arg_flat(std::string_view key) const {
772805 return impl_->get_connection_value (key, MHD_GET_ARGUMENT_KIND);
773806}
774807
775- const http::arg_view_map http_request::get_args () const {
808+ const http::arg_view_map& http_request::get_args () const {
809+ // TASK-017: lazily populate the args view-map cache from the pmr-backed
810+ // `unescaped_args` (which is itself populated lazily by populate_args()).
776811 impl_->populate_args ();
777-
778- http::arg_view_map arguments;
779- for (const auto & [key, value] : impl_->unescaped_args ) {
780- auto & arg_values = arguments[key];
781- for (const auto & v : value) {
782- arg_values.values .push_back (v);
812+ if (!impl_->args_view_cache_built_ ) {
813+ impl_->args_view_cached_ .clear ();
814+ for (const auto & [key, value] : impl_->unescaped_args ) {
815+ // The string_view keys/values alias the pmr-backed strings owned
816+ // by `unescaped_args` -- same lifetime as the request.
817+ auto & arg_values = impl_->args_view_cached_ [
818+ std::string_view (key.data (), key.size ())];
819+ arg_values.values .reserve (value.size ());
820+ for (const auto & v : value) {
821+ arg_values.values .emplace_back (v.data (), v.size ());
822+ }
783823 }
824+ impl_->args_view_cache_built_ = true ;
784825 }
785- return arguments ;
826+ return impl_-> args_view_cached_ ;
786827}
787828
788829const std::map<std::string_view, std::string_view, http::arg_comparator> http_request::get_args_flat () const {
@@ -798,7 +839,7 @@ http::file_info& http_request::get_or_create_file_info(const std::string& key, c
798839 return impl_->files_ [key][upload_file_name];
799840}
800841
801- const std::map<std::string, std::map<std::string, http::file_info>> http_request::get_files () const {
842+ const std::map<std::string, std::map<std::string, http::file_info>>& http_request::get_files () const noexcept {
802843 return impl_->files_ ;
803844}
804845
0 commit comments