Skip to content

Commit

Permalink
wip: porting to N-API
Browse files Browse the repository at this point in the history
  • Loading branch information
tuananh committed Mar 21, 2018
1 parent a3717f9 commit 94d0e3e
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 128 deletions.
3 changes: 2 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
'src/camaro.cpp'
],
'include_dirs': [
'<!(node -e "require(\'nan\')")'
"<!@(node -p \"require('node-addon-api').include\")"
],
'dependencies': ["<!(node -p \"require('node-addon-api').gyp\")"],
'cflags_cc': [
'-std=c++11',
'-fexceptions',
Expand Down
7 changes: 6 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "camaro",
"version": "2.2.8",
"version": "3.0.0",
"description": "Transforming XML to JSON using Node.js binding to native pugixml parser library",
"homepage": "https://github.com/tuananh/camaro",
"bugs": "https://github.com/tuananh/camaro/issues",
Expand All @@ -10,7 +10,8 @@
"test": "istanbul test ./node_modules/tape/bin/tape 'test/*test.js'",
"cover": "istanbul cover ./node_modules/tape/bin/tape 'test/*test.js'",
"bench": "node benchmark",
"install": "node-pre-gyp install --fallback-to-build"
"install": "node-pre-gyp install --fallback-to-build",
"clean": "rm -rf node_modules; rm -rf lib"
},
"repository": {
"type": "git",
Expand All @@ -28,6 +29,7 @@
"license": "MIT",
"dependencies": {
"nan": "2.9.1",
"node-addon-api": "1.2.0",
"node-pre-gyp": "0.9.0"
},
"devDependencies": {
Expand Down
47 changes: 15 additions & 32 deletions src/camaro.cpp
Original file line number Diff line number Diff line change
@@ -1,41 +1,24 @@
#define NOMINMAX
#include <nan.h>
#include <napi.h>
#include "transform.cpp"

using v8::Local;
using v8::String;
using v8::Value;
using v8::Object;
using v8::Isolate;
using v8::FunctionTemplate;
Napi::Object transform(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
std::string xml = info[0].As<Napi::String>().Utf8Value();
std::string json_template = info[1].As<Napi::String>().Utf8Value();

void transform(const Nan::FunctionCallbackInfo<Value>& args) {
if (args.Length() < 2) {
Nan::ThrowTypeError("Wrong number of arguments");
return;
}
Napi::Object output = Napi::Object::New(env);
transform_xml(xml, json_template, info, output);

if (!args[0]->IsString() || !args[1]->IsString()) {
Nan::ThrowTypeError("Wrong arguments");
return;
}

String::Utf8Value param1(args[0]->ToString());
String::Utf8Value param2(args[1]->ToString());

std::string xml = std::string(*param1);
std::string json_template = std::string(*param2);

Isolate* isolate = args.GetIsolate();
Local<Object> obj = Object::New(isolate);
transform_xml(xml, json_template, args, obj);

args.GetReturnValue().Set(obj);
return output;
}

void Init(Local<Object> exports) {
exports->Set(Nan::New("transform").ToLocalChecked(),
Nan::New<FunctionTemplate>(transform)->GetFunction());
Napi::Object Init(Napi::Env env, Napi::Object exports)
{
exports.Set(Napi::String::New(env, "transform"), Napi::Function::New(env, transform));

return exports;
}

NODE_MODULE(camaro, Init)
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
170 changes: 78 additions & 92 deletions src/transform.cpp
Original file line number Diff line number Diff line change
@@ -1,98 +1,89 @@
#define PUGIXML_NO_EXCEPTIONS
#define PUGIXML_HEADER_ONLY

#include <string>
#include "json/json.hpp"
#include "napi.h"
#include "pugixml/src/pugixml.hpp"
#include "json/json.hpp"
#include <string>

using json = nlohmann::json;
using string = std::string;
using xquery = pugi::xpath_query;
using nodeset = pugi::xpath_node_set;
using v8::Local;
using v8::String;
using v8::Number;
using v8::Boolean;
using v8::Value;
using v8::Object;
using v8::Array;
using v8::Isolate;

enum ReturnType { T_NUMBER, T_STRING, T_BOOLEAN };

template <typename T>
void walk(T& doc, json& n, Local<Object>& output, string key,
const Nan::FunctionCallbackInfo<Value>& args);
void walk(T &doc, json &n, Napi::Object &output, string key,
const Napi::CallbackInfo &info);

inline bool string_contains(string to_check, string prefix) {
return to_check.size() >= prefix.size() &&
to_check.compare(0, prefix.size(), prefix) == 0;
}

inline char charAt(string& str, size_t pos) {
inline char charAt(string &str, size_t pos) {
if (str.size() > pos) {
return str.at(pos);
} else {
return '\0';
}
}

ReturnType get_return_type(string& path) {
ReturnType get_return_type(string &path) {
const char ch = charAt(path, 0);
ReturnType t = T_STRING;
switch (ch) {
case 'b':
if (string_contains(path, "boolean(")) {
t = T_BOOLEAN;
}
break;
case 'c':
if (string_contains(path, "count(") || string_contains(path, "ceiling(")) {
t = T_NUMBER;
}
break;
case 'f':
if (string_contains(path, "floor(")) {
t = T_NUMBER;
}
break;
case 'n':
if (string_contains(path, "number(")) {
t = T_NUMBER;
}
break;
case 'r':
if (string_contains(path, "round(")) {
t = T_NUMBER;
}
break;
case 's':
if (string_contains(path, "sum(")) {
t = T_NUMBER;
}
break;
default:
t = T_STRING;
break;
case 'b':
if (string_contains(path, "boolean(")) {
t = T_BOOLEAN;
}
break;
case 'c':
if (string_contains(path, "count(") || string_contains(path, "ceiling(")) {
t = T_NUMBER;
}
break;
case 'f':
if (string_contains(path, "floor(")) {
t = T_NUMBER;
}
break;
case 'n':
if (string_contains(path, "number(")) {
t = T_NUMBER;
}
break;
case 'r':
if (string_contains(path, "round(")) {
t = T_NUMBER;
}
break;
case 's':
if (string_contains(path, "sum(")) {
t = T_NUMBER;
}
break;
default:
t = T_STRING;
break;
}

return t;
}

template <typename T>
Local<Boolean> seek_single_boolean(
T& xnode, json& j, const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Napi::Boolean query_boolean(T &xnode, json &j, const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
string path = j;
xquery query(path.c_str());
auto val = query.evaluate_boolean(xnode);
return Boolean::New(isolate, val);
return Napi::Boolean::New(env, val);
}

template <typename T>
Local<String> seek_single_string(T& xnode, json& j,
const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Napi::String query_string(T &xnode, json &j, const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
string path = j;
string val = "";

Expand All @@ -103,24 +94,22 @@ Local<String> seek_single_string(T& xnode, json& j,
val = query.evaluate_string(xnode);
}

return String::NewFromUtf8(isolate, val.c_str());
return Napi::String::New(env, val.c_str());
}

template <typename T>
Local<Number> seek_single_number(T& xnode, json& j,
const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Napi::Number query_number(T &xnode, json &j, const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
string path = j;
xquery query(path.c_str());
double val = query.evaluate_number(xnode);
return Number::New(isolate, val);
return Napi::Number::New(env, val);
}

template <typename T>
Local<Array> seek_array(T& doc, json& node,
const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Array> tmp = Array::New(isolate);
Napi::Array query_array(T &doc, json &node, const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Array tmp = Napi::Array::New(env);

// a special case for backward compatible with xpath-object-transform
if (node.empty()) {
Expand All @@ -136,23 +125,23 @@ Local<Array> seek_array(T& doc, json& node,
auto inner = node[1];

if (inner.is_object()) {
Local<Object> obj = Object::New(isolate);
Napi::Object obj = Napi::Object::New(env);
for (json::iterator it = inner.begin(); it != inner.end(); ++it) {
walk(n, it.value(), obj, it.key(), args);
walk(n, it.value(), obj, it.key(), info);
}
const char* pkey = std::to_string(i).c_str();
tmp->Set(String::NewFromUtf8(isolate, pkey), obj);
const char *pkey = std::to_string(i).c_str();
tmp.Set(Napi::String::New(env, pkey), obj);
} else if (inner.is_string()) {
string path = inner;
ReturnType type = get_return_type((path));
if (type == T_STRING) {
tmp->Set(i, seek_single_string(n, inner, args));
tmp.Set(i, query_string(n, inner, info));
}
if (type == T_NUMBER) {
tmp->Set(i, seek_single_number(n, inner, args));
tmp.Set(i, query_number(n, inner, info));
}
if (type == T_BOOLEAN) {
tmp->Set(i, seek_single_boolean(n, inner, args));
tmp.Set(i, query_boolean(n, inner, info));
}
}
}
Expand All @@ -161,57 +150,54 @@ Local<Array> seek_array(T& doc, json& node,
}

template <typename T>
Local<Object> seek_object(T& doc, json& node,
const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Object> output = Object::New(isolate);
Napi::Object query_object(T &doc, json &node, const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();
Napi::Object output = Napi::Object::New(env);

for (json::iterator it = node.begin(); it != node.end(); ++it) {
string key = it.key();
walk(doc, *it, output, key, args);
walk(doc, *it, output, key, info);
}

return output;
}

template <typename T>
void walk(T& doc, json& n, Local<Object>& output, string key,
const Nan::FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
const char* ckey = key.c_str();
void walk(T &doc, json &n, Napi::Object &output, string key,
const Napi::CallbackInfo &info) {
Napi::Env env = info.Env();

const char *ckey = key.c_str();
if (n.is_array()) {
output->Set(String::NewFromUtf8(isolate, ckey), seek_array(doc, n, args));
output.Set(Napi::String::New(env, ckey), query_array(doc, n, info));
} else if (n.is_object()) {
output->Set(String::NewFromUtf8(isolate, ckey), seek_object(doc, n, args));
output.Set(Napi::String::New(env, ckey), query_object(doc, n, info));
} else if (n.is_string()) {
string path = n;
ReturnType type = get_return_type(path);
if (type == T_NUMBER) {
output->Set(String::NewFromUtf8(isolate, ckey),
seek_single_number(doc, n, args));
output.Set(Napi::String::New(env, ckey), query_number(doc, n, info));
}
if (type == T_STRING) {
output->Set(String::NewFromUtf8(isolate, ckey),
seek_single_string(doc, n, args));
output.Set(Napi::String::New(env, ckey), query_string(doc, n, info));
}
if (type == T_BOOLEAN) {
output->Set(String::NewFromUtf8(isolate, ckey),
seek_single_boolean(doc, n, args));
output.Set(Napi::String::New(env, ckey), query_boolean(doc, n, info));
}
}
}

void transform_xml(string xml, string fmt,
const Nan::FunctionCallbackInfo<Value>& args,
Local<Object>& output) {
void transform_xml(string xml, string fmt, const Napi::CallbackInfo &info,
Napi::Object &output) {
pugi::xml_document doc;

if (doc.load_string(xml.c_str())) {
auto j = json::parse(fmt);

for (json::iterator it = j.begin(); it != j.end(); ++it) {
string key = it.key();
auto& node = j[key];
walk(doc, node, output, key, args);
auto &node = j[key];
walk(doc, node, output, key, info);
}
}
}

0 comments on commit 94d0e3e

Please sign in to comment.