Skip to content

Commit 24a3ef9

Browse files
committed
Performance improvement for multipart form data file upload.
1 parent bc3e098 commit 24a3ef9

File tree

1 file changed

+89
-60
lines changed

1 file changed

+89
-60
lines changed

httplib.h

Lines changed: 89 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2874,7 +2874,8 @@ inline const char *status_message(int status) {
28742874
}
28752875

28762876
inline bool can_compress_content_type(const std::string &content_type) {
2877-
return (!content_type.find("text/") && content_type != "text/event-stream") ||
2877+
return (!content_type.rfind("text/", 0) &&
2878+
content_type != "text/event-stream") ||
28782879
content_type == "image/svg+xml" ||
28792880
content_type == "application/javascript" ||
28802881
content_type == "application/json" ||
@@ -3681,17 +3682,15 @@ class MultipartFormDataParser {
36813682
static const std::string dash_ = "--";
36823683
static const std::string crlf_ = "\r\n";
36833684

3684-
buf_.append(buf, n); // TODO: performance improvement
3685+
buf_append(buf, n);
36853686

3686-
while (!buf_.empty()) {
3687+
while (buf_size() > 0) {
36873688
switch (state_) {
36883689
case 0: { // Initial boundary
36893690
auto pattern = dash_ + boundary_ + crlf_;
3690-
if (pattern.size() > buf_.size()) { return true; }
3691-
auto pos = buf_.find(pattern);
3692-
if (pos != 0) { return false; }
3693-
buf_.erase(0, pattern.size());
3694-
off_ += pattern.size();
3691+
if (pattern.size() > buf_size()) { return true; }
3692+
if (!buf_start_with(pattern)) { return false; }
3693+
buf_erase(pattern.size());
36953694
state_ = 1;
36963695
break;
36973696
}
@@ -3701,22 +3700,21 @@ class MultipartFormDataParser {
37013700
break;
37023701
}
37033702
case 2: { // Headers
3704-
auto pos = buf_.find(crlf_);
3705-
while (pos != std::string::npos) {
3703+
auto pos = buf_find(crlf_);
3704+
while (pos < buf_size()) {
37063705
// Empty line
37073706
if (pos == 0) {
37083707
if (!header_callback(file_)) {
37093708
is_valid_ = false;
37103709
return false;
37113710
}
3712-
buf_.erase(0, crlf_.size());
3713-
off_ += crlf_.size();
3711+
buf_erase(crlf_.size());
37143712
state_ = 3;
37153713
break;
37163714
}
37173715

37183716
static const std::string header_name = "content-type:";
3719-
const auto header = buf_.substr(0, pos);
3717+
const auto header = buf_head(pos);
37203718
if (start_with_case_ignore(header, header_name)) {
37213719
file_.content_type = trim_copy(header.substr(header_name.size()));
37223720
} else {
@@ -3727,66 +3725,60 @@ class MultipartFormDataParser {
37273725
}
37283726
}
37293727

3730-
buf_.erase(0, pos + crlf_.size());
3731-
off_ += pos + crlf_.size();
3732-
pos = buf_.find(crlf_);
3728+
buf_erase(pos + crlf_.size());
3729+
pos = buf_find(crlf_);
37333730
}
37343731
if (state_ != 3) { return true; }
37353732
break;
37363733
}
37373734
case 3: { // Body
37383735
{
37393736
auto pattern = crlf_ + dash_;
3740-
if (pattern.size() > buf_.size()) { return true; }
3737+
if (pattern.size() > buf_size()) { return true; }
37413738

3742-
auto pos = find_string(buf_, pattern);
3739+
auto pos = buf_find(pattern);
37433740

3744-
if (!content_callback(buf_.data(), pos)) {
3741+
if (!content_callback(buf_data(), pos)) {
37453742
is_valid_ = false;
37463743
return false;
37473744
}
37483745

3749-
off_ += pos;
3750-
buf_.erase(0, pos);
3746+
buf_erase(pos);
37513747
}
37523748
{
37533749
auto pattern = crlf_ + dash_ + boundary_;
3754-
if (pattern.size() > buf_.size()) { return true; }
3750+
if (pattern.size() > buf_size()) { return true; }
37553751

3756-
auto pos = buf_.find(pattern);
3757-
if (pos != std::string::npos) {
3758-
if (!content_callback(buf_.data(), pos)) {
3752+
auto pos = buf_find(pattern);
3753+
if (pos < buf_size()) {
3754+
if (!content_callback(buf_data(), pos)) {
37593755
is_valid_ = false;
37603756
return false;
37613757
}
37623758

3763-
off_ += pos + pattern.size();
3764-
buf_.erase(0, pos + pattern.size());
3759+
buf_erase(pos + pattern.size());
37653760
state_ = 4;
37663761
} else {
3767-
if (!content_callback(buf_.data(), pattern.size())) {
3762+
if (!content_callback(buf_data(), pattern.size())) {
37683763
is_valid_ = false;
37693764
return false;
37703765
}
37713766

3772-
off_ += pattern.size();
3773-
buf_.erase(0, pattern.size());
3767+
buf_erase(pattern.size());
37743768
}
37753769
}
37763770
break;
37773771
}
37783772
case 4: { // Boundary
3779-
if (crlf_.size() > buf_.size()) { return true; }
3780-
if (buf_.compare(0, crlf_.size(), crlf_) == 0) {
3781-
buf_.erase(0, crlf_.size());
3782-
off_ += crlf_.size();
3773+
if (crlf_.size() > buf_size()) { return true; }
3774+
if (buf_start_with(crlf_)) {
3775+
buf_erase(crlf_.size());
37833776
state_ = 1;
37843777
} else {
37853778
auto pattern = dash_ + crlf_;
3786-
if (pattern.size() > buf_.size()) { return true; }
3787-
if (buf_.compare(0, pattern.size(), pattern) == 0) {
3788-
buf_.erase(0, pattern.size());
3789-
off_ += pattern.size();
3779+
if (pattern.size() > buf_size()) { return true; }
3780+
if (buf_start_with(pattern)) {
3781+
buf_erase(pattern.size());
37903782
is_valid_ = true;
37913783
state_ = 5;
37923784
} else {
@@ -3821,41 +3813,80 @@ class MultipartFormDataParser {
38213813
return true;
38223814
}
38233815

3824-
bool start_with(const std::string &a, size_t off,
3816+
std::string boundary_;
3817+
3818+
size_t state_ = 0;
3819+
bool is_valid_ = false;
3820+
MultipartFormData file_;
3821+
3822+
// Buffer
3823+
bool start_with(const std::string &a, size_t spos, size_t epos,
38253824
const std::string &b) const {
3826-
if (a.size() - off < b.size()) { return false; }
3825+
if (epos - spos < b.size()) { return false; }
38273826
for (size_t i = 0; i < b.size(); i++) {
3828-
if (a[i + off] != b[i]) { return false; }
3827+
if (a[i + spos] != b[i]) { return false; }
38293828
}
38303829
return true;
38313830
}
38323831

3833-
size_t find_string(const std::string &s, const std::string &pattern) const {
3834-
auto c = pattern.front();
3832+
size_t buf_size() const { return buf_epos_ - buf_spos_; }
38353833

3836-
size_t off = 0;
3837-
while (off < s.size()) {
3838-
auto pos = s.find(c, off);
3839-
if (pos == std::string::npos) { return s.size(); }
3834+
const char *buf_data() const { return &buf_[buf_spos_]; }
38403835

3841-
auto rem = s.size() - pos;
3842-
if (pattern.size() > rem) { return pos; }
3836+
std::string buf_head(size_t l) const { return buf_.substr(buf_spos_, l); }
38433837

3844-
if (start_with(s, pos, pattern)) { return pos; }
3838+
bool buf_start_with(const std::string &s) const {
3839+
return start_with(buf_, buf_spos_, buf_epos_, s);
3840+
}
3841+
3842+
size_t buf_find(const std::string &s) const {
3843+
auto c = s.front();
3844+
3845+
size_t off = buf_spos_;
3846+
while (off < buf_epos_) {
3847+
auto pos = off;
3848+
while (true) {
3849+
if (pos == buf_epos_) { return buf_size(); }
3850+
if (buf_[pos] == c) { break; }
3851+
pos++;
3852+
}
3853+
3854+
auto remaining_size = buf_epos_ - pos;
3855+
if (s.size() > remaining_size) { return buf_size(); }
3856+
3857+
if (start_with(buf_, pos, buf_epos_, s)) { return pos - buf_spos_; }
38453858

38463859
off = pos + 1;
38473860
}
38483861

3849-
return s.size();
3862+
return buf_size();
38503863
}
38513864

3852-
std::string boundary_;
3865+
void buf_append(const char *data, size_t n) {
3866+
auto remaining_size = buf_size();
3867+
if (remaining_size > 0) {
3868+
for (size_t i = 0; i < remaining_size; i++) {
3869+
buf_[i] = buf_[buf_spos_ + i];
3870+
}
3871+
}
3872+
buf_spos_ = 0;
3873+
buf_epos_ = remaining_size;
3874+
3875+
if (remaining_size + n > buf_.size()) {
3876+
buf_.resize(remaining_size + n);
3877+
}
3878+
3879+
for (size_t i = 0; i < n; i++) {
3880+
buf_[buf_epos_ + i] = data[i];
3881+
}
3882+
buf_epos_ += n;
3883+
}
3884+
3885+
void buf_erase(size_t size) { buf_spos_ += size; }
38533886

38543887
std::string buf_;
3855-
size_t state_ = 0;
3856-
bool is_valid_ = false;
3857-
size_t off_ = 0;
3858-
MultipartFormData file_;
3888+
size_t buf_spos_ = 0;
3889+
size_t buf_epos_ = 0;
38593890
};
38603891

38613892
inline std::string to_lower(const char *beg, const char *end) {
@@ -4318,7 +4349,7 @@ inline size_t Request::get_param_value_count(const char *key) const {
43184349

43194350
inline bool Request::is_multipart_form_data() const {
43204351
const auto &content_type = get_header_value("Content-Type");
4321-
return !content_type.find("multipart/form-data");
4352+
return !content_type.rfind("multipart/form-data", 0);
43224353
}
43234354

43244355
inline bool Request::has_file(const char *key) const {
@@ -5122,9 +5153,7 @@ Server::create_server_socket(const char *host, int port, int socket_flags,
51225153
if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
51235154
return false;
51245155
}
5125-
if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) {
5126-
return false;
5127-
}
5156+
if (::listen(sock, CPPHTTPLIB_LISTEN_BACKLOG)) { return false; }
51285157
return true;
51295158
});
51305159
}

0 commit comments

Comments
 (0)