dmitry Wed, 01 Sep 2010 15:34:48 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=302979
Log: Reduced overhead of FastCGI using near-perfect hash function and predcalculated hash values. Changed paths: U php/php-src/trunk/sapi/cgi/cgi_main.c U php/php-src/trunk/sapi/cgi/fastcgi.c U php/php-src/trunk/sapi/cgi/fastcgi.h
Modified: php/php-src/trunk/sapi/cgi/cgi_main.c =================================================================== --- php/php-src/trunk/sapi/cgi/cgi_main.c 2010-09-01 14:47:36 UTC (rev 302978) +++ php/php-src/trunk/sapi/cgi/cgi_main.c 2010-09-01 15:34:48 UTC (rev 302979) @@ -267,41 +267,30 @@ #define STDOUT_FILENO 1 #endif -static inline size_t sapi_cgibin_single_write(const char *str, uint str_length TSRMLS_DC) +static inline size_t sapi_cgi_single_write(const char *str, uint str_length TSRMLS_DC) { #ifdef PHP_WRITE_STDOUT long ret; -#else - size_t ret; -#endif - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - long ret = fcgi_write(request, FCGI_STDOUT, str, str_length); - if (ret <= 0) { - return 0; - } - return ret; - } - -#ifdef PHP_WRITE_STDOUT ret = write(STDOUT_FILENO, str, str_length); if (ret <= 0) return 0; return ret; #else + size_t ret; + ret = fwrite(str, 1, MIN(str_length, 16384), stdout); return ret; #endif } -static int sapi_cgibin_ub_write(const char *str, uint str_length TSRMLS_DC) +static int sapi_cgi_ub_write(const char *str, uint str_length TSRMLS_DC) { const char *ptr = str; uint remaining = str_length; size_t ret; while (remaining > 0) { - ret = sapi_cgibin_single_write(ptr, remaining TSRMLS_CC); + ret = sapi_cgi_single_write(ptr, remaining TSRMLS_CC); if (!ret) { php_handle_aborted_connection(); return str_length - remaining; @@ -313,21 +302,43 @@ return str_length; } +static int sapi_fcgi_ub_write(const char *str, uint str_length TSRMLS_DC) +{ + const char *ptr = str; + uint remaining = str_length; + fcgi_request *request = (fcgi_request*) SG(server_context); -static void sapi_cgibin_flush(void *server_context) + while (remaining > 0) { + long ret = fcgi_write(request, FCGI_STDOUT, ptr, remaining); + + if (ret <= 0) { + php_handle_aborted_connection(); + return str_length - remaining; + } + ptr += ret; + remaining -= ret; + } + + return str_length; +} + +static void sapi_cgi_flush(void *server_context) { - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) server_context; - if ( + if (fflush(stdout) == EOF) { + php_handle_aborted_connection(); + } +} + +static void sapi_fcgi_flush(void *server_context) +{ + fcgi_request *request = (fcgi_request*) server_context; + + if ( #ifndef PHP_WIN32 !parent && #endif request && !fcgi_flush(request, 0)) { - php_handle_aborted_connection(); - } - return; - } - if (fflush(stdout) == EOF) { + php_handle_aborted_connection(); } } @@ -493,12 +504,24 @@ count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes)); while (read_bytes < count_bytes) { - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes); - } else { - tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); + tmp_read_bytes = read(STDIN_FILENO, buffer + read_bytes, count_bytes - read_bytes); + if (tmp_read_bytes <= 0) { + break; } + read_bytes += tmp_read_bytes; + } + return read_bytes; +} + +static int sapi_fcgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) +{ + uint read_bytes = 0; + int tmp_read_bytes; + fcgi_request *request = (fcgi_request*) SG(server_context); + + count_bytes = MIN(count_bytes, (uint) SG(request_info).content_length - SG(read_post_bytes)); + while (read_bytes < count_bytes) { + tmp_read_bytes = fcgi_read(request, buffer + read_bytes, count_bytes - read_bytes); if (tmp_read_bytes <= 0) { break; } @@ -507,43 +530,33 @@ return read_bytes; } -static char *sapi_cgibin_getenv(char *name, size_t name_len TSRMLS_DC) +static char *sapi_cgi_getenv(char *name, size_t name_len TSRMLS_DC) { + return getenv(name); +} + +static char *sapi_fcgi_getenv(char *name, size_t name_len TSRMLS_DC) +{ /* when php is started by mod_fastcgi, no regular environment * is provided to PHP. It is always sent to PHP at the start * of a request. So we have to do our own lookup to get env * vars. This could probably be faster somehow. */ - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - return fcgi_getenv(request, name, name_len); - } + fcgi_request *request = (fcgi_request*) SG(server_context); + char *ret = fcgi_getenv(request, name, name_len); + + if (ret) return ret; /* if cgi, or fastcgi and not found in fcgi env check the regular environment */ return getenv(name); } -static char *_sapi_cgibin_putenv(char *name, char *value TSRMLS_DC) +static char *_sapi_cgi_putenv(char *name, int name_len, char *value) { - int name_len; #if !HAVE_SETENV || !HAVE_UNSETENV int len; char *buf; #endif - if (!name) { - return NULL; - } - name_len = strlen(name); - - /* when php is started by mod_fastcgi, no regular environment - * is provided to PHP. It is always sent to PHP at the start - * of a request. So we have to do our own lookup to get env - * vars. This could probably be faster somehow. */ - if (fcgi_is_fastcgi()) { - fcgi_request *request = (fcgi_request*) SG(server_context); - return fcgi_putenv(request, name, name_len, value); - } - #if HAVE_SETENV if (value) { setenv(name, value, 1); @@ -584,9 +597,16 @@ static char *sapi_cgi_read_cookies(TSRMLS_D) { - return sapi_cgibin_getenv((char *) "HTTP_COOKIE", sizeof("HTTP_COOKIE")-1 TSRMLS_CC); + return getenv("HTTP_COOKIE"); } +static char *sapi_fcgi_read_cookies(TSRMLS_D) +{ + fcgi_request *request = (fcgi_request*) SG(server_context); + + return FCGI_GETENV(request, "HTTP_COOKIE"); +} + static void cgi_php_load_env_var(char *var, unsigned int var_len, char *val, unsigned int val_len, void *arg TSRMLS_DC) { zval *array_ptr = (zval*)arg; @@ -598,7 +618,7 @@ } } -void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) +static void cgi_php_import_environment_variables(zval *array_ptr TSRMLS_DC) { if (PG(http_globals)[TRACK_VARS_ENV] && array_ptr != PG(http_globals)[TRACK_VARS_ENV] && @@ -648,25 +668,51 @@ if (CGIG(fix_pathinfo)) { char *script_name = SG(request_info).request_uri; - unsigned int script_name_len = script_name ? strlen(script_name) : 0; - char *path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); - unsigned int path_info_len = path_info ? strlen(path_info) : 0; + char *path_info; + unsigned int path_info_len; + int free_php_self; + ALLOCA_FLAG(use_heap) - php_self_len = script_name_len + path_info_len; - php_self = emalloc(php_self_len + 1); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); - if (script_name) { - memcpy(php_self, script_name, script_name_len + 1); + path_info = FCGI_GETENV(request, "PATH_INFO"); + } else { + path_info = getenv("PATH_INFO"); } + if (path_info) { - memcpy(php_self + script_name_len, path_info, path_info_len + 1); + unsigned int path_info_len = strlen(path_info); + + if (script_name) { + unsigned int script_name_len = strlen(script_name); + + php_self_len = script_name_len + path_info_len; + php_self = do_alloca(php_self_len + 1, use_heap); + memcpy(php_self, script_name, script_name_len + 1); + memcpy(php_self + script_name_len, path_info, path_info_len + 1); + free_php_self = 1; + } else { + php_self = path_info; + free_php_self = 0; + } + } else if (script_name) { + php_self = script_name; + php_self_len = strlen(script_name); + free_php_self = 0; + } else { + php_self = ""; + php_self_len = 0; + free_php_self = 0; } /* Build the special-case PHP_SELF variable for the CGI version */ if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &php_self, php_self_len, &php_self_len TSRMLS_CC)) { php_register_variable_safe("PHP_SELF", php_self, php_self_len, track_vars_array TSRMLS_CC); } - efree(php_self); + if (free_php_self) { + free_alloca(php_self, use_heap); + } } else { php_self = SG(request_info).request_uri ? SG(request_info).request_uri : ""; php_self_len = strlen(php_self); @@ -787,7 +833,13 @@ if (php_ini_has_per_host_config()) { /* Activate per-host-system-configuration defined in php.ini and stored into configuration_hash during startup */ - server_name = sapi_cgibin_getenv("SERVER_NAME", sizeof("SERVER_NAME") - 1 TSRMLS_CC); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + server_name = FCGI_GETENV(request, "SERVER_NAME"); + } else { + server_name = getenv("SERVER_NAME"); + } /* SERVER_NAME should also be defined at this stage..but better check it anyway */ if (server_name) { server_name_len = strlen(server_name); @@ -821,7 +873,13 @@ /* Load and activate user ini files in path starting from DOCUMENT_ROOT */ if (PG(user_ini_filename) && *PG(user_ini_filename)) { - doc_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1 TSRMLS_CC); + if (fcgi_is_fastcgi()) { + fcgi_request *request = (fcgi_request*) SG(server_context); + + doc_root = FCGI_GETENV(request, "DOCUMENT_ROOT"); + } else { + doc_root = getenv("DOCUMENT_ROOT"); + } /* DOCUMENT_ROOT should also be defined at this stage..but better check it anyway */ if (doc_root) { doc_root_len = strlen(doc_root); @@ -862,7 +920,7 @@ php_handle_aborted_connection(); } } else { - sapi_cgibin_flush(SG(server_context)); + sapi_cgi_flush(SG(server_context)); } } return SUCCESS; @@ -888,10 +946,10 @@ sapi_cgi_activate, /* activate */ sapi_cgi_deactivate, /* deactivate */ - sapi_cgibin_ub_write, /* unbuffered write */ - sapi_cgibin_flush, /* flush */ + sapi_cgi_ub_write, /* unbuffered write */ + sapi_cgi_flush, /* flush */ NULL, /* get uid */ - sapi_cgibin_getenv, /* getenv */ + sapi_cgi_getenv, /* getenv */ php_error, /* error handler */ @@ -966,34 +1024,43 @@ */ static int is_valid_path(const char *path) { - const char *p; + const char *p = path; - if (!path) { + if (UNEXPECTED(!p)) { return 0; } - p = strstr(path, ".."); - if (p) { - if ((p == path || IS_SLASH(*(p-1))) && - (*(p+2) == 0 || IS_SLASH(*(p+2))) - ) { - return 0; - } - while (1) { - p = strstr(p+1, ".."); - if (!p) { - break; + if (UNEXPECTED(*p == '.') && *(p+1) == '.' && (!*(p+2) || IS_SLASH(*(p+2)))) { + return 0; + } + while (*p) { + if (IS_SLASH(*p)) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(*p == '.')) { + p++; + if (UNEXPECTED(!*p) || UNEXPECTED(IS_SLASH(*p))) { + return 0; + } + } } - if (IS_SLASH(*(p-1)) && - (*(p+2) == 0 || IS_SLASH(*(p+2))) - ) { - return 0; - } } + p++; } return 1; } /* }}} */ +#define CGI_GETENV(name) \ + ((request) ? \ + FCGI_GETENV(request, name) : \ + getenv(name)) + +#define CGI_PUTENV(name, value) \ + ((request) ? \ + FCGI_PUTENV(request, name, value) : \ + _sapi_cgi_putenv(name, sizeof(name)-1, value)) + /* {{{ init_request_info initializes request_info structure @@ -1060,10 +1127,10 @@ Comments in the code below refer to using the above URL in a request */ -static void init_request_info(TSRMLS_D) +static void init_request_info(fcgi_request *request TSRMLS_DC) { - char *env_script_filename = sapi_cgibin_getenv("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME")-1 TSRMLS_CC); - char *env_path_translated = sapi_cgibin_getenv("PATH_TRANSLATED", sizeof("PATH_TRANSLATED")-1 TSRMLS_CC); + char *env_script_filename = CGI_GETENV("SCRIPT_FILENAME"); + char *env_path_translated = CGI_GETENV("PATH_TRANSLATED"); char *script_path_translated = env_script_filename; /* some broken servers do not have script_filename or argv0 @@ -1089,14 +1156,14 @@ * of the script will be retreived later via argc/argv */ if (script_path_translated) { const char *auth; - char *content_length = sapi_cgibin_getenv("CONTENT_LENGTH", sizeof("CONTENT_LENGTH")-1 TSRMLS_CC); - char *content_type = sapi_cgibin_getenv("CONTENT_TYPE", sizeof("CONTENT_TYPE")-1 TSRMLS_CC); - char *env_path_info = sapi_cgibin_getenv("PATH_INFO", sizeof("PATH_INFO")-1 TSRMLS_CC); - char *env_script_name = sapi_cgibin_getenv("SCRIPT_NAME", sizeof("SCRIPT_NAME")-1 TSRMLS_CC); + char *content_length = CGI_GETENV("CONTENT_LENGTH"); + char *content_type = CGI_GETENV("CONTENT_TYPE"); + char *env_path_info = CGI_GETENV("PATH_INFO"); + char *env_script_name = CGI_GETENV("SCRIPT_NAME"); #ifdef PHP_WIN32 /* Hack for buggy IIS that sets incorrect PATH_INFO */ - char *env_server_software = sapi_cgibin_getenv("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE")-1 TSRMLS_CC); + char *env_server_software = CGI_GETENV("SERVER_SOFTWARE"); if (env_server_software && env_script_name && @@ -1104,20 +1171,20 @@ strncmp(env_server_software, "Microsoft-IIS", sizeof("Microsoft-IIS")-1) == 0 && strncmp(env_path_info, env_script_name, strlen(env_script_name)) == 0 ) { - env_path_info = _sapi_cgibin_putenv("ORIG_PATH_INFO", env_path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("ORIG_PATH_INFO", env_path_info); env_path_info += strlen(env_script_name); if (*env_path_info == 0) { env_path_info = NULL; } - env_path_info = _sapi_cgibin_putenv("PATH_INFO", env_path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("PATH_INFO", env_path_info); } #endif if (CGIG(fix_pathinfo)) { struct stat st; char *real_path = NULL; - char *env_redirect_url = sapi_cgibin_getenv("REDIRECT_URL", sizeof("REDIRECT_URL")-1 TSRMLS_CC); - char *env_document_root = sapi_cgibin_getenv("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT")-1 TSRMLS_CC); + char *env_redirect_url = CGI_GETENV("REDIRECT_URL"); + char *env_document_root = CGI_GETENV("DOCUMENT_ROOT"); char *orig_path_translated = env_path_translated; char *orig_path_info = env_path_info; char *orig_script_name = env_script_name; @@ -1125,7 +1192,7 @@ int script_path_translated_len; if (!env_document_root && PG(doc_root)) { - env_document_root = _sapi_cgibin_putenv("DOCUMENT_ROOT", PG(doc_root) TSRMLS_CC); + env_document_root = CGI_PUTENV("DOCUMENT_ROOT", PG(doc_root)); /* fix docroot */ TRANSLATE_SLASHES(env_document_root); } @@ -1192,28 +1259,28 @@ if (orig_path_info) { char old; - _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); old = path_info[0]; path_info[0] = 0; if (!orig_script_name || strcmp(orig_script_name, env_path_info) != 0) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_path_info TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_path_info); } else { SG(request_info).request_uri = orig_script_name; } path_info[0] = old; } - env_path_info = _sapi_cgibin_putenv("PATH_INFO", path_info TSRMLS_CC); + env_path_info = CGI_PUTENV("PATH_INFO", path_info); } if (!orig_script_filename || strcmp(orig_script_filename, pt) != 0) { if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", pt TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", pt); } TRANSLATE_SLASHES(pt); @@ -1243,9 +1310,9 @@ } path_translated[path_translated_len] = '\0'; if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); } - env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC); + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); efree(path_translated); } else if ( env_script_name && strstr(pt, env_script_name) @@ -1262,9 +1329,9 @@ } path_translated[path_translated_len] = '\0'; if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); } - env_path_translated = _sapi_cgibin_putenv("PATH_TRANSLATED", path_translated TSRMLS_CC); + env_path_translated = CGI_PUTENV("PATH_TRANSLATED", path_translated); efree(path_translated); } break; @@ -1277,18 +1344,18 @@ * have failed anyway... we output 'no input file' now. */ if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", NULL TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", NULL); SG(sapi_headers).http_response_code = 404; } if (!SG(request_info).request_uri) { if (!orig_script_name || strcmp(orig_script_name, env_script_name) != 0) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); } else { SG(request_info).request_uri = orig_script_name; } @@ -1302,25 +1369,25 @@ (script_path_translated != orig_script_filename && strcmp(script_path_translated, orig_script_filename) != 0)) { if (orig_script_filename) { - _sapi_cgibin_putenv("ORIG_SCRIPT_FILENAME", orig_script_filename TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_FILENAME", orig_script_filename); } - script_path_translated = _sapi_cgibin_putenv("SCRIPT_FILENAME", script_path_translated TSRMLS_CC); + script_path_translated = CGI_PUTENV("SCRIPT_FILENAME", script_path_translated); } if (env_redirect_url) { if (orig_path_info) { - _sapi_cgibin_putenv("ORIG_PATH_INFO", orig_path_info TSRMLS_CC); - _sapi_cgibin_putenv("PATH_INFO", NULL TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_INFO", orig_path_info); + CGI_PUTENV("PATH_INFO", NULL); } if (orig_path_translated) { - _sapi_cgibin_putenv("ORIG_PATH_TRANSLATED", orig_path_translated TSRMLS_CC); - _sapi_cgibin_putenv("PATH_TRANSLATED", NULL TSRMLS_CC); + CGI_PUTENV("ORIG_PATH_TRANSLATED", orig_path_translated); + CGI_PUTENV("PATH_TRANSLATED", NULL); } } if (env_script_name != orig_script_name) { if (orig_script_name) { - _sapi_cgibin_putenv("ORIG_SCRIPT_NAME", orig_script_name TSRMLS_CC); + CGI_PUTENV("ORIG_SCRIPT_NAME", orig_script_name); } - SG(request_info).request_uri = _sapi_cgibin_putenv("SCRIPT_NAME", env_script_name TSRMLS_CC); + SG(request_info).request_uri = CGI_PUTENV("SCRIPT_NAME", env_script_name); } else { SG(request_info).request_uri = env_script_name; } @@ -1342,14 +1409,14 @@ SG(request_info).path_translated = estrdup(script_path_translated); } - SG(request_info).request_method = sapi_cgibin_getenv("REQUEST_METHOD", sizeof("REQUEST_METHOD")-1 TSRMLS_CC); + SG(request_info).request_method = CGI_GETENV("REQUEST_METHOD"); /* FIXME - Work out proto_num here */ - SG(request_info).query_string = sapi_cgibin_getenv("QUERY_STRING", sizeof("QUERY_STRING")-1 TSRMLS_CC); + SG(request_info).query_string = CGI_GETENV("QUERY_STRING"); SG(request_info).content_type = (content_type ? content_type : "" ); SG(request_info).content_length = (content_length ? atoi(content_length) : 0); /* The CGI RFC allows servers to pass on unvalidated Authorization data */ - auth = sapi_cgibin_getenv("HTTP_AUTHORIZATION", sizeof("HTTP_AUTHORIZATION")-1 TSRMLS_CC); + auth = CGI_GETENV("HTTP_AUTHORIZATION"); php_handle_auth_data(auth TSRMLS_CC); } } @@ -1867,6 +1934,13 @@ fastcgi = fcgi_is_fastcgi(); } if (fastcgi) { + /* Override SAPI callbacks */ + sapi_module.ub_write = sapi_fcgi_ub_write; + sapi_module.flush = sapi_fcgi_flush; + sapi_module.read_post = sapi_fcgi_read_post; + sapi_module.getenv = sapi_fcgi_getenv; + sapi_module.read_cookies = sapi_fcgi_read_cookies; + /* How many times to run PHP scripts before dying */ if (getenv("PHP_FCGI_MAX_REQUESTS")) { max_requests = atoi(getenv("PHP_FCGI_MAX_REQUESTS")); @@ -2028,7 +2102,7 @@ #endif while (!fastcgi || fcgi_accept_request(request) >= 0) { SG(server_context) = fastcgi ? (void *) request : (void *) 1; - init_request_info(TSRMLS_C); + init_request_info(request TSRMLS_CC); CG(interactive) = 0; if (!cgi && !fastcgi) { Modified: php/php-src/trunk/sapi/cgi/fastcgi.c =================================================================== --- php/php-src/trunk/sapi/cgi/fastcgi.c 2010-09-01 14:47:36 UTC (rev 302978) +++ php/php-src/trunk/sapi/cgi/fastcgi.c 2010-09-01 15:34:48 UTC (rev 302979) @@ -142,11 +142,12 @@ /* hash table */ -#define FCGI_HASH_TABLE_SIZE 64 +#define FCGI_HASH_TABLE_SIZE 128 #define FCGI_HASH_TABLE_MASK (FCGI_HASH_TABLE_SIZE - 1) +#define FCGI_HASH_SEG_SIZE 4096 typedef struct _fcgi_hash_bucket { - unsigned long hash_value; + unsigned int hash_value; unsigned int var_len; char *var; unsigned int val_len; @@ -175,16 +176,16 @@ fcgi_data_seg *data; } fcgi_hash; -static void fcgi_hash_init(fcgi_hash *h, unsigned int seg_size) +static void fcgi_hash_init(fcgi_hash *h) { memset(h->hash_table, 0, sizeof(h->hash_table)); h->list = NULL; h->buckets = (fcgi_hash_buckets*)malloc(sizeof(fcgi_hash_buckets)); h->buckets->idx = 0; h->buckets->next = NULL; - h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size); + h->data = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + FCGI_HASH_SEG_SIZE); h->data->pos = h->data->data; - h->data->end = h->data->pos + seg_size; + h->data->end = h->data->pos + FCGI_HASH_SEG_SIZE; h->data->next = NULL; } @@ -231,29 +232,31 @@ static inline char* fcgi_hash_strndup(fcgi_hash *h, char *str, unsigned int str_len) { + char *ret; + if (UNEXPECTED(h->data->pos + str_len + 1 >= h->data->end)) { - fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) + str_len); + unsigned int seg_size = (str_len + 1 > FCGI_HASH_SEG_SIZE) ? str_len + 1 : FCGI_HASH_SEG_SIZE; + fcgi_data_seg *p = (fcgi_data_seg*)malloc(sizeof(fcgi_data_seg) - 1 + seg_size); p->pos = p->data; - p->end = p->pos + str_len + 1; + p->end = p->pos + seg_size; p->next = h->data; h->data = p; } - memcpy(h->data->pos, str, str_len); - str = h->data->pos; - h->data->pos[str_len] = 0; + ret = h->data->pos; + memcpy(ret, str, str_len); + ret[str_len] = 0; h->data->pos += str_len + 1; - return str; + return ret; } -static char* fcgi_hash_set(fcgi_hash *h, char *var, unsigned int var_len, char *val, unsigned int val_len) +static char* fcgi_hash_set(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, char *val, unsigned int val_len) { - unsigned long hash_value = zend_inline_hash_func(var, var_len); unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; fcgi_hash_bucket *p = h->hash_table[idx]; - while (p != NULL) { - if (p->hash_value == hash_value && + while (UNEXPECTED(p != NULL)) { + if (UNEXPECTED(p->hash_value == hash_value) && p->var_len == var_len && memcmp(p->var, var, var_len) == 0) { @@ -272,21 +275,20 @@ } p = h->buckets->data + h->buckets->idx; h->buckets->idx++; + p->next = h->hash_table[idx]; + h->hash_table[idx] = p; + p->list_next = h->list; + h->list = p; p->hash_value = hash_value; p->var_len = var_len; p->var = fcgi_hash_strndup(h, var, var_len); p->val_len = val_len; p->val = fcgi_hash_strndup(h, val, val_len); - p->next = h->hash_table[idx]; - h->hash_table[idx] = p; - p->list_next = h->list; - h->list = p; return p->val; } -static void fcgi_hash_del(fcgi_hash *h, char *var, unsigned int var_len) +static void fcgi_hash_del(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len) { - unsigned long hash_value = zend_inline_hash_func(var, var_len); unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; fcgi_hash_bucket **p = &h->hash_table[idx]; @@ -304,9 +306,8 @@ } } -static char *fcgi_hash_get(fcgi_hash *h, char *var, unsigned int var_len, unsigned int *val_len) +static char *fcgi_hash_get(fcgi_hash *h, unsigned int hash_value, char *var, unsigned int var_len, unsigned int *val_len) { - unsigned long hash_value = zend_inline_hash_func(var, var_len); unsigned int idx = hash_value & FCGI_HASH_TABLE_MASK; fcgi_hash_bucket *p = h->hash_table[idx]; @@ -745,7 +746,7 @@ req->tcp = !GetNamedPipeInfo((HANDLE)_get_osfhandle(req->listen_socket), NULL, NULL, NULL, NULL); #endif - fcgi_hash_init(&req->env, 4096); + fcgi_hash_init(&req->env); return req; } @@ -858,7 +859,7 @@ ret = 0; break; } - fcgi_hash_set(&req->env, (char*)p, name_len, (char*)p + name_len, val_len); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC(p, name_len), (char*)p, name_len, (char*)p + name_len, val_len); p += name_len + val_len; } return ret; @@ -909,13 +910,13 @@ req->keep = (((fcgi_begin_request*)buf)->flags & FCGI_KEEP_CONN); switch ((((fcgi_begin_request*)buf)->roleB1 << 8) + ((fcgi_begin_request*)buf)->roleB0) { case FCGI_RESPONDER: - fcgi_hash_set(&req->env, "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "RESPONDER", sizeof("RESPONDER")-1); break; case FCGI_AUTHORIZER: - fcgi_hash_set(&req->env, "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "AUTHORIZER", sizeof("AUTHORIZER")-1); break; case FCGI_FILTER: - fcgi_hash_set(&req->env, "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1); + fcgi_hash_set(&req->env, FCGI_HASH_FUNC("FCGI_ROLE", sizeof("FCGI_ROLE")-1), "FCGI_ROLE", sizeof("FCGI_ROLE")-1, "FILTER", sizeof("FILTER")-1); break; default: return 0; @@ -1429,21 +1430,35 @@ if (!req) return NULL; - return fcgi_hash_get(&req->env, (char*)var, var_len, &val_len); + return fcgi_hash_get(&req->env, FCGI_HASH_FUNC(var, var_len), (char*)var, var_len, &val_len); } +char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value) +{ + unsigned int val_len; + + return fcgi_hash_get(&req->env, hash_value, (char*)var, var_len, &val_len); +} + char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val) { - if (var && req) { - if (val == NULL) { - fcgi_hash_del(&req->env, var, var_len); - } else { - return fcgi_hash_set(&req->env, var, var_len, val, strlen(val)); - } + if (!req) return NULL; + if (val == NULL) { + fcgi_hash_del(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len); + } else { + return fcgi_hash_set(&req->env, FCGI_HASH_FUNC(var, var_len), var, var_len, val, strlen(val)); } - return NULL; } +char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val) +{ + if (val == NULL) { + fcgi_hash_del(&req->env, hash_value, var, var_len); + } else { + return fcgi_hash_set(&req->env, hash_value, var, var_len, val, strlen(val)); + } +} + void fcgi_loadenv(fcgi_request *req, fcgi_apply_func func, zval *array TSRMLS_DC) { fcgi_hash_apply(&req->env, func, array TSRMLS_CC); Modified: php/php-src/trunk/sapi/cgi/fastcgi.h =================================================================== --- php/php-src/trunk/sapi/cgi/fastcgi.h 2010-09-01 14:47:36 UTC (rev 302978) +++ php/php-src/trunk/sapi/cgi/fastcgi.h 2010-09-01 15:34:48 UTC (rev 302979) @@ -26,6 +26,23 @@ #define FCGI_KEEP_CONN 1 +/* this is near the perfect hash function for most useful FastCGI variables + * which combines efficiency and minimal hash collisions + */ + +#define FCGI_HASH_FUNC(var, var_len) \ + (UNEXPECTED(var_len < 3) ? var_len : \ + (((unsigned int)var[3]) << 2) + \ + (((unsigned int)var[var_len-2]) << 4) + \ + (((unsigned int)var[var_len-1]) << 2) + \ + var_len) + +#define FCGI_GETENV(request, name) \ + fcgi_quick_getenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1)) + +#define FCGI_PUTENV(request, name, value) \ + fcgi_quick_putenv(request, name, sizeof(name)-1, FCGI_HASH_FUNC(name, sizeof(name)-1), value) + typedef enum _fcgi_role { FCGI_RESPONDER = 1, FCGI_AUTHORIZER = 2, @@ -108,6 +125,8 @@ char* fcgi_getenv(fcgi_request *req, const char* var, int var_len); char* fcgi_putenv(fcgi_request *req, char* var, int var_len, char* val); +char* fcgi_quick_getenv(fcgi_request *req, const char* var, int var_len, unsigned int hash_value); +char* fcgi_quick_putenv(fcgi_request *req, char* var, int var_len, unsigned int hash_value, char* val); void fcgi_loadenv(fcgi_request *req, fcgi_apply_func load_func, zval *array TSRMLS_DC); int fcgi_read(fcgi_request *req, char *str, int len);
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php