about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
authornormalperson <normalperson@19e92222-5c0b-0410-8929-a290d50e31e9>2008-03-02 02:34:57 +0000
committernormalperson <normalperson@19e92222-5c0b-0410-8929-a290d50e31e9>2008-03-02 02:34:57 +0000
commit6b981f388abd352f57c984818a26182af56b1f6d (patch)
tree8b6a0ac3e54e92d3969a66bd9956049ce4154407 /ext
parentcf4c3f142c4403f3eb1bad4baedf58ec9247d1e5 (diff)
downloadunicorn-6b981f388abd352f57c984818a26182af56b1f6d.tar.gz
Allocate one string object and avoid appending to it causing it
to be resized.  Additionally, optimize the string toupper copy
so that it's done in a single pass.

Also, use an inline, locale-independent toupper() implementation
which should be more predictable for users with exotic locales
(HTTP header names are always ASCII).

The following test script was used:
require 'mongrel'
include Mongrel

parser = HttpParser.new
req = "GET /ruby HTTP/1.1\r\n" \
      "User-Agent: curl/7.12.3\r\n" \
      "Host: bogomips.org\r\n" \
      "Accept: */*\r\n" \
      "\r\n".freeze
hash = Hash.new
100000.times do
  parser.execute(hash, req, 0)
  parser.reset
  hash.clear
end


git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@989 19e92222-5c0b-0410-8929-a290d50e31e9
Diffstat (limited to 'ext')
-rw-r--r--ext/http11/ext_help.h2
-rw-r--r--ext/http11/http11.c29
2 files changed, 21 insertions, 10 deletions
diff --git a/ext/http11/ext_help.h b/ext/http11/ext_help.h
index 8b4d754..1017c64 100644
--- a/ext/http11/ext_help.h
+++ b/ext/http11/ext_help.h
@@ -4,6 +4,8 @@
 #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
 #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
 #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);
+#define ASCII_UPCASE_CHAR(ch) (ch & ~0x20)
+
 
 #ifdef DEBUG
 #define TRACE()  fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
diff --git a/ext/http11/http11.c b/ext/http11/http11.c
index dd9bc79..3cb6697 100644
--- a/ext/http11/http11.c
+++ b/ext/http11/http11.c
@@ -7,7 +7,6 @@
 #include <assert.h>
 #include <string.h>
 #include "http11_parser.h"
-#include <ctype.h>
 
 #ifndef RSTRING_PTR
 #define RSTRING_PTR(s) (RSTRING(s)->ptr)
@@ -69,7 +68,8 @@ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
 
 void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
 {
-  char *ch, *end;
+  char *ch;
+  const char *fch;
   VALUE req = (VALUE)data;
   VALUE v = Qnil;
   VALUE f = Qnil;
@@ -78,15 +78,24 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s
   VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
 
   v = rb_str_new(value, vlen);
-  f = rb_str_dup(global_http_prefix);
-  f = rb_str_buf_cat(f, field, flen);
 
-  for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) {
-    if(*ch == '-') {
-      *ch = '_';
-    } else {
-      *ch = toupper(*ch);
-    }
+  /*
+   * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len)
+   * in my testing, because: there's no minimum allocation length (and
+   * no check for it, either), RSTRING_LEN(f) does not need to be
+   * written twice, and and RSTRING_PTR(f) will already be
+   * null-terminated for us.
+   */
+  f = rb_str_new(NULL, RSTRING_LEN(global_http_prefix) + flen);
+  memcpy(RSTRING_PTR(f),
+         RSTRING_PTR(global_http_prefix),
+         RSTRING_LEN(global_http_prefix));
+
+  ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix);
+  for(fch = field; flen-- != 0; ++fch) {
+    *ch++ = (*fch >= 'a' && *fch <= 'z') ?
+            ASCII_UPCASE_CHAR(*fch) :
+            (*fch == '-' ? '_' : *fch);
   }
 
   rb_hash_aset(req, f, v);