Skip to content

Commit

Permalink
Merge pull request WiseLibs#31 from JoshuaWise/efficient-string-join
Browse files Browse the repository at this point in the history
implemented a more efficient string join
  • Loading branch information
JoshuaWise authored May 15, 2017
2 parents 95411ec + 5d3114c commit 9308439
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 109 deletions.
6 changes: 4 additions & 2 deletions src/binder/bind-object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
int Binder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
int len = bindMap->length;
BindPair* pairs = bindMap->pairs;
v8::Isolate* isolate = v8::Isolate::GetCurrent();

for (int i=0; i<len; ++i) {
v8::Local<v8::String> key = Nan::New(pairs[i].name).ToLocalChecked();
v8::Local<v8::String> key = v8::Local<v8::String>::New(isolate, pairs[i].name);

// Check if the named parameter was provided.
v8::Maybe<bool> has_property = Nan::HasOwnProperty(obj, key);
Expand All @@ -18,7 +19,8 @@ int Binder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
return i;
}
if (!has_property.FromJust()) {
CONCAT3(message, "Missing named parameter \"", pairs[i].name, "\"");
v8::String::Utf8Value param_name(key);
CONCAT3(message, "Missing named parameter \"", *param_name, "\"");
error = COPY(message.c_str());
return i;
}
Expand Down
6 changes: 4 additions & 2 deletions src/multi-binder/bind-object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
int MultiBinder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
int len = bindMap->length;
BindPair* pairs = bindMap->pairs;
v8::Isolate* isolate = v8::Isolate::GetCurrent();

// Save current handle.
sqlite3_stmt* current_handle = handle;

for (int i=0; i<len; ++i) {
v8::Local<v8::String> key = Nan::New(pairs[i].name).ToLocalChecked();
v8::Local<v8::String> key = v8::Local<v8::String>::New(isolate, pairs[i].name);

// Check if the named parameter was provided.
v8::Maybe<bool> has_property = Nan::HasOwnProperty(obj, key);
Expand All @@ -22,7 +23,8 @@ int MultiBinder::BindObject(v8::Local<v8::Object> obj, BindMap* bindMap) {
return i;
}
if (!has_property.FromJust()) {
CONCAT3(message, "Missing named parameter \"", pairs[i].name, "\"");
v8::String::Utf8Value param_name(key);
CONCAT3(message, "Missing named parameter \"", *param_name, "\"");
error = COPY(message.c_str());
return i;
}
Expand Down
2 changes: 1 addition & 1 deletion src/objects/database/create-statement.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ NAN_METHOD(Database::CreateStatement) {

// Builds actual sqlite3_stmt handle.
const void* tail;
int status = sqlite3_prepare16_v2(db->db_handle, *utf16, utf16.length() * sizeof (uint16_t) + 1, &stmt->st_handle, &tail);
int status = sqlite3_prepare16_v2(db->db_handle, *utf16, utf16.length() * sizeof(uint16_t) + 1, &stmt->st_handle, &tail);

// Validates the newly created statement.
if (status != SQLITE_OK) {
Expand Down
39 changes: 17 additions & 22 deletions src/objects/database/create-transaction.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// .transaction(Array sqls) -> Transaction

NAN_METHOD(Database::CreateTransaction) {
GET_ISOLATE();
REQUIRE_ARGUMENT_ARRAY(0, sources);

Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
Expand All @@ -21,10 +22,9 @@ NAN_METHOD(Database::CreateTransaction) {
if (len > max_transaction_length) {
return Nan::ThrowRangeError("Too many SQL statements were provided");
}
v8::Local<v8::Array> digestedSources = Nan::New<v8::Array>(len);

// Validate and digest source strings.
v8::Local<v8::String> semicolon = Nan::New(";").ToLocalChecked();
// Get the values of each source string.
SQLRope rope(len);
for (unsigned int i=0; i<len; ++i) {
Nan::MaybeLocal<v8::Value> maybeValue = Nan::Get(sources, i);
if (maybeValue.IsEmpty()) {
Expand All @@ -34,21 +34,14 @@ NAN_METHOD(Database::CreateTransaction) {
if (!value->IsString()) {
return Nan::ThrowTypeError("Expected each item in the given array to be a string");
}
v8::Local<v8::String> source = v8::Local<v8::String>::Cast(value);
v8::String::Value utf16(source);
uint16_t last_char = (*utf16)[utf16.length() - 1];
if (last_char != 0x3b) {
source = v8::String::Concat(source, semicolon);
}
Nan::Set(digestedSources, i, source);
rope.Push(v8::Local<v8::String>::Cast(value));
}

// Create joined source string.
v8::Local<v8::Value> joinArgs[1] = {Nan::New("\n").ToLocalChecked()};
INVOKE_METHOD(joinedSource, digestedSources, "join", 1, joinArgs);
if (!joinedSource->IsString()) {
return Nan::ThrowTypeError("Expected Array.prototype.join to return a string");
}
// Join the source strings together.
size_t joined_length = rope.GetConcatLength();
const uint16_t* joined_sources = rope.Concat(joined_length);
v8::Local<v8::String> joinedSources = StringFromUtf16(isolate, joined_sources, joined_length);
delete[] joined_sources;


CONSTRUCTING_PRIVILEGES = true;
Expand All @@ -57,18 +50,20 @@ NAN_METHOD(Database::CreateTransaction) {
CONSTRUCTING_PRIVILEGES = false;
Transaction* trans = Nan::ObjectWrap::Unwrap<Transaction>(transaction);


// Initializes C++ object properties.
trans->db = db;
trans->handle_count = len;
trans->handles = new sqlite3_stmt* [len]();

// Create statement handles from each source string.
for (unsigned int i=0; i<len; ++i) {
v8::Local<v8::String> source = v8::Local<v8::String>::Cast(Nan::Get(digestedSources, i).ToLocalChecked());
v8::String::Value utf16(source);
const void* tail;
v8::String::Value* source = rope.Get(i);
int utf16_length = source->length();
const uint16_t* utf16 = **source;

int status = sqlite3_prepare16_v2(db->db_handle, *utf16, utf16.length() * sizeof (uint16_t) + 1, &(trans->handles[i]), &tail);
const void* tail;
int status = sqlite3_prepare16_v2(db->db_handle, utf16, utf16_length * sizeof(uint16_t) + 1, &(trans->handles[i]), &tail);

// Validates the newly created statement.
if (status != SQLITE_OK) {
Expand All @@ -78,14 +73,14 @@ NAN_METHOD(Database::CreateTransaction) {
if (trans->handles[i] == NULL) {
return Nan::ThrowTypeError("One of the supplied SQL strings contains no statements");
}
if (tail != (const void*)(*utf16 + utf16.length())) {
if (tail != (const void*)(utf16 + utf16_length)) {
return Nan::ThrowRangeError("Each provided string may only contain a single SQL statement");
}
if (sqlite3_stmt_readonly(trans->handles[i])) {
return Nan::ThrowTypeError("Transactions cannot contain read-only statements");
}
}
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("source"), joinedSource, FROZEN);
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("source"), joinedSources, FROZEN);
Nan::ForceSet(transaction, NEW_INTERNAL_STRING_FAST("database"), info.This(), FROZEN);
if (db->safe_ints) {trans->state |= SAFE_INTS;}

Expand Down
8 changes: 4 additions & 4 deletions src/objects/database/database.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../../util/transaction-handles.h"
#include "../../util/bind-map.h"
#include "../../util/functor.h"
#include "../../util/sql-rope.h"

const int max_buffer_size = node::Buffer::kMaxLength > 0x7fffffffU ? 0x7fffffff : static_cast<int>(node::Buffer::kMaxLength);
const int max_string_size = v8::String::kMaxLength;
Expand Down Expand Up @@ -55,7 +56,7 @@ void Database::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)

v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(Nan::New("Database").ToLocalChecked());
t->SetClassName(NEW_INTERNAL_STRING_FAST("Database"));

Nan::SetPrototypeMethod(t, "close", Close);
Nan::SetPrototypeMethod(t, "prepare", CreateStatement);
Expand All @@ -65,10 +66,9 @@ void Database::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module)
Nan::SetPrototypeMethod(t, "pragma", Pragma);
Nan::SetPrototypeMethod(t, "checkpoint", Checkpoint);
Nan::SetPrototypeMethod(t, "defaultSafeIntegers", DefaultSafeIntegers);
Nan::SetAccessor(t->InstanceTemplate(), Nan::New("open").ToLocalChecked(), Open);
Nan::SetAccessor(t->InstanceTemplate(), NEW_INTERNAL_STRING_FAST("open"), Open);

Nan::Set(exports, Nan::New("Database").ToLocalChecked(),
Nan::GetFunction(t).ToLocalChecked());
Nan::Set(exports, NEW_INTERNAL_STRING_FAST("Database"), Nan::GetFunction(t).ToLocalChecked());
}

// Returns an SQLite3 result code.
Expand Down
11 changes: 7 additions & 4 deletions src/objects/database/pragma.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@

typedef struct PragmaInfo {
v8::Local<v8::Value> rows;
v8::Isolate* isolate;
bool simple;
bool after_first;
} PragmaInfo;

int PragmaCallback(void* x, int column_count, char** results, char** column_names) {
PragmaInfo* pragma_info = static_cast<PragmaInfo*>(x);
v8::Isolate* isolate = pragma_info->isolate;

if (pragma_info->simple) {
if (!pragma_info->after_first) {
pragma_info->after_first = true;
pragma_info->rows = Nan::New(results[0]).ToLocalChecked();
pragma_info->rows = StringFromUtf8(isolate, results[0], -1);
}
} else {
v8::Local<v8::Object> row = Nan::New<v8::Object>();
for (int i=0; i<column_count; ++i) {
Nan::Set(row, Nan::New(column_names[i]).ToLocalChecked(), Nan::New(results[i]).ToLocalChecked());
Nan::Set(row, StringFromUtf8(isolate, column_names[i], -1), StringFromUtf8(isolate, results[i], -1));
}
v8::Local<v8::Array> rows = v8::Local<v8::Array>::Cast(pragma_info->rows);
Nan::Set(rows, rows->Length(), row);
Expand All @@ -27,6 +29,7 @@ int PragmaCallback(void* x, int column_count, char** results, char** column_name
}

NAN_METHOD(Database::Pragma) {
GET_ISOLATE();
REQUIRE_ARGUMENT_STRING(0, source);
TRUTHINESS_OF_ARGUMENT(1, simple_result);
Database* db = Nan::ObjectWrap::Unwrap<Database>(info.This());
Expand All @@ -38,12 +41,12 @@ NAN_METHOD(Database::Pragma) {
}

// Prepares the SQL string.
v8::Local<v8::String> sql = v8::String::Concat(Nan::New("PRAGMA ").ToLocalChecked(), source);
v8::Local<v8::String> sql = v8::String::Concat(StringFromLatin1(isolate, "PRAGMA ", -1), source);
v8::String::Utf8Value utf8(sql);
char* err;

// Executes the SQL on the database handle.
PragmaInfo pragma_info = {Nan::New<v8::Array>(), simple_result, false};
PragmaInfo pragma_info = {Nan::New<v8::Array>(), isolate, simple_result, false};
sqlite3_exec(db->db_handle, *utf8, PragmaCallback, &pragma_info, &err);
if (err != NULL) {
db->ThrowError(err);
Expand Down
15 changes: 7 additions & 8 deletions src/objects/int64/int64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@ void Int64::Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {

v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(Nan::New("Int64").ToLocalChecked());
t->SetClassName(NEW_INTERNAL_STRING_FAST("Int64"));

Nan::SetAccessor(t->InstanceTemplate(), Nan::New("low").ToLocalChecked(), Low);
Nan::SetAccessor(t->InstanceTemplate(), Nan::New("high").ToLocalChecked(), High);
Nan::SetAccessor(t->InstanceTemplate(), NEW_INTERNAL_STRING_FAST("low"), Low);
Nan::SetAccessor(t->InstanceTemplate(), NEW_INTERNAL_STRING_FAST("high"), High);
Nan::SetPrototypeMethod(t, "toString", ToString);
Nan::SetPrototypeMethod(t, "valueOf", ValueOf);

constructor.Reset(Nan::GetFunction(t).ToLocalChecked());
constructorTemplate.Reset(t);

Nan::Set(exports, Nan::New("Int64").ToLocalChecked(),
Nan::GetFunction(t).ToLocalChecked());
Nan::Set(exports, NEW_INTERNAL_STRING_FAST("Int64"), Nan::GetFunction(t).ToLocalChecked());
}
sqlite3_int64* Int64::FastConstructInt = NULL;
CONSTRUCTOR(Int64::constructor);
Expand Down Expand Up @@ -75,9 +74,9 @@ NAN_GETTER(Int64::High) {
);
}
NAN_METHOD(Int64::ToString) {
info.GetReturnValue().Set(Nan::New(
std::to_string(static_cast<long long>(Nan::ObjectWrap::Unwrap<Int64>(info.This())->full)).c_str()
).ToLocalChecked());
GET_ISOLATE();
std::string string = std::to_string(static_cast<long long>(Nan::ObjectWrap::Unwrap<Int64>(info.This())->full));
info.GetReturnValue().Set(StringFromLatin1(isolate, string.c_str(), string.length()));
}
NAN_METHOD(Int64::ValueOf) {
Int64* int64 = Nan::ObjectWrap::Unwrap<Int64>(info.This());
Expand Down
2 changes: 1 addition & 1 deletion src/objects/statement/each.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// .each(...any boundValues, function callback) -> undefined

NAN_METHOD(Statement::Each) {
GET_ISOLATE();
Statement* stmt = Nan::ObjectWrap::Unwrap<Statement>(info.This());
if (!(stmt->state & RETURNS_DATA)) {
return Nan::ThrowTypeError("This statement does not return data. Use run() instead");
Expand All @@ -9,7 +10,6 @@ NAN_METHOD(Statement::Each) {
QUERY_START(stmt, statement, STATEMENT_BIND, info, func_index);
const bool safe_integers = (stmt->state & SAFE_INTS) != 0;
const bool pluck = (stmt->state & PLUCK_COLUMN) != 0;
v8::Isolate* const isolate = info.GetIsolate();

stmt->db->busy = true;

Expand Down
5 changes: 2 additions & 3 deletions src/objects/statement/statement.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#include <set>
#include <string>
#include <sqlite3.h>
#include <nan.h>
#include "statement.h"
Expand Down Expand Up @@ -32,9 +31,9 @@ void Statement::Init() {

v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(Nan::New("Statement").ToLocalChecked());
t->SetClassName(NEW_INTERNAL_STRING_FAST("Statement"));

Nan::SetAccessor(t->InstanceTemplate(), Nan::New("returnsData").ToLocalChecked(), ReturnsData);
Nan::SetAccessor(t->InstanceTemplate(), NEW_INTERNAL_STRING_FAST("returnsData"), ReturnsData);
Nan::SetPrototypeMethod(t, "safeIntegers", SafeIntegers);
Nan::SetPrototypeMethod(t, "bind", Bind);
Nan::SetPrototypeMethod(t, "pluck", Pluck);
Expand Down
5 changes: 3 additions & 2 deletions src/objects/statement/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ BindMap* Statement::GetBindMap() {
int param_count = sqlite3_bind_parameter_count(st_handle);
int capacity = 0;
BindMap* bind_map = &extras->bind_map;
v8::Isolate* isolate = v8::Isolate::GetCurrent();

for (int i=1; i<=param_count; ++i) {
const char* name = sqlite3_bind_parameter_name(st_handle, i);
if (name != NULL) {
if (bind_map->length == capacity) {
bind_map->Grow(&capacity);
bind_map->Grow(isolate, &capacity);
}
bind_map->Add(std::string(name + 1), i);
bind_map->Add(isolate, name + 1, i);
}
}
state |= HAS_BIND_MAP;
Expand Down
2 changes: 1 addition & 1 deletion src/objects/transaction/transaction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void Transaction::Init() {

v8::Local<v8::FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(New);
t->InstanceTemplate()->SetInternalFieldCount(1);
t->SetClassName(Nan::New("Transaction").ToLocalChecked());
t->SetClassName(NEW_INTERNAL_STRING_FAST("Transaction"));

Nan::SetPrototypeMethod(t, "safeIntegers", SafeIntegers);
Nan::SetPrototypeMethod(t, "bind", Bind);
Expand Down
5 changes: 3 additions & 2 deletions src/objects/transaction/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ BindMap* Transaction::GetBindMap() {
if (!(state & HAS_BIND_MAP)) {
int capacity = 0;
BindMap* bind_map = &extras->bind_map;
v8::Isolate* isolate = v8::Isolate::GetCurrent();

for (unsigned int h=0; h<handle_count; ++h) {
sqlite3_stmt* handle = handles[h];
Expand All @@ -14,9 +15,9 @@ BindMap* Transaction::GetBindMap() {
const char* name = sqlite3_bind_parameter_name(handle, i);
if (name != NULL) {
if (bind_map->length == capacity) {
bind_map->Grow(&capacity);
bind_map->Grow(isolate, &capacity);
}
bind_map->Add(std::string(name + 1), i, h);
bind_map->Add(isolate, name + 1, i, h);
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions src/util/bind-map.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
#ifndef BETTER_SQLITE3_BIND_MAP_H
#define BETTER_SQLITE3_BIND_MAP_H

#include <string>
#include "./macros.h"

const int parameter_bits = 10;
const int parameter_mask = (1 << parameter_bits) - 1;
const int max_transaction_length = (1 << ((sizeof(int) * 8) - parameter_bits)) - 1;

typedef struct BindPair {
std::string name;
class BindPair { public:
~BindPair() {name.Reset();}
v8::Persistent<v8::String> name;
int index;
} BindPair;
};

class BindMap { public:
explicit BindMap() : pairs(NULL), length(0) {}
Expand All @@ -21,21 +22,20 @@ class BindMap { public:
static inline int GetTransactionIndex(int index) {
return index >> parameter_bits;
}
inline void Add(std::string name, int index) {
pairs[length].name = name;
inline void Add(v8::Isolate* isolate, const char* name, int index) {
pairs[length].name.Reset(isolate, InternalizedFromUtf8(isolate, name, -1));
pairs[length].index = index;
length += 1;
}
inline void Add(std::string name, int parameter_index, int transaction_index) {
pairs[length].name = name;
pairs[length].index = parameter_index | (transaction_index << parameter_bits);
length += 1;
inline void Add(v8::Isolate* isolate, const char* name, int parameter_index, int transaction_index) {
Add(isolate, name, parameter_index | (transaction_index << parameter_bits));
}
void Grow(int* capacity) {
void Grow(v8::Isolate* isolate, int* capacity) {
int new_capacity = (*capacity << 1) | 2;
BindPair* new_pairs = new BindPair[new_capacity];
for (int i=0; i<length; ++i) {
new_pairs[i] = pairs[i];
new_pairs[i].name.Reset(isolate, pairs[i].name);
new_pairs[i].index = pairs[i].index;
}
delete[] pairs;
pairs = new_pairs;
Expand Down
Loading

0 comments on commit 9308439

Please sign in to comment.