@@ -122,66 +122,104 @@ class BindingData : public BaseObject {
122122 SET_MEMORY_INFO_NAME (BindingData)
123123};
124124
125- // helper class for the Parser
126- struct StringPtr {
125+ class Parser ;
126+
127+ class StringPtrAllocator {
128+ public:
127129 // Memory impact: ~8KB per parser (66 StringPtr × 128 bytes).
128- static constexpr size_t kSlabSize = 128 ;
130+ static constexpr size_t kSlabSize = 8192 ;
131+
132+ StringPtrAllocator () { buffer_.SetLength (0 ); }
133+
134+ // Allocate memory from the slab. Returns nullptr if full.
135+ char * Allocate (size_t size) {
136+ const size_t current = buffer_.length ();
137+ if (current + size > kSlabSize ) {
138+ return nullptr ;
139+ }
140+ buffer_.SetLength (current + size);
141+ return buffer_.out () + current;
142+ }
143+
144+ // Check if pointer is within this allocator's buffer.
145+ bool Contains (const char * ptr) const {
146+ return ptr >= buffer_.out () && ptr < buffer_.out () + buffer_.capacity ();
147+ }
148+ // Reset allocator for new message.
149+ void Reset () { buffer_.SetLength (0 ); }
129150
151+ private:
152+ MaybeStackBuffer<char , kSlabSize > buffer_;
153+ };
154+
155+ struct StringPtr {
130156 StringPtr () = default ;
131157 ~StringPtr () { Reset (); }
132158
133159 StringPtr (const StringPtr&) = delete ;
134160 StringPtr& operator =(const StringPtr&) = delete ;
135161
136- // If str_ does not point to a heap string yet, this function makes it do
162+ void SetAllocator (StringPtrAllocator* allocator) { allocator_ = allocator; }
163+
164+ // If str_ does not point to owned storage yet, this function makes it do
137165 // so. This is called at the end of each http_parser_execute() so as not
138166 // to leak references. See issue #2438 and test-http-parser-bad-ref.js.
139167 void Save () {
140- if (!on_heap_ && !using_slab_ && size_ > 0 ) {
141- if (size_ <= kSlabSize ) {
142- memcpy (slab_, str_, size_);
143- str_ = slab_;
144- using_slab_ = true ;
145- } else {
146- char * s = new char [size_];
147- memcpy (s, str_, size_);
148- str_ = s;
149- on_heap_ = true ;
168+ if (str_ == nullptr || on_heap_ ||
169+ (allocator_ != nullptr && allocator_->Contains (str_))) {
170+ return ;
171+ }
172+ // Try allocator first, fall back to heap
173+ if (allocator_ != nullptr ) {
174+ char * ptr = allocator_->Allocate (size_);
175+ if (ptr != nullptr ) {
176+ memcpy (ptr, str_, size_);
177+ str_ = ptr;
178+ return ;
150179 }
151180 }
181+ char * s = new char [size_];
182+ memcpy (s, str_, size_);
183+ str_ = s;
184+ on_heap_ = true ;
152185 }
153186
154187 void Reset () {
155188 if (on_heap_) {
156189 delete[] str_;
157190 on_heap_ = false ;
158191 }
159- using_slab_ = false ;
160192 str_ = nullptr ;
161193 size_ = 0 ;
162194 }
163195
164196 void Update (const char * str, size_t size) {
165197 if (str_ == nullptr ) {
166198 str_ = str;
167- } else if (on_heap_ || using_slab_ || str_ + size_ != str) {
168- const size_t total = size_ + size;
169-
170- if (!on_heap_ && total <= kSlabSize ) {
171- if (!using_slab_) {
172- memcpy (slab_, str_, size_);
173- using_slab_ = true ;
174- }
175- memcpy (slab_ + size_, str, size);
176- str_ = slab_;
199+ } else if (on_heap_ ||
200+ (allocator_ != nullptr && allocator_->Contains (str_)) ||
201+ str_ + size_ != str) {
202+ // Non-consecutive input, make a copy
203+ const size_t new_size = size_ + size;
204+ char * new_str = nullptr ;
205+
206+ // Try allocator first (if not already on heap)
207+ if (!on_heap_ && allocator_ != nullptr ) {
208+ new_str = allocator_->Allocate (new_size);
209+ }
210+
211+ if (new_str != nullptr ) {
212+ memcpy (new_str, str_, size_);
213+ memcpy (new_str + size_, str, size);
214+ str_ = new_str;
177215 } else {
178- char * s = new char [total];
216+ // Fall back to heap
217+ char * s = new char [new_size];
179218 memcpy (s, str_, size_);
180219 memcpy (s + size_, str, size);
181220 if (on_heap_) delete[] str_;
182- on_heap_ = true ;
183- using_slab_ = false ;
184221 str_ = s;
222+ on_heap_ = true ;
185223 }
186224 }
187225 size_ += size;
@@ -204,13 +242,10 @@ struct StringPtr {
204242
205243 const char * str_ = nullptr ;
206244 bool on_heap_ = false ;
207- bool using_slab_ = false ;
208245 size_t size_ = 0 ;
209- char slab_[ kSlabSize ] ;
246+ StringPtrAllocator* allocator_ = nullptr ;
210247};
211248
212- class Parser ;
213-
214249struct ParserComparator {
215250 bool operator ()(const Parser* lhs, const Parser* rhs) const ;
216251};
@@ -267,6 +302,13 @@ class Parser : public AsyncWrap, public StreamListener {
267302 current_buffer_len_(0 ),
268303 current_buffer_data_(nullptr ),
269304 binding_data_(binding_data) {
305+ // Wire up all StringPtrs to use the shared allocator
306+ for (size_t i = 0 ; i < kMaxHeaderFieldsCount ; i++) {
307+ fields_[i].SetAllocator (&allocator_);
308+ values_[i].SetAllocator (&allocator_);
309+ }
310+ url_.SetAllocator (&allocator_);
311+ status_message_.SetAllocator (&allocator_);
270312 }
271313
272314 SET_NO_MEMORY_INFO ()
@@ -285,6 +327,7 @@ class Parser : public AsyncWrap, public StreamListener {
285327 headers_completed_ = false ;
286328 chunk_extensions_nread_ = 0 ;
287329 last_message_start_ = uv_hrtime ();
330+ allocator_.Reset ();
288331 url_.Reset ();
289332 status_message_.Reset ();
290333
@@ -1013,6 +1056,7 @@ class Parser : public AsyncWrap, public StreamListener {
10131056
10141057
10151058 llhttp_t parser_;
1059+ StringPtrAllocator allocator_; // shared slab for all StringPtrs
10161060 StringPtr fields_[kMaxHeaderFieldsCount ]; // header fields
10171061 StringPtr values_[kMaxHeaderFieldsCount ]; // header values
10181062 StringPtr url_;
0 commit comments