Skip to content

Commit

Permalink
First significant push. Most functionality ok.
Browse files Browse the repository at this point in the history
  • Loading branch information
albanm committed Aug 13, 2014
1 parent c70a449 commit c355bf8
Show file tree
Hide file tree
Showing 20 changed files with 662 additions and 178 deletions.
102 changes: 100 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,104 @@
node-libxslt
============

Node.js bindings for libxslt compatible with libxmljs
Node.js bindings for [libxslt](http://xmlsoft.org/libxslt/) compatible with [libxmljs](https://github.com/polotek/libxmljs/issues/226).

**Not working yet. Come back later.**
Installation
------------

npm install node-libxslt

Basic usage
-----------

```js
var lixslt = require('node-libxslt');

var stylesheet = libxslt.stylesheet(stylesheetString);

var params = {
MyParam: 'my value'
};

// 'params' parameter is optional
stylesheet.apply(documentString, params, function(err, result){
// err contains any error from parsing the document or applying the the stylesheet
// result is a string containing the result of the transformation
});

```

Libxmljs integration
--------------------

Node-libxslt depends on [libxmljs](https://github.com/polotek/libxmljs/issues/226) in the same way that [libxslt](http://xmlsoft.org/libxslt/) depends on [libxml](http://xmlsoft.org/). This dependancy makes possible to bundle and to load in memory libxml only once for users of both libraries.

It is possible to work with libxmljs documents instead of strings:

```js
var lixslt = require('node-libxslt');
var libxmljs = require('libxmljs');

var stylesheetObj = libxmljs.parseXml(stylesheetString);
var stylesheet = libxslt.stylesheet(stylesheetObj);

var document = libxmljs.parseXml(documentString);
stylesheet.apply(document, function(err, result){
// result is now a libxmljs document containing the result of the transformation
});

```

This is only useful if you already needed to parse a document before applying the stylesheet for previous manipulations.
Or if you wish to be returned a document instead of a string for ulterior manipulations.
In these cases you will prevent extraneous parsings and serializations.

Sync or async
-------------

The same *apply()* function can be used in synchronous mode simply by removing the callback parameter.
In this case if a parsing error occurs it will be thrown.

```js
var lixslt = require('node-libxslt');

var stylesheet = libxslt.stylesheet(stylesheetString);

var result = stylesheet.apply(documentString);

```

The asynchronous function uses the [libuv work queue](http://nikhilm.github.io/uvbook/threads.html#libuv-work-queue)
to provide parallelized computation in node.js worker threads. This makes it non-blocking for the main event loop of node.js.

Note that libxmljs parsing doesn't use the work queue, so only a part of the process is actually parallelized.

A small benchmark is available in the project. It has a very limited scope, it uses always the same small transformation a few thousand times.
To run it use:

node benchmark.js

This is an example of its results with an intel core i5 3.1GHz:

```
10000 synchronous apply from parsed doc in 331ms = 30211/s
10000 asynchronous apply in series from parsed doc in 538ms = 18587/s
10000 asynchronous apply in parallel from parsed doc in 217ms = 46083/s
```

Observations:
- it's pretty fast !
- asynchronous is slower when running in series.
- asynchronous can become faster when concurrency is high.

Conclusion:
- use asynchronous by default it will be kinder to your main event loop and is pretty fast anyway.
- use synchronous only if you really want the highest performance and expect low concurrency.
- of course you can also use synchronous simply to reduce code depth. If you don't expect a huge load it will be ok.

OS compatibility
----------------

Right now only 64bits linux is supported. The support of other environments is a work in progress.

For windows user: node-libxslt depends on [node-gyp](https://github.com/TooTallNate/node-gyp), you will have to go through its installation.
101 changes: 101 additions & 0 deletions benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
var fs = require('fs');
var async = require('async');
var libxmljs = require("libxmljs");
var libxslt = require('./index');

var stylesheetStr = fs.readFileSync('./test/resources/cd.xsl', 'utf8');
var stylesheetObj = libxmljs.parseXml(stylesheetStr);
var stylesheet = libxslt.stylesheet(stylesheetObj);
var docStr = fs.readFileSync('./test/resources/cd.xml', 'utf8');
var docObj = libxmljs.parseXml(docStr);

var bench = function(name, iterations, f) {
return function(callback) {
var before = Date.now();
f(iterations, function() {
var duration = (Date.now() - before);
console.log('%d %s in %dms = %d/s', iterations, name, duration, Math.round(iterations / (duration / 1000)));
if (callback) callback();
});
};
};

var stylesheetParsingStr = function(iterations, callback) {
for (var i = 0; i < iterations; i++) {
libxslt.stylesheet(stylesheetStr);
}
callback();
};

var stylesheetParsingObj = function(iterations, callback) {
for (var i = 0; i < iterations; i++) {
libxslt.stylesheet(stylesheetObj);
}
callback();
};

var applySyncStr = function(iterations, callback) {
for (var i = 0; i < iterations; i++) {
stylesheet.apply(docStr);
}
callback();
};

var applySyncObj = function(iterations, callback) {
for (var i = 0; i < iterations; i++) {
stylesheet.apply(docObj);
}
callback();
};

var applyAsyncSeriesStr = function(iterations, callback) {
var i = 0;
async.eachSeries(new Array(iterations), function(u, callbackEach) {
stylesheet.apply(docStr, function(err, result) {
i++;
callbackEach(err);
});
}, callback);
};

var applyAsyncSeriesObj = function(iterations, callback) {
var i = 0;
async.eachSeries(new Array(iterations), function(u, callbackEach) {
stylesheet.apply(docObj, function(err, result) {
i++;
callbackEach(err);
});
}, callback);
};

var applyAsyncParallelStr = function(iterations, callback) {
var i = 0;
async.eachLimit(new Array(iterations), 10, function(u, callbackEach) {
stylesheet.apply(docStr, function(err, result) {
i++;
callbackEach(err);
});
}, callback);
};

var applyAsyncParallelObj = function(iterations, callback) {
var i = 0;
async.eachLimit(new Array(iterations), 10, function(u, callbackEach) {
stylesheet.apply(docObj, function(err, result) {
i++;
callbackEach(err);
});
}, callback);
};

var iterations = 10000;
async.series([
//bench('stylesheet parsing from string\t\t\t', iterations, stylesheetParsingStr),
//bench('stylesheet parsing from parsed doc\t\t\t', iterations, stylesheetParsingObj),
//bench('synchronous apply from string\t\t\t', iterations, applySyncStr),
bench('synchronous apply from parsed doc\t\t\t', iterations, applySyncObj),
//bench('asynchronous apply in series from string\t\t', iterations, applyAsyncSeriesStr),
bench('asynchronous apply in series from parsed doc\t', iterations, applyAsyncSeriesObj),
//bench('asynchronous apply in parallel from string\t', iterations, applyAsyncParallelStr),
bench('asynchronous apply in parallel from parsed doc\t', iterations, applyAsyncParallelObj)
]);
9 changes: 4 additions & 5 deletions binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
"sources": [ "src/node_libxslt.cc", "src/stylesheet.cc" ],
"include_dirs": ["<!(node -e \"require('nan')\")"],
'dependencies': [
'./deps/libxslt.gyp:libxslt',
'./deps/libxslt.gyp:libexslt',
'./node_modules/libxmljs/vendor/libxml/libxml.gyp:libxml',
'./node_modules/libxmljs/binding.gyp:xmljs'
]
'./deps/libxslt/libxslt.gyp:libxslt',
'./deps/libxslt/libxslt.gyp:libexslt'
],
'cflags': ['-g'],
}
]
}
22 changes: 22 additions & 0 deletions common.gypi
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# imitation of this https://github.com/TooTallNate/node-vorbis/blob/master/common.gypi
{
'variables': {
'node_xmljs': '<!(node -p -e "require(\'path\').dirname(require.resolve(\'libxmljs\'))")',
'xmljs_include_dirs': [
'<(node_xmljs)/src/',
'<(node_xmljs)/vendor/libxml/include'
],
'conditions': [
['OS=="win"', {
'xmljs_libraries': [
'<(node_xmljs)/build/$(Configuration)/xmljs.lib'
],
}, {
'xmljs_libraries': [
'<(node_xmljs)/build/$(BUILDTYPE)/xmljs.node',
'-Wl,-rpath,<(node_xmljs)/build/$(BUILDTYPE)'
],
}],
],
},
}
76 changes: 0 additions & 76 deletions deps/libxslt.gyp

This file was deleted.

Loading

0 comments on commit c355bf8

Please sign in to comment.