@@ -122,72 +122,116 @@ class BindingData : public BaseObject {
122122 SET_MEMORY_INFO_NAME (BindingData)
123123};
124124
125- // helper class for the Parser
126- struct StringPtr {
127- StringPtr () {
128- on_heap_ = false ;
129- Reset ();
130- }
125+ class Parser ;
126+
127+ class StringPtrAllocator {
128+ public:
129+ // Memory impact: ~8KB per parser (66 StringPtr × 128 bytes).
130+ static constexpr size_t kSlabSize = 8192 ;
131131
132+ StringPtrAllocator () = default ;
132133
133- ~StringPtr () {
134- Reset ();
134+ // Allocate memory from the slab. Returns nullptr if full.
135+ char * TryAllocate (size_t size) {
136+ if (length_ + size > kSlabSize ) {
137+ return nullptr ;
138+ }
139+ char * ptr = buffer_ + length_;
140+ length_ += size;
141+ return ptr;
142+ }
143+
144+ // Check if pointer is within this allocator's buffer.
145+ bool Contains (const char * ptr) const {
146+ return ptr >= buffer_ && ptr < buffer_ + kSlabSize ;
135147 }
136148
149+ // Reset allocator for new message.
150+ void Reset () { length_ = 0 ; }
137151
138- // If str_ does not point to a heap string yet, this function makes it do
152+ private:
153+ char buffer_[kSlabSize ];
154+ size_t length_ = 0 ;
155+ };
156+
157+ struct StringPtr {
158+ StringPtr () = default ;
159+ ~StringPtr () { Reset (); }
160+
161+ StringPtr (const StringPtr&) = delete ;
162+ StringPtr& operator =(const StringPtr&) = delete ;
163+
164+ // If str_ does not point to owned storage yet, this function makes it do
139165 // so. This is called at the end of each http_parser_execute() so as not
140166 // to leak references. See issue #2438 and test-http-parser-bad-ref.js.
141- void Save () {
142- if (!on_heap_ && size_ > 0 ) {
143- char * s = new char [size_];
144- memcpy (s, str_, size_);
145- str_ = s;
146- on_heap_ = true ;
167+ void Save (StringPtrAllocator* allocator) {
168+ if (str_ == nullptr || on_heap_ ||
169+ (allocator != nullptr && allocator->Contains (str_))) {
170+ return ;
147171 }
172+ // Try allocator first, fall back to heap
173+ if (allocator != nullptr ) {
174+ char * ptr = allocator->TryAllocate (size_);
175+ if (ptr != nullptr ) {
176+ memcpy (ptr, str_, size_);
177+ str_ = ptr;
178+ return ;
179+ }
180+ }
181+ char * s = new char [size_];
182+ memcpy (s, str_, size_);
183+ str_ = s;
184+ on_heap_ = true ;
148185 }
149186
150-
151187 void Reset () {
152188 if (on_heap_) {
153189 delete[] str_;
154190 on_heap_ = false ;
155191 }
156-
157192 str_ = nullptr ;
158193 size_ = 0 ;
159194 }
160195
161-
162- void Update (const char * str, size_t size) {
196+ void Update (const char * str, size_t size, StringPtrAllocator* allocator) {
163197 if (str_ == nullptr ) {
164198 str_ = str;
165- } else if (on_heap_ || str_ + size_ != str) {
166- // Non-consecutive input, make a copy on the heap.
167- // TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad.
168- char * s = new char [size_ + size];
169- memcpy (s, str_, size_) ;
170- memcpy (s + size_, str, size) ;
171-
172- if (on_heap_ )
173- delete[] str_;
174- else
175- on_heap_ = true ;
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-> TryAllocate (new_size);
209+ }
176210
177- str_ = s;
211+ if (new_str != nullptr ) {
212+ memcpy (new_str, str_, size_);
213+ memcpy (new_str + size_, str, size);
214+ str_ = new_str;
215+ } else {
216+ // Fall back to heap
217+ char * s = new char [new_size];
218+ memcpy (s, str_, size_);
219+ memcpy (s + size_, str, size);
220+ if (on_heap_) delete[] str_;
221+ str_ = s;
222+ on_heap_ = true ;
223+ }
178224 }
179225 size_ += size;
180226 }
181227
182-
183228 Local<String> ToString (Environment* env) const {
184229 if (size_ != 0 )
185230 return OneByteString (env->isolate (), str_, size_);
186231 else
187232 return String::Empty (env->isolate ());
188233 }
189234
190-
191235 // Strip trailing OWS (SPC or HTAB) from string.
192236 Local<String> ToTrimmedString (Environment* env) {
193237 while (size_ > 0 && IsOWS (str_[size_ - 1 ])) {
@@ -196,14 +240,11 @@ struct StringPtr {
196240 return ToString (env);
197241 }
198242
199-
200- const char * str_;
201- bool on_heap_;
202- size_t size_;
243+ const char * str_ = nullptr ;
244+ bool on_heap_ = false ;
245+ size_t size_ = 0 ;
203246};
204247
205- class Parser ;
206-
207248struct ParserComparator {
208249 bool operator ()(const Parser* lhs, const Parser* rhs) const ;
209250};
@@ -259,8 +300,7 @@ class Parser : public AsyncWrap, public StreamListener {
259300 : AsyncWrap(binding_data->env (), wrap),
260301 current_buffer_len_(0 ),
261302 current_buffer_data_(nullptr ),
262- binding_data_(binding_data) {
263- }
303+ binding_data_(binding_data) {}
264304
265305 SET_NO_MEMORY_INFO ()
266306 SET_MEMORY_INFO_NAME(Parser)
@@ -278,6 +318,7 @@ class Parser : public AsyncWrap, public StreamListener {
278318 headers_completed_ = false ;
279319 chunk_extensions_nread_ = 0 ;
280320 last_message_start_ = uv_hrtime ();
321+ allocator_.Reset ();
281322 url_.Reset ();
282323 status_message_.Reset ();
283324
@@ -308,7 +349,7 @@ class Parser : public AsyncWrap, public StreamListener {
308349 return rv;
309350 }
310351
311- url_.Update (at, length);
352+ url_.Update (at, length, &allocator_ );
312353 return 0 ;
313354 }
314355
@@ -319,7 +360,7 @@ class Parser : public AsyncWrap, public StreamListener {
319360 return rv;
320361 }
321362
322- status_message_.Update (at, length);
363+ status_message_.Update (at, length, &allocator_ );
323364 return 0 ;
324365 }
325366
@@ -345,7 +386,7 @@ class Parser : public AsyncWrap, public StreamListener {
345386 CHECK_LT (num_fields_, kMaxHeaderFieldsCount );
346387 CHECK_EQ (num_fields_, num_values_ + 1 );
347388
348- fields_[num_fields_ - 1 ].Update (at, length);
389+ fields_[num_fields_ - 1 ].Update (at, length, &allocator_ );
349390
350391 return 0 ;
351392 }
@@ -366,7 +407,7 @@ class Parser : public AsyncWrap, public StreamListener {
366407 CHECK_LT (num_values_, arraysize (values_));
367408 CHECK_EQ (num_values_, num_fields_);
368409
369- values_[num_values_ - 1 ].Update (at, length);
410+ values_[num_values_ - 1 ].Update (at, length, &allocator_ );
370411
371412 return 0 ;
372413 }
@@ -594,15 +635,15 @@ class Parser : public AsyncWrap, public StreamListener {
594635 }
595636
596637 void Save () {
597- url_.Save ();
598- status_message_.Save ();
638+ url_.Save (&allocator_ );
639+ status_message_.Save (&allocator_ );
599640
600641 for (size_t i = 0 ; i < num_fields_; i++) {
601- fields_[i].Save ();
642+ fields_[i].Save (&allocator_ );
602643 }
603644
604645 for (size_t i = 0 ; i < num_values_; i++) {
605- values_[i].Save ();
646+ values_[i].Save (&allocator_ );
606647 }
607648 }
608649
@@ -1006,6 +1047,7 @@ class Parser : public AsyncWrap, public StreamListener {
10061047
10071048
10081049 llhttp_t parser_;
1050+ StringPtrAllocator allocator_; // shared slab for all StringPtrs
10091051 StringPtr fields_[kMaxHeaderFieldsCount ]; // header fields
10101052 StringPtr values_[kMaxHeaderFieldsCount ]; // header values
10111053 StringPtr url_;
0 commit comments