From 20bde47f5ce9416e303d21f31e9bf143a4fe33d7 Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Tue, 13 Jan 2026 22:59:35 +0300 Subject: [PATCH 1/2] http: implement slab allocation for HTTP header parsing --- src/node_http_parser.cc | 76 +++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 26ddbf57854672..1b51f36ad06e1c 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -124,62 +124,70 @@ class BindingData : public BaseObject { // helper class for the Parser struct StringPtr { - StringPtr() { - on_heap_ = false; - Reset(); - } - + // Memory impact: ~8KB per parser (66 StringPtr × 128 bytes). + static constexpr size_t kSlabSize = 128; - ~StringPtr() { - Reset(); - } + StringPtr() = default; + ~StringPtr() { Reset(); } + StringPtr(const StringPtr&) = delete; + StringPtr& operator=(const StringPtr&) = delete; // If str_ does not point to a heap string yet, this function makes it do // so. This is called at the end of each http_parser_execute() so as not // to leak references. See issue #2438 and test-http-parser-bad-ref.js. void Save() { - if (!on_heap_ && size_ > 0) { - char* s = new char[size_]; - memcpy(s, str_, size_); - str_ = s; - on_heap_ = true; + if (!on_heap_ && !using_slab_ && size_ > 0) { + if (size_ <= kSlabSize) { + memcpy(slab_, str_, size_); + str_ = slab_; + using_slab_ = true; + } else { + char* s = new char[size_]; + memcpy(s, str_, size_); + str_ = s; + on_heap_ = true; + } } } - void Reset() { if (on_heap_) { delete[] str_; on_heap_ = false; } - + using_slab_ = false; str_ = nullptr; size_ = 0; } - void Update(const char* str, size_t size) { if (str_ == nullptr) { str_ = str; - } else if (on_heap_ || str_ + size_ != str) { - // Non-consecutive input, make a copy on the heap. - // TODO(bnoordhuis) Use slab allocation, O(n) allocs is bad. - char* s = new char[size_ + size]; - memcpy(s, str_, size_); - memcpy(s + size_, str, size); - - if (on_heap_) - delete[] str_; - else + } else if (on_heap_ || using_slab_ || str_ + size_ != str) { + const size_t total = size_ + size; + + if (!on_heap_ && total <= kSlabSize) { + if (!using_slab_) { + memcpy(slab_, str_, size_); + using_slab_ = true; + } + memcpy(slab_ + size_, str, size); + str_ = slab_; + } else { + char* s = new char[total]; + memcpy(s, str_, size_); + memcpy(s + size_, str, size); + if (on_heap_) + delete[] str_; on_heap_ = true; - - str_ = s; + using_slab_ = false; + str_ = s; + } } size_ += size; } - Local ToString(Environment* env) const { if (size_ != 0) return OneByteString(env->isolate(), str_, size_); @@ -187,7 +195,6 @@ struct StringPtr { return String::Empty(env->isolate()); } - // Strip trailing OWS (SPC or HTAB) from string. Local ToTrimmedString(Environment* env) { while (size_ > 0 && IsOWS(str_[size_ - 1])) { @@ -196,10 +203,11 @@ struct StringPtr { return ToString(env); } - - const char* str_; - bool on_heap_; - size_t size_; + const char* str_ = nullptr; + bool on_heap_ = false; + bool using_slab_ = false; + size_t size_ = 0; + char slab_[kSlabSize]; }; class Parser; From 91155569ed4f6127a9f582f50c0b953e5b3b89f7 Mon Sep 17 00:00:00 2001 From: Mert Can Altin Date: Tue, 13 Jan 2026 23:06:00 +0300 Subject: [PATCH 2/2] lint --- src/node_http_parser.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 1b51f36ad06e1c..97c31b4d178d52 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -178,8 +178,7 @@ struct StringPtr { char* s = new char[total]; memcpy(s, str_, size_); memcpy(s + size_, str, size); - if (on_heap_) - delete[] str_; + if (on_heap_) delete[] str_; on_heap_ = true; using_slab_ = false; str_ = s;