This is an automated email from the ASF dual-hosted git repository.

zwoop pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 464c61349a cripts: shrink Context from 3408 to 1920 bytes, expand 
data[] to 16 (#13195)
464c61349a is described below

commit 464c61349a3ba9d251ad8f36c884e39c912dbd28
Author: Leif Hedstrom <[email protected]>
AuthorDate: Mon Jun 1 23:08:44 2026 -0600

    cripts: shrink Context from 3408 to 1920 bytes, expand data[] to 16 (#13195)
    
    * cripts: shrink Context from 3408 to 1920 bytes, expand data[] to 16
    
    Pimpl Url::Path and Url::Query state behind unique_ptr so the heavy
    vector/unordered_map/cripts::string members only allocate when a script
    actually decomposes the path or query. Lazy-allocate Pristine, Parent,
    and Remap From/To URLs in _UrlBlock — they're rarely all touched, and
    the embedded 384-byte Url objects dominated the per-txn cost. Pimpl
    Error::Reason for the same reason. Drop the unused INET6_ADDRSTRLEN
    buffer in detail::ConnBase. Make cripts::Url's destructor virtual now
    that we delete via unique_ptr. Bump CONTEXT_DATA_SLOTS from 4 to 16
    so scripts have room to stash more per-txn state — the 384-byte cost
    sits inside the budget freed by the URL/Connection cuts.
    
    * Address Copilot's review comments
    
    Bounds-check Path::Erase to avoid dereferencing a null _owner when
    ix is out of range — operator[] returns a default-constructed String
    in that case, and the subsequent p.operator=("") would then crash
    inside String::operator=.
---
 include/cripts/Connections.hpp |   7 +-
 include/cripts/Context.hpp     |  22 ++--
 include/cripts/Error.hpp       |   9 +-
 include/cripts/Urls.hpp        |  82 ++++++++++-----
 src/cripts/Context.cc          |  13 ++-
 src/cripts/Error.cc            |  10 +-
 src/cripts/Urls.cc             | 224 +++++++++++++++++++++++------------------
 7 files changed, 215 insertions(+), 152 deletions(-)

diff --git a/include/cripts/Connections.hpp b/include/cripts/Connections.hpp
index a8e851d7ae..88a76e896e 100644
--- a/include/cripts/Connections.hpp
+++ b/include/cripts/Connections.hpp
@@ -459,10 +459,9 @@ protected:
 
   void virtual _initialize() { _initialized = true; }
 
-  cripts::Transaction   *_state  = nullptr;
-  struct sockaddr const *_socket = nullptr;
-  TSVConn                _vc     = nullptr;
-  char                   _str[INET6_ADDRSTRLEN + 1];
+  cripts::Transaction   *_state       = nullptr;
+  struct sockaddr const *_socket      = nullptr;
+  TSVConn                _vc          = nullptr;
   bool                   _initialized = false;
 
 }; // End class ConnBase
diff --git a/include/cripts/Context.hpp b/include/cripts/Context.hpp
index 00bc36eafc..19b92bc77a 100644
--- a/include/cripts/Context.hpp
+++ b/include/cripts/Context.hpp
@@ -18,6 +18,7 @@
 #pragma once
 
 #include <array>
+#include <memory>
 #include <variant>
 #include "ts/ts.h"
 #include "ts/remap.h"
@@ -28,7 +29,7 @@
 #include "cripts/Connections.hpp"
 
 // These are pretty arbitrary for now
-constexpr int CONTEXT_DATA_SLOTS = 4;
+constexpr int CONTEXT_DATA_SLOTS = 16;
 
 namespace cripts
 {
@@ -132,23 +133,16 @@ public:
   } _cache;
 
   struct _UrlBlock {
-    cripts::Client::URL  &request;
-    cripts::Pristine::URL pristine;
-    cripts::Parent::URL   parent;
+    cripts::Client::URL                   &request;
+    std::unique_ptr<cripts::Pristine::URL> pristine;
+    std::unique_ptr<cripts::Parent::URL>   parent;
 
     struct {
-      cripts::Remap::From::URL from;
-      cripts::Remap::To::URL   to;
+      std::unique_ptr<cripts::Remap::From::URL> from;
+      std::unique_ptr<cripts::Remap::To::URL>   to;
     } remap;
 
-    _UrlBlock(Context *ctx, cripts::Client::URL &alias) : request(alias)
-    {
-      request.set_context(ctx);
-      pristine.set_context(ctx);
-      parent.set_context(ctx);
-      remap.from.set_context(ctx);
-      remap.to.set_context(ctx);
-    }
+    _UrlBlock(Context *ctx, cripts::Client::URL &alias) : request(alias) { 
request.set_context(ctx); }
 
   } _urls;
 }; // End class Context
diff --git a/include/cripts/Error.hpp b/include/cripts/Error.hpp
index da674abe60..f0957c7bf6 100644
--- a/include/cripts/Error.hpp
+++ b/include/cripts/Error.hpp
@@ -17,6 +17,7 @@
 */
 #pragma once
 
+#include <memory>
 #include <utility>
 
 #include "ts/ts.h"
@@ -132,10 +133,10 @@ public:
   void Execute(cripts::Context *context);
 
 private:
-  Reason _reason;
-  Status _status;
-  bool   _failed   = false;
-  bool   _redirect = false;
+  std::unique_ptr<Reason> _reason;
+  Status                  _status;
+  bool                    _failed   = false;
+  bool                    _redirect = false;
 };
 
 } // namespace cripts
diff --git a/include/cripts/Urls.hpp b/include/cripts/Urls.hpp
index c28f1d1dab..73580aa068 100644
--- a/include/cripts/Urls.hpp
+++ b/include/cripts/Urls.hpp
@@ -18,6 +18,7 @@
 #pragma once
 
 #include <algorithm>
+#include <memory>
 #include <string>
 #include <string_view>
 #include <unordered_map>
@@ -338,7 +339,7 @@ public:
 
     cripts::string_view GetSV() override;
     cripts::string      operator+=(cripts::string_view add);
-    self_type           operator=(cripts::string_view path);
+    self_type          &operator=(cripts::string_view path);
     String              operator[](Segments::size_type ix);
 
     void
@@ -346,8 +347,10 @@ public:
     {
       auto p = operator[](ix);
 
-      _size -= p.size();
-      p.operator=("");
+      if (_state && ix < _state->segments.size()) {
+        _state->size -= p.size();
+        p.operator=("");
+      }
     }
 
     void
@@ -368,7 +371,7 @@ public:
     void
     Flush()
     {
-      if (_modified) {
+      if (_state && _state->modified) {
         operator=(GetSV());
       }
     }
@@ -376,10 +379,23 @@ public:
   private:
     void _parser();
 
-    bool                      _modified = false;
-    Segments                  _segments; // Lazy loading on this
-    cripts::string            _storage;  // Used when recombining the segments 
into a full path
-    cripts::string::size_type _size = 0; // Mostly a guestimate for managing 
_storage
+    struct State {
+      bool                      modified = false;
+      Segments                  segments; // Ordered list of path segments
+      cripts::string            storage;  // Used when recombining the 
segments into a full path
+      cripts::string::size_type size = 0; // Mostly a guestimate for managing 
storage
+    };
+
+    State &
+    _ensure_state()
+    {
+      if (!_state) {
+        _state = std::make_unique<State>();
+      }
+      return *_state;
+    }
+
+    std::unique_ptr<State> _state; // Lazily allocated when path is parsed or 
modified
 
   }; // End class Url::Path
 
@@ -461,18 +477,18 @@ public:
 
     using Component::Component;
 
-    Query(cripts::string_view load)
+    Query(cripts::string_view load) : _state(std::make_unique<State>())
     {
-      _data       = load;
-      _size       = load.size();
-      _loaded     = true;
-      _standalone = true;
+      _data              = load;
+      _state->size       = load.size();
+      _loaded            = true;
+      _state->standalone = true;
     }
 
     void Reset() override;
 
     cripts::string_view GetSV() override;
-    self_type           operator=(cripts::string_view query);
+    self_type          &operator=(cripts::string_view query);
     cripts::string      operator+=(cripts::string_view add);
     Parameter           operator[](cripts::string_view param);
     void                Erase(cripts::string_view param);
@@ -482,7 +498,9 @@ public:
     Erase()
     {
       operator=("");
-      _size = 0;
+      if (_state) {
+        _state->size = 0;
+      }
     }
 
     void
@@ -503,14 +521,14 @@ public:
       // Make sure the hash and vector are populated
       _parser();
 
-      std::ranges::sort(_ordered);
-      _modified = true;
+      std::ranges::sort(_state->ordered);
+      _state->modified = true;
     }
 
     void
     Flush()
     {
-      if (_modified) {
+      if (_state && _state->modified) {
         operator=(GetSV());
       }
     }
@@ -518,19 +536,33 @@ public:
   private:
     void _parser();
 
-    bool           _modified   = false;
-    bool           _standalone = false;  // This component is used outside of 
a URL owner, not common
-    OrderedParams  _ordered;             // Ordered vector of all parameters, 
can be sorted etc.
-    HashParams     _hashed;              // Unordered map to go from "name" to 
the query parameter
-    cripts::string _storage;             // Used when recombining the query 
params into a
-                                         // full query string
-    cripts::string::size_type _size = 0; // Mostly a guesttimate
+    struct State {
+      bool                      modified   = false;
+      bool                      standalone = false; // This component is used 
outside of a URL owner, not common
+      OrderedParams             ordered;            // Ordered vector of all 
parameters, can be sorted etc.
+      HashParams                hashed;             // Unordered map to go 
from "name" to the query parameter
+      cripts::string            storage;            // Used when recombining 
the query params into a full query string
+      cripts::string::size_type size = 0;           // Mostly a guesttimate
+    };
+
+    State &
+    _ensure_state()
+    {
+      if (!_state) {
+        _state = std::make_unique<State>();
+      }
+      return *_state;
+    }
+
+    std::unique_ptr<State> _state; // Lazily allocated when query is parsed or 
modified
 
   }; // End class Url::Query
 
 public:
   Url() : scheme(this), host(this), port(this), path(this), query(this) {}
 
+  virtual ~Url() = default;
+
   // Clear anything "cached" in the Url, this is rather draconian, but it's 
safe...
   virtual void
   Reset()
diff --git a/src/cripts/Context.cc b/src/cripts/Context.cc
index 76bcc822b2..fe1a2ce389 100644
--- a/src/cripts/Context.cc
+++ b/src/cripts/Context.cc
@@ -48,13 +48,12 @@ Context::reset()
     _server.request.Reset();
   }
 
-  // Clear the initialized URLs before calling next hook
-  if (_urls.pristine.Initialized()) {
-    _urls.pristine.Reset();
-  }
-  if (_urls.parent.Initialized()) {
-    _urls.parent.Reset();
-  }
+  // Release lazy URLs entirely — they get recreated on demand for the next 
txn.
+  _urls.pristine.reset();
+  _urls.parent.reset();
+  _urls.remap.from.reset();
+  _urls.remap.to.reset();
+
   if (_cache.url.Initialized()) {
     _cache.url.Reset();
   }
diff --git a/src/cripts/Error.cc b/src/cripts/Error.cc
index 63935a5206..f93ea90b92 100644
--- a/src/cripts/Error.cc
+++ b/src/cripts/Error.cc
@@ -41,7 +41,10 @@ void
 Error::Reason::_set(cripts::Context *context, const cripts::string_view msg)
 {
   context->state.error.Fail();
-  context->state.error._reason._setter(msg);
+  if (!context->state.error._reason) {
+    context->state.error._reason = std::make_unique<Reason>();
+  }
+  context->state.error._reason->_setter(msg);
 }
 
 // For convenience, an optional Reason message can also be specified with the 
status
@@ -52,7 +55,10 @@ Error::Status::_set(cripts::Context *context, TSHttpStatus 
status, const cripts:
   context->state.error._status._setter(status);
 
   if (msg.size() > 0) {
-    context->state.error._reason._setter(msg);
+    if (!context->state.error._reason) {
+      context->state.error._reason = std::make_unique<Reason>();
+    }
+    context->state.error._reason->_setter(msg);
   }
 
   if (context->state.error.Redirected() || status == 
TS_HTTP_STATUS_MOVED_PERMANENTLY ||
diff --git a/src/cripts/Urls.cc b/src/cripts/Urls.cc
index f47e49f371..044b0c551a 100644
--- a/src/cripts/Urls.cc
+++ b/src/cripts/Urls.cc
@@ -112,25 +112,27 @@ Url::Port::operator=(int port)
 cripts::string_view
 Url::Path::GetSV()
 {
-  if (_segments.size() > 0) {
+  if (_state && _state->segments.size() > 0) {
     std::ostringstream path;
 
-    std::ranges::copy(_segments, 
std::ostream_iterator<cripts::string_view>(path, "/"));
-    _storage.reserve(_size);
-    _storage = std::string_view(path.str());
-    if (_storage.size() > 0) {
-      _storage.pop_back(); // Removes the trailing /
+    std::ranges::copy(_state->segments, 
std::ostream_iterator<cripts::string_view>(path, "/"));
+    _state->storage.reserve(_state->size);
+    _state->storage = std::string_view(path.str());
+    if (_state->storage.size() > 0) {
+      _state->storage.pop_back(); // Removes the trailing /
     }
 
-    return {_storage};
+    return {_state->storage};
   } else if (_owner && _data.empty()) {
     const char *value = nullptr;
     int         len   = 0;
 
     _ensure_initialized(_owner);
-    value   = TSUrlPathGet(_owner->_bufp, _owner->_urlp, &len);
-    _data   = cripts::string_view(value, len);
-    _size   = len;
+    value = TSUrlPathGet(_owner->_bufp, _owner->_urlp, &len);
+    _data = cripts::string_view(value, len);
+    if (_state) {
+      _state->size = len;
+    }
     _loaded = true;
   }
 
@@ -144,14 +146,14 @@ Url::Path::operator[](Segments::size_type ix)
 
   _ensure_initialized(_owner);
   _parser(); // Make sure the segments are loaded
-  if (ix < _segments.size()) {
-    ret._initialize(_segments[ix], this, ix);
+  if (_state && ix < _state->segments.size()) {
+    ret._initialize(_state->segments[ix], this, ix);
   }
 
   return ret; // RVO
 }
 
-Url::Path
+Url::Path &
 Url::Path::operator=(cripts::string_view path)
 {
   _ensure_initialized(_owner);
@@ -183,10 +185,11 @@ Url::Path::String::operator=(const cripts::string_view 
str)
 {
   _ensure_initialized(_owner->_owner);
   CAssert(!_owner->_owner->ReadOnly()); // This can not be a read-only URL
-  _owner->_size          -= _owner->_segments[_ix].size();
-  _owner->_segments[_ix]  = str;
-  _owner->_size          += str.size();
-  _owner->_modified       = true;
+  CAssert(_owner->_state);              // Should have been allocated by 
operator[]/_parser()
+  _owner->_state->size          -= _owner->_state->segments[_ix].size();
+  _owner->_state->segments[_ix]  = str;
+  _owner->_state->size          += str.size();
+  _owner->_state->modified       = true;
 
   return *this;
 }
@@ -196,51 +199,53 @@ Url::Path::Reset()
 {
   Component::Reset();
 
-  _segments.clear();
-  _storage.clear();
-  _size     = 0;
-  _modified = false;
+  _state.reset();
 }
 
 void
 Url::Path::Push(cripts::string_view val)
 {
   _parser();
-  _modified = true;
-  _segments.push_back(val);
+  auto &s    = _ensure_state();
+  s.modified = true;
+  s.segments.push_back(val);
 }
 
 void
 Url::Path::Insert(Segments::size_type ix, cripts::string_view val)
 {
   _parser();
-  _modified = true;
-  _segments.insert(_segments.begin() + ix, val);
+  auto &s    = _ensure_state();
+  s.modified = true;
+  s.segments.insert(s.segments.begin() + ix, val);
 }
 
 void
 Url::Path::_parser()
 {
-  if (_segments.size() == 0) {
-    _segments = Split('/');
+  auto &s = _ensure_state();
+
+  if (s.segments.size() == 0) {
+    s.segments = Split('/');
   }
 }
 
 Url::Query::Parameter &
 Url::Query::Parameter::operator=(const cripts::string_view str)
 {
-  CAssert(!_owner->_standalone);
+  CAssert(!_owner->_state || !_owner->_state->standalone);
   _ensure_initialized(_owner->_owner);
   CAssert(!_owner->_owner->ReadOnly()); // This can not be a read-only URL
-  auto iter = _owner->_hashed.find(_name);
+  auto &s    = _owner->_ensure_state();
+  auto  iter = s.hashed.find(_name);
 
-  if (iter != _owner->_hashed.end()) {
+  if (iter != s.hashed.end()) {
     iter->second = str; // Can be an empty string here!
   } else {
-    _owner->_ordered.push_back(_name);
-    _owner->_hashed[_name] = str;
+    s.ordered.push_back(_name);
+    s.hashed[_name] = str;
   }
-  _owner->_modified = true;
+  s.modified = true;
 
   return *this;
 }
@@ -248,31 +253,31 @@ Url::Query::Parameter::operator=(const 
cripts::string_view str)
 cripts::string_view
 Url::Query::GetSV()
 {
-  if (!_standalone) {
+  if (!_state || !_state->standalone) {
     _ensure_initialized(_owner);
   }
-  if (_ordered.size() > 0) {
-    _storage.clear();
-    _storage.reserve(_size);
+  if (_state && _state->ordered.size() > 0) {
+    _state->storage.clear();
+    _state->storage.reserve(_state->size);
 
     // ToDo: This is wonky, has to be a better std:: iteration to do here
-    for (const auto key : _ordered) {
-      auto iter = _hashed.find(key);
+    for (const auto key : _state->ordered) {
+      auto iter = _state->hashed.find(key);
 
-      if (_storage.size() > 0) {
-        _storage += "&";
+      if (_state->storage.size() > 0) {
+        _state->storage += "&";
       }
 
-      if (iter != _hashed.end()) {
-        _storage += iter->first;
+      if (iter != _state->hashed.end()) {
+        _state->storage += iter->first;
         if (iter->second.size() > 0) {
-          _storage += '=';
-          _storage += iter->second;
+          _state->storage += '=';
+          _state->storage += iter->second;
         }
       }
     }
 
-    return {_storage};
+    return {_state->storage};
   }
 
   // This gets weird when we modify the query parameter components, and can 
possibly empty
@@ -282,19 +287,21 @@ Url::Query::GetSV()
     const char *value = nullptr;
     int         len   = 0;
 
-    value   = TSUrlHttpQueryGet(_owner->_bufp, _owner->_urlp, &len);
-    _data   = cripts::string_view(value, len);
-    _size   = len;
+    value = TSUrlHttpQueryGet(_owner->_bufp, _owner->_urlp, &len);
+    _data = cripts::string_view(value, len);
+    if (_state) {
+      _state->size = len;
+    }
     _loaded = true;
   }
 
   return _data;
 }
 
-Url::Query
+Url::Query &
 Url::Query::operator=(cripts::string_view query)
 {
-  CAssert(!_standalone);
+  CAssert(!_state || !_state->standalone);
   _ensure_initialized(_owner);
   CAssert(!_owner->ReadOnly()); // This can not be a read-only URL
   TSUrlHttpQuerySet(_owner->_bufp, _owner->_urlp, query.data(), query.size());
@@ -323,15 +330,15 @@ Url::Query::Parameter
 Url::Query::operator[](cripts::string_view param)
 {
   // Make sure the hash and vector are populated, but only if we have an owner
-  if (!_standalone) {
+  if (!_state || !_state->standalone) {
     _ensure_initialized(_owner);
   }
   _parser();
 
   Parameter ret;
-  auto      iter = _hashed.find(param);
+  auto      iter = _state->hashed.find(param);
 
-  if (iter != _hashed.end()) {
+  if (iter != _state->hashed.end()) {
     ret._initialize(iter->first, iter->second, this);
   } else {
     ret._initialize(param, "", this);
@@ -346,21 +353,25 @@ Url::Query::Erase(cripts::string_view param)
   // Make sure the hash and vector are populated
   _parser();
 
-  auto iter  = _hashed.find(param);
-  auto viter = std::ranges::find(_ordered, param);
+  auto &s     = *_state;
+  auto  iter  = s.hashed.find(param);
+  auto  viter = std::ranges::find(s.ordered, param);
 
-  if (iter != _hashed.end()) {
-    _size -= iter->second.size(); // Size of the erased value
-    _hashed.erase(iter);
+  if (iter != s.hashed.end()) {
+    s.size -= iter->second.size(); // Size of the erased value
+    s.hashed.erase(iter);
 
-    CAssert(viter != _ordered.end());
-    _size -= viter->size(); // Length of the erased key
-    _ordered.erase(viter);
+    CAssert(viter != s.ordered.end());
+    s.size -= viter->size(); // Length of the erased key
+    s.ordered.erase(viter);
 
-    if (_ordered.size() == 0) {
+    if (s.ordered.size() == 0) {
       Reset();
+      // After Reset() the state is gone; re-create it just so _modified can 
be tracked.
+      _ensure_state().modified = true;
+    } else {
+      s.modified = true;
     }
-    _modified = true; // Make sure to set this after we reset above ...
   }
 }
 
@@ -371,21 +382,23 @@ 
Url::Query::Erase(std::initializer_list<cripts::string_view> list, bool keep)
     // Make sure the hash and vector are populated
     _parser();
 
-    for (auto viter = _ordered.begin(); viter != _ordered.end();) {
+    auto &s = *_state;
+
+    for (auto viter = s.ordered.begin(); viter != s.ordered.end();) {
       if (list.end() == std::ranges::find(list, *viter)) {
-        auto iter = _hashed.find(*viter);
-
-        CAssert(iter != _hashed.end());
-        _size -= iter->second.size(); // Size of the erased value
-        _size -= viter->size();       // Length of the erased key
-        _hashed.erase(iter);
-        viter     = _ordered.erase(viter);
-        _modified = true;
+        auto iter = s.hashed.find(*viter);
+
+        CAssert(iter != s.hashed.end());
+        s.size -= iter->second.size(); // Size of the erased value
+        s.size -= viter->size();       // Length of the erased key
+        s.hashed.erase(iter);
+        viter      = s.ordered.erase(viter);
+        s.modified = true;
       } else {
         ++viter;
       }
     }
-    if (_ordered.size() == 0) {
+    if (s.ordered.size() == 0) {
       Reset();
     }
   } else {
@@ -400,17 +413,15 @@ Url::Query::Reset()
 {
   Component::Reset();
 
-  _ordered.clear();
-  _hashed.clear();
-  _storage.clear();
-  _size     = 0;
-  _modified = false;
+  _state.reset();
 }
 
 void
 Url::Query::_parser()
 {
-  if (_ordered.size() == 0) {
+  auto &s = _ensure_state();
+
+  if (s.ordered.size() == 0) {
     for (const auto sv : Split('&')) {
       const auto          eq  = sv.find_first_of('=');
       cripts::string_view key = sv.substr(0, eq);
@@ -420,8 +431,8 @@ Url::Query::_parser()
         val = sv.substr(eq + 1);
       }
 
-      _ordered.push_back(key); // Keep the order
-      _hashed[key] = val;
+      s.ordered.push_back(key); // Keep the order
+      s.hashed[key] = val;
     }
   }
 }
@@ -447,17 +458,21 @@ Url::String()
 Pristine::URL &
 Pristine::URL::_get(cripts::Context *context)
 {
-  _ensure_initialized(&context->_urls.pristine);
-  return context->_urls.pristine;
+  auto &slot = context->_urls.pristine;
+
+  if (!slot) {
+    slot = std::make_unique<Pristine::URL>();
+    slot->set_context(context);
+  }
+  _ensure_initialized(slot.get());
+  return *slot;
 }
 
 void
 Pristine::URL::_initialize()
 {
-  Pristine::URL *url = &_context->_urls.pristine;
-
   TSAssert(_context->state.txnp);
-  if (TSHttpTxnPristineUrlGet(_context->state.txnp, &url->_bufp, &url->_urlp) 
!= TS_SUCCESS) {
+  if (TSHttpTxnPristineUrlGet(_context->state.txnp, &_bufp, &_urlp) != 
TS_SUCCESS) {
     _context->state.error.Fail();
   } else {
     super_type::_initialize(); // Only if successful
@@ -519,8 +534,14 @@ Remap::From::URL::_initialize()
 Remap::From::URL &
 Remap::From::URL::_get(cripts::Context *context)
 {
-  _ensure_initialized(&context->_urls.remap.from);
-  return context->_urls.remap.from;
+  auto &slot = context->_urls.remap.from;
+
+  if (!slot) {
+    slot = std::make_unique<Remap::From::URL>();
+    slot->set_context(context);
+  }
+  _ensure_initialized(slot.get());
+  return *slot;
 }
 
 void
@@ -539,8 +560,14 @@ Remap::To::URL::_initialize()
 Remap::To::URL &
 Remap::To::URL::_get(cripts::Context *context)
 {
-  _ensure_initialized(&context->_urls.remap.to);
-  return context->_urls.remap.to;
+  auto &slot = context->_urls.remap.to;
+
+  if (!slot) {
+    slot = std::make_unique<Remap::To::URL>();
+    slot->set_context(context);
+  }
+  _ensure_initialized(slot.get());
+  return *slot;
 }
 
 Cache::URL &
@@ -623,19 +650,24 @@ Cache::URL::_update()
 Parent::URL &
 Parent::URL::_get(cripts::Context *context)
 {
-  _ensure_initialized(&context->_urls.parent);
-  return context->_urls.parent;
+  auto &slot = context->_urls.parent;
+
+  if (!slot) {
+    slot = std::make_unique<Parent::URL>();
+    slot->set_context(context);
+  }
+  _ensure_initialized(slot.get());
+  return *slot;
 }
 
 void
 Parent::URL::_initialize()
 {
-  Parent::URL     *url = &_context->_urls.parent;
   Client::Request &req = Client::Request::_get(_context); // Repurpose / 
create the shared request object
 
-  if (TSUrlCreate(req.BufP(), &url->_urlp) == TS_SUCCESS) {
+  if (TSUrlCreate(req.BufP(), &_urlp) == TS_SUCCESS) {
     TSAssert(_context->state.txnp);
-    if (TSHttpTxnParentSelectionUrlGet(_context->state.txnp, req.BufP(), 
url->_urlp) != TS_SUCCESS) {
+    if (TSHttpTxnParentSelectionUrlGet(_context->state.txnp, req.BufP(), 
_urlp) != TS_SUCCESS) {
       _context->state.error.Fail();
       return;
     }

Reply via email to