Skip to content

Commit 20bde47

Browse files
committed
http: implement slab allocation for HTTP header parsing
1 parent b5bda89 commit 20bde47

File tree

1 file changed

+42
-34
lines changed

1 file changed

+42
-34
lines changed

src/node_http_parser.cc

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -124,70 +124,77 @@ class BindingData : public BaseObject {
124124

125125
// helper class for the Parser
126126
struct StringPtr {
127-
StringPtr() {
128-
on_heap_ = false;
129-
Reset();
130-
}
131-
127+
// Memory impact: ~8KB per parser (66 StringPtr × 128 bytes).
128+
static constexpr size_t kSlabSize = 128;
132129

133-
~StringPtr() {
134-
Reset();
135-
}
130+
StringPtr() = default;
131+
~StringPtr() { Reset(); }
136132

133+
StringPtr(const StringPtr&) = delete;
134+
StringPtr& operator=(const StringPtr&) = delete;
137135

138136
// If str_ does not point to a heap string yet, this function makes it do
139137
// so. This is called at the end of each http_parser_execute() so as not
140138
// to leak references. See issue #2438 and test-http-parser-bad-ref.js.
141139
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;
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;
150+
}
147151
}
148152
}
149153

150-
151154
void Reset() {
152155
if (on_heap_) {
153156
delete[] str_;
154157
on_heap_ = false;
155158
}
156-
159+
using_slab_ = false;
157160
str_ = nullptr;
158161
size_ = 0;
159162
}
160163

161-
162164
void Update(const char* str, size_t size) {
163165
if (str_ == nullptr) {
164166
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
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_;
177+
} else {
178+
char* s = new char[total];
179+
memcpy(s, str_, size_);
180+
memcpy(s + size_, str, size);
181+
if (on_heap_)
182+
delete[] str_;
175183
on_heap_ = true;
176-
177-
str_ = s;
184+
using_slab_ = false;
185+
str_ = s;
186+
}
178187
}
179188
size_ += size;
180189
}
181190

182-
183191
Local<String> ToString(Environment* env) const {
184192
if (size_ != 0)
185193
return OneByteString(env->isolate(), str_, size_);
186194
else
187195
return String::Empty(env->isolate());
188196
}
189197

190-
191198
// Strip trailing OWS (SPC or HTAB) from string.
192199
Local<String> ToTrimmedString(Environment* env) {
193200
while (size_ > 0 && IsOWS(str_[size_ - 1])) {
@@ -196,10 +203,11 @@ struct StringPtr {
196203
return ToString(env);
197204
}
198205

199-
200-
const char* str_;
201-
bool on_heap_;
202-
size_t size_;
206+
const char* str_ = nullptr;
207+
bool on_heap_ = false;
208+
bool using_slab_ = false;
209+
size_t size_ = 0;
210+
char slab_[kSlabSize];
203211
};
204212

205213
class Parser;

0 commit comments

Comments
 (0)