Skip to content

Instantly share code, notes, and snippets.

@errzey
Created May 9, 2011 16:34
Show Gist options
  • Save errzey/962839 to your computer and use it in GitHub Desktop.
Save errzey/962839 to your computer and use it in GitHub Desktop.
method mperf testing
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <time.h>
#include <inttypes.h>
enum evhttp_cmd_type {
EVHTTP_REQ_GET = 1 << 0,
EVHTTP_REQ_POST = 1 << 1,
EVHTTP_REQ_HEAD = 1 << 2,
EVHTTP_REQ_PUT = 1 << 3,
EVHTTP_REQ_DELETE = 1 << 4,
EVHTTP_REQ_OPTIONS = 1 << 5,
EVHTTP_REQ_TRACE = 1 << 6,
EVHTTP_REQ_CONNECT = 1 << 7,
EVHTTP_REQ_PATCH = 1 << 8
};
int
evhttp_parse_request_line(char * line) {
char * method;
char * uri;
char * ver;
int type;
/* Parse the request line */
method = strsep(&line, " ");
if (line == NULL) {
return -1;
}
uri = strsep(&line, " ");
if (line == NULL) {
return -1;
}
ver = strsep(&line, " ");
if (line != NULL) {
return -1;
}
/* First line */
if (strcmp(method, "GET") == 0) {
type = EVHTTP_REQ_GET;
} else if (strcmp(method, "POST") == 0) {
type = EVHTTP_REQ_POST;
} else if (strcmp(method, "HEAD") == 0) {
type = EVHTTP_REQ_HEAD;
} else if (strcmp(method, "PUT") == 0) {
type = EVHTTP_REQ_PUT;
} else if (strcmp(method, "DELETE") == 0) {
type = EVHTTP_REQ_DELETE;
} else if (strcmp(method, "OPTIONS") == 0) {
type = EVHTTP_REQ_OPTIONS;
} else if (strcmp(method, "TRACE") == 0) {
type = EVHTTP_REQ_TRACE;
} else if (strcmp(method, "PATCH") == 0) {
type = EVHTTP_REQ_PATCH;
} else {
type = -1;
}
return type;
} /* evhttp_parse_request_line */
int
optimized_parse_request_line_v2(char * line) {
char * method;
char * uri;
char * ver;
size_t mlen;
uint32_t u32;
uint64_t u64;
int type = -1;
method = strsep(&line, " ");
if (!line) {
return -1;
}
uri = strsep(&line, " ");
if (!line) {
return -1;
}
ver = strsep(&line, " ");
if (line) {
return -1;
}
mlen = (uri - method) - 1;
switch (mlen) {
case 3:
u32 = *((uint32_t *)method);
/* GET\0 == 0x00544547 */
/* PUT\0 == 0x00545550 */
if (u32 == 0x00544547) {
type = EVHTTP_REQ_GET;
} else if (u32 == 0x00545550) {
type = EVHTTP_REQ_PUT;
}
break;
case 4:
u32 = *((uint32_t *)method);
/* POST == 0x54534F50 */
/* HEAD == 0x44414548 */
if (u32 == 0x54534F50) {
type = EVHTTP_REQ_POST;
} else if (u32 == 0x44414548) {
type = EVHTTP_REQ_HEAD;
}
break;
case 5:
/* PATCH\0 == 0x4843544150 */
/* TRACE\0 == 0x4543415254 */
u64 = *((uint64_t *)method);
if ((u64 & 0xFFFFFFFFFFFF) == 0x4843544150) {
type = EVHTTP_REQ_PATCH;
} else if ((u64 & 0xFFFFFFFFFFFF) == 0x4543415254) {
type = EVHTTP_REQ_TRACE;
}
break;
case 6:
/* DELETE\0 == 0x4554454C4544 */
u64 = *((uint64_t *)method);
if ((u64 & 0xFFFFFFFFFFFFFF) == 0x4554454C4544) {
type = EVHTTP_REQ_DELETE;
}
break;
case 7:
/* OPTIONS\0 == 0x534E4F4954504F */
/* CONNECT\0 == 0x5443454E4E4F43 */
u64 = *((uint64_t *)method);
if ((u64 & 0xFFFFFFFFFFFFFFFF) == 0x534E4F4954504F) {
type = EVHTTP_REQ_OPTIONS;
} else if ((u64 & 0xFFFFFFFFFFFFFFFF) == 0x5443454E4E4F43) {
type = EVHTTP_REQ_CONNECT;
}
break;
} /* switch */
return type;
} /* optimized_parse_request_line_v2 */
int
optimized_parse_request_line_v1_2(char * line) {
char * method;
char * uri;
char * ver;
size_t mlen;
int type = -1;
method = strsep(&line, " ");
if (!line) {
return -1;
}
uri = strsep(&line, " ");
if (!line) {
return -1;
}
ver = strsep(&line, " ");
if (line) {
return -1;
}
mlen = (uri - method) - 1;
switch (mlen) {
case 3:
switch (*method) {
case 'G':
if (!strncmp(method, "GET", 3)) {
type = EVHTTP_REQ_GET;
}
break;
case 'P':
if (!strncmp(method, "PUT", 3)) {
type = EVHTTP_REQ_PUT;
}
break;
default:
break;
}
break;
case 4:
switch (*method) {
case 'P':
if (!strncmp(method, "POST", 4)) {
type = EVHTTP_REQ_POST;
}
break;
case 'H':
if (!strncmp(method, "HEAD", 4)) {
type = EVHTTP_REQ_HEAD;
}
break;
default:
break;
}
case 5:
switch (*method) {
case 'P':
if (!strncmp(method, "PATCH", 5)) {
type = EVHTTP_REQ_PATCH;
}
break;
case 'T':
if (!strncmp(method, "TRACE", 5)) {
type = EVHTTP_REQ_TRACE;
}
break;
default:
break;
}
break;
case 6:
if (!strncmp(method, "DELETE", 6)) {
type = EVHTTP_REQ_DELETE;
}
break;
case 7:
switch (*method) {
case 'O':
if (!strncmp(method, "OPTIONS", 7)) {
type = EVHTTP_REQ_OPTIONS;
}
break;
case 'C':
if (!strncmp(method, "CONNECT", 7)) {
type = EVHTTP_REQ_CONNECT;
}
break;
default:
break;
}
break;
} /* switch */
return type;
} /* optimized_parse_request_line_v1_2 */
int
optimized_parse_request_line_v1_1(char * line) {
char * method;
char * uri;
char * ver;
size_t mlen;
int type = -1;
method = strsep(&line, " ");
if (!line) {
return -1;
}
uri = strsep(&line, " ");
if (!line) {
return -1;
}
ver = strsep(&line, " ");
if (line) {
return -1;
}
mlen = (uri - method) - 1;
switch (mlen) {
case 3:
switch (*method) {
case 'G':
if (!memcmp(method, "GET", 3)) {
type = EVHTTP_REQ_GET;
}
break;
case 'P':
if (!memcmp(method, "PUT", 3)) {
type = EVHTTP_REQ_PUT;
}
break;
default:
break;
}
break;
case 4:
switch (*method) {
case 'P':
if (!memcmp(method, "POST", 4)) {
type = EVHTTP_REQ_POST;
}
break;
case 'H':
if (!memcmp(method, "HEAD", 4)) {
type = EVHTTP_REQ_HEAD;
}
break;
default:
break;
}
case 5:
switch (*method) {
case 'P':
if (!memcmp(method, "PATCH", 5)) {
type = EVHTTP_REQ_PATCH;
}
break;
case 'T':
if (!memcmp(method, "TRACE", 5)) {
type = EVHTTP_REQ_TRACE;
}
break;
default:
break;
}
break;
case 6:
if (!memcmp(method, "DELETE", 6)) {
type = EVHTTP_REQ_DELETE;
}
break;
case 7:
switch (*method) {
case 'O':
if (!memcmp(method, "OPTIONS", 7)) {
type = EVHTTP_REQ_OPTIONS;
}
break;
case 'C':
if (!memcmp(method, "CONNECT", 7)) {
type = EVHTTP_REQ_CONNECT;
}
break;
default:
break;
}
break;
} /* switch */
return type;
} /* optimized_parse_request_line_v1_1 */
/* Using lookup tables (switch/case), along with using the length of the method
* to get to the right table entry quicker. */
int
optimized_parse_request_line_v1(char * line) {
char * method;
char * uri;
char * ver;
size_t mlen;
int type = -1;
method = strsep(&line, " ");
if (!line) {
return -1;
}
uri = strsep(&line, " ");
if (!line) {
return -1;
}
ver = strsep(&line, " ");
if (line) {
return -1;
}
mlen = (uri - method) - 1;
switch (mlen) {
case 3:
/* The length of the method string is 3, meaning it can only be one of two methods: GET or PUT */
/* Since both GET and PUT share the same character 'T' at the end,
* if the string doesn't have 'T', we can immediately determine this
* is an invalid HTTP method */
if (method[2] != 'T') {
break;
}
switch (*method) {
case 'G':
/* This first byte is 'G', so make sure the next byte is
* 'E', if it isn't then this isn't a valid method */
if (method[1] == 'E') {
type = EVHTTP_REQ_GET;
}
break;
case 'P':
/* First byte is P, check second byte for 'U', if not,
* we know it's an invalid method */
if (method[1] == 'U') {
type = EVHTTP_REQ_PUT;
}
break;
default:
break;
}
break;
case 4:
/* The method length is 4 bytes, leaving only the methods "POST" and
* "HEAD" */
switch (*method) {
case 'P':
if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
type = EVHTTP_REQ_POST;
}
break;
case 'H':
if (method[3] == 'D' && method[2] == 'A' && method[1] == 'E') {
type = EVHTTP_REQ_HEAD;
}
break;
default:
break;
}
case 5:
/* Method length is 5 bytes, which can only encompass PATCH and
* TRACE */
switch (*method) {
case 'P':
if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
type = EVHTTP_REQ_PATCH;
}
break;
case 'T':
if (method[4] == 'E' && method[3] == 'C' && method[2] == 'A' && method[1] == 'R') {
type = EVHTTP_REQ_TRACE;
}
break;
default:
break;
}
break;
case 6:
/* Method length is 6, only valid method 6 bytes in length is DELEte
*/
/* If the first byte isn't 'D' then it's invalid */
if (*method != 'D') {
break;
}
if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
type = EVHTTP_REQ_DELETE;
}
break;
case 7:
/* Method length is 7, only valid methods are "OPTIONS" and
* "CONNECT" */
switch (*method) {
case 'O':
if (method[6] == 'S' && method[5] == 'N' && method[4] == 'O' &&
method[3] == 'I' && method[2] == 'T' && method[1] == 'P') {
type = EVHTTP_REQ_OPTIONS;
}
break;
case 'C':
/* T C E N N O C */
if (method[6] == 'T' && method[5] == 'C' && method[4] == 'E' &&
method[3] == 'N' && method[2] == 'N' && method[1] == 'O') {
type = EVHTTP_REQ_CONNECT;
}
break;
default:
break;
}
break;
} /* switch */
return type;
} /* optimized_parse_request_line_v1 */
#define LOOPS 800000
int
main(int argc, char ** argv) {
int i;
if (argc <= 1) {
printf("Usage %s \"request line\"\n", argv[0]);
exit(1);
}
for (i = 0; i < LOOPS; i++) {
char * line = strdup(argv[1]);
evhttp_parse_request_line(line);
free(line);
}
for (i = 0; i < LOOPS; i++) {
char * line = strdup(argv[1]);
optimized_parse_request_line_v1(line);
free(line);
}
for (i = 0; i < LOOPS; i++) {
char * line = strdup(argv[1]);
optimized_parse_request_line_v1_1(line);
free(line);
}
for (i = 0; i < LOOPS; i++) {
char * line = strdup(argv[1]);
optimized_parse_request_line_v1_2(line);
free(line);
}
for (i = 0; i < LOOPS; i++) {
char * line = strdup(argv[1]);
optimized_parse_request_line_v2(line);
free(line);
}
return 0;
} /* main */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment