Fast buffers for http-parser
This commit is contained in:
parent
5506f99dfa
commit
5f935f6c14
@ -68,6 +68,15 @@ static Persistent<String> upgrade_sym;
|
||||
|
||||
static struct http_parser_settings settings;
|
||||
|
||||
|
||||
// This is a hack to get the current_buffer to the callbacks with the least
|
||||
// amount of overhead. Nothing else will run while http_parser_execute()
|
||||
// runs, therefore this pointer can be set and used for the execution.
|
||||
static Local<Value>* current_buffer;
|
||||
static char* current_buffer_data;
|
||||
static size_t current_buffer_len;
|
||||
|
||||
|
||||
// Callback prototype for http_cb
|
||||
#define DEFINE_HTTP_CB(name) \
|
||||
static int name(http_parser *p) { \
|
||||
@ -88,16 +97,16 @@ static struct http_parser_settings settings;
|
||||
#define DEFINE_HTTP_DATA_CB(name) \
|
||||
static int name(http_parser *p, const char *at, size_t length) { \
|
||||
Parser *parser = static_cast<Parser*>(p->data); \
|
||||
assert(parser->buffer_); \
|
||||
assert(current_buffer); \
|
||||
Local<Value> cb_value = parser->handle_->Get(name##_sym); \
|
||||
if (!cb_value->IsFunction()) return 0; \
|
||||
Local<Function> cb = Local<Function>::Cast(cb_value); \
|
||||
Local<Value> argv[3] = { Local<Value>::New(parser->buffer_->handle_) \
|
||||
, Integer::New(at - parser->buffer_->data()) \
|
||||
Local<Value> argv[3] = { *current_buffer \
|
||||
, Integer::New(at - current_buffer_data) \
|
||||
, Integer::New(length) \
|
||||
}; \
|
||||
Local<Value> ret = cb->Call(parser->handle_, 3, argv); \
|
||||
assert(parser->buffer_); \
|
||||
assert(current_buffer); \
|
||||
if (ret.IsEmpty()) { \
|
||||
parser->got_exception_ = true; \
|
||||
return -1; \
|
||||
@ -137,12 +146,10 @@ method_to_str(unsigned short m) {
|
||||
class Parser : public ObjectWrap {
|
||||
public:
|
||||
Parser(enum http_parser_type type) : ObjectWrap() {
|
||||
buffer_ = NULL;
|
||||
Init(type);
|
||||
}
|
||||
|
||||
~Parser() {
|
||||
assert(buffer_ == NULL && "Destroying a parser while it's parsing");
|
||||
}
|
||||
|
||||
DEFINE_HTTP_CB(on_message_begin)
|
||||
@ -214,7 +221,7 @@ class Parser : public ObjectWrap {
|
||||
}
|
||||
|
||||
parser->Wrap(args.This());
|
||||
assert(!parser->buffer_);
|
||||
assert(!current_buffer);
|
||||
|
||||
return args.This();
|
||||
}
|
||||
@ -225,41 +232,50 @@ class Parser : public ObjectWrap {
|
||||
|
||||
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
|
||||
|
||||
assert(!parser->buffer_);
|
||||
if (parser->buffer_) {
|
||||
assert(!current_buffer);
|
||||
assert(!current_buffer_data);
|
||||
|
||||
if (current_buffer) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("Already parsing a buffer")));
|
||||
}
|
||||
|
||||
if (!Buffer::HasInstance(args[0])) {
|
||||
Local<Value> buffer_v = args[0];
|
||||
|
||||
if (!Buffer::HasInstance(buffer_v)) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("Argument should be a buffer")));
|
||||
}
|
||||
|
||||
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
|
||||
Local<Object> buffer_obj = buffer_v->ToObject();
|
||||
char *buffer_data = Buffer::Data(buffer_obj);
|
||||
size_t buffer_len = Buffer::Length(buffer_obj);
|
||||
|
||||
size_t off = args[1]->Int32Value();
|
||||
if (off >= buffer->length()) {
|
||||
if (off >= buffer_len) {
|
||||
return ThrowException(Exception::Error(
|
||||
String::New("Offset is out of bounds")));
|
||||
}
|
||||
|
||||
size_t len = args[2]->Int32Value();
|
||||
if (off+len > buffer->length()) {
|
||||
if (off+len > buffer_len) {
|
||||
return ThrowException(Exception::Error(
|
||||
String::New("Length is extends beyond buffer")));
|
||||
}
|
||||
|
||||
// Assign 'buffer_' while we parse. The callbacks will access that varible.
|
||||
parser->buffer_ = buffer;
|
||||
current_buffer = &buffer_v;
|
||||
current_buffer_data = buffer_data;
|
||||
current_buffer_len = buffer_len;
|
||||
parser->got_exception_ = false;
|
||||
|
||||
size_t nparsed =
|
||||
http_parser_execute(&parser->parser_, &settings, buffer->data()+off, len);
|
||||
http_parser_execute(&parser->parser_, &settings, buffer_data + off, len);
|
||||
|
||||
// Unassign the 'buffer_' variable
|
||||
assert(parser->buffer_);
|
||||
parser->buffer_ = NULL;
|
||||
assert(current_buffer);
|
||||
current_buffer = NULL;
|
||||
current_buffer_data = NULL;
|
||||
|
||||
// If there was an exception in one of the callbacks
|
||||
if (parser->got_exception_) return Local<Value>();
|
||||
@ -282,7 +298,7 @@ class Parser : public ObjectWrap {
|
||||
|
||||
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
|
||||
|
||||
assert(!parser->buffer_);
|
||||
assert(!current_buffer);
|
||||
parser->got_exception_ = false;
|
||||
|
||||
http_parser_execute(&(parser->parser_), &settings, NULL, 0);
|
||||
@ -313,13 +329,10 @@ class Parser : public ObjectWrap {
|
||||
private:
|
||||
|
||||
void Init (enum http_parser_type type) {
|
||||
assert(buffer_ == NULL); // don't call this during Execute()
|
||||
http_parser_init(&parser_, type);
|
||||
|
||||
parser_.data = this;
|
||||
}
|
||||
|
||||
Buffer * buffer_; // The buffer currently being parsed.
|
||||
bool got_exception_;
|
||||
http_parser parser_;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user