Skip to content

Commit

Permalink
Improved API and added examples
Browse files Browse the repository at this point in the history
  • Loading branch information
dinhoabreu committed Mar 16, 2012
1 parent 5ace01f commit 3d5d2c7
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 85 deletions.
49 changes: 49 additions & 0 deletions README.md
@@ -0,0 +1,49 @@
# codes

Node.js iconv bindings - Streamable, fast and lightweight

## Installing with [npm](http://npmjs.org/)

npm install codes

## Usage

Encode from one character encoding to another:

var assert = require('assert');
var codes = require('../index');

// convert from UTF-8 to ISO-8859-1
var str = 'É isso ai. Testando acentuação: áéíóú...';
var buffer = codes.encode(str, 'ISO-8859-1//TRANSLIT');
var str2 = codes.decode(buffer, 'ISO-8859-1');
assert.equal(str, str2);

// stream from LATIN1 to UTF-8
var cs = codes.createStream('UTF-8', 'LATIN1');
var str4 = '';
cs.on('data', function (data) {
var str3 = data.toString();
console.log(str3);
str4 += str3;
});
cs.on('end', function () {
assert.equal(str4, str);
console.log(str4);
});
cs.on('error', function (error) {
console.log(error);
});
cs.write(buffer.slice(0,1));
cs.write(buffer.slice(1,8));
cs.write(buffer.slice(8,16));
cs.write(buffer.slice(16,24));
cs.write(buffer.slice(24, 28));
cs.write(buffer.slice(28, 32));
cs.write(buffer.slice(32));
cs.end();

// convert from UTF-8 to ASCII
var str = 'aéióuçÁEÍOÚC';
var ascii = codes.convert(str, 'ASCII//TRANSLIT', 'UTF-8');
console.log(str + ' => ' + ascii.toString());
2 changes: 1 addition & 1 deletion deps.sh
Expand Up @@ -19,7 +19,7 @@ fi

cd deps/libiconv-1.14/
if [ ! -f Makefile ]; then
./configure --prefix=$PREFIX --enable-static
./configure --prefix=$PREFIX --enable-static --enable-extra-encodings
fi;
make -s && make -s install
cd -
Expand Down
37 changes: 37 additions & 0 deletions examples/ex01.js
@@ -0,0 +1,37 @@
var assert = require('assert');
var codes = require('../index');

// convert from UTF-8 to ISO-8859-1
var str = 'É isso ai. Testando acentuação: áéíóú...';
var buffer = codes.encode(str, 'ISO-8859-1//TRANSLIT');
var str2 = codes.decode(buffer, 'ISO-8859-1');
assert.equal(str, str2);

// stream from LATIN1 to UTF-8
var cs = codes.createStream('UTF-8', 'LATIN1');
var str4 = '';
cs.on('data', function (data) {
var str3 = data.toString();
console.log(str3);
str4 += str3;
});
cs.on('end', function () {
assert.equal(str4, str);
console.log(str4);
});
cs.on('error', function (error) {
console.log(error);
});
cs.write(buffer.slice(0,1));
cs.write(buffer.slice(1,8));
cs.write(buffer.slice(8,16));
cs.write(buffer.slice(16,24));
cs.write(buffer.slice(24, 28));
cs.write(buffer.slice(28, 32));
cs.write(buffer.slice(32));
cs.end();

// convert from UTF-8 to ASCII
var str = 'aéióuçÁEÍOÚC';
var ascii = codes.convert(str, 'ASCII//TRANSLIT', 'UTF-8');
console.log(str + ' => ' + ascii.toString());
64 changes: 34 additions & 30 deletions lib/index.js → index.js
@@ -1,23 +1,21 @@
var bind = require('../build/Release/iconv_binding');
var bind = require('./build/Release/iconv');
var stream = require('stream');
var util = require('util');

function create(options) {
var from = options.from || 'UTF-8',
to = options.to,
size = options.size || 8192,
function create(to, from, options) {
var from = from || 'UTF-8',
to = to.split('//'),
size = options && options.size || 8192,
iconv;
from = bind.canonicalize(from);
to = bind.canonicalize(to);
if (options.option)
to += options.option;
to[0] = bind.canonicalize(to[0]);
to = to.join('//');
iconv = new bind.Iconv(to, from);
return {from: from, to: to, size: size, iconv: iconv};
}

function IconvStream(options) {
var h = create(options);
this.options = options;
function CodesStream(to, from, options) {
var h = create(to, from, options);
this._iconv = h.iconv;
this._input = new Buffer(h.size);
this._input.start = 0;
Expand All @@ -28,15 +26,15 @@ function IconvStream(options) {
this.readable = true;
this.writable = true;
}
util.inherits(IconvStream, stream.Stream);
util.inherits(CodesStream, stream.Stream);

IconvStream.prototype.write = function (data) {
CodesStream.prototype.write = function (data) {
if (!this.writable) {
this.emit('error', new Error('stream not writable'))
return false;
}
if (!Buffer.isBuffer(data)) {
var encoding = 'utf8';
var encoding = 'UTF-8';
if (typeof(arguments[1]) == 'string') encoding = arguments[1];
data = new Buffer('' + data, encoding);
}
Expand All @@ -54,7 +52,7 @@ IconvStream.prototype.write = function (data) {
return true;
}

IconvStream.prototype.end = function (data) {
CodesStream.prototype.end = function (data) {
if (data)
this.write.apply(this, arguments);
if (this.writable) {
Expand All @@ -65,7 +63,7 @@ IconvStream.prototype.end = function (data) {
this.writable = false;
}

IconvStream.prototype._write = function (chunk) {
CodesStream.prototype._write = function (chunk) {
var input = this._input;
chunk.stop = false;
while (chunk.length > 0 && !chunk.stop)
Expand All @@ -78,14 +76,13 @@ IconvStream.prototype._write = function (chunk) {
}
}

IconvStream.prototype._convert = function (chunk) {
CodesStream.prototype._convert = function (chunk) {
var iconv = this._iconv;
var output = this._output;
var r = iconv.convert(chunk, output);
var chunkEnd = chunk.length - r.leftBytesIn;
var outputEnd = output.length - r.leftBytesOut;
var chunk = chunk.slice(chunkEnd);
var data = output.slice(0, outputEnd);
var offsetIn = r.offsetIn;
var chunk = chunk.slice(offsetIn);
var data = output.slice(0, r.offsetOut);
chunk.stop = true;
if (data.length)
this.emit('data', data);
Expand All @@ -98,7 +95,7 @@ IconvStream.prototype._convert = function (chunk) {
case 'EILSEQ':
var error = new Error(r.error);
error.code = r.errno;
chunk = chunk.slice(chunkEnd + 1);
chunk = chunk.slice(offsetIn + 1);
this.emit('error', error);
return chunk.slice(0, 0);
break;
Expand All @@ -112,18 +109,25 @@ IconvStream.prototype._convert = function (chunk) {
return chunk;
}

exports.createStream = function (options) {
return new IconvStream(options);
exports.CodesStream = CodesStream;
exports.createStream = function (to, from, options) {
return new CodesStream(to, from, options);
}
exports.convert = function (input, options) {
exports.convert = function (input, to, from, options) {
if (typeof input == 'string')
input = new Buffer(input, 'utf8');
var h = create(options);
var output = new Buffer(input.length * 2);
input = new Buffer(input, 'UTF-8');
var h = create(to, from, options);
var output = new Buffer(input.length * 4);
var r = h.iconv.convert(input, output);
var outputEnd = output.length - r.leftBytesOut;
return output.slice(0, outputEnd);
return output.slice(0, r.offsetOut);
}
exports.encode = function (input, to, options) {
return this.convert(input, to, 'UTF-8', options);
}
exports.decode = function (input, from, options) {
return this.convert(input, 'UTF-8', from, options).toString();
}
exports.encodings = bind.encodings;
exports.canonicalize = bind.canonicalize;
exports.TRANSLIT = '//TRANSLIT';
exports.IGNORE = '//IGNORE';
8 changes: 5 additions & 3 deletions package.json
Expand Up @@ -2,12 +2,14 @@
"author": "Edison E. Abreu <dinho.abreu@gmail.com>",
"name": "codes",
"description": "Node.js iconv bindings - Streamable, fast and lightweight",
"version": "0.0.2",
"keywords": "iconv charset encoding encode decode convert code codes stream fast ligthweight",
"homepage": "https://github.com/dinhoabreu/codes",
"version": "0.1.0",
"repository": {
"type": "git",
"url": "git@github.com:dinhoabreu/codes.git"
},
"main": "lib/index.js",
"main": "index.js",
"scripts": {
"prepublish": "node-waf distclean && rm -rf libiconv",
"preinstall": "./deps.sh",
Expand All @@ -20,6 +22,6 @@
},
"optionalDependencies": {},
"engines": {
"node": "0.6.x"
"node": ">=0.6.x"
}
}
36 changes: 19 additions & 17 deletions src/iconv_binding.cc → src/iconv.cc
Expand Up @@ -57,15 +57,15 @@ class Iconv {
static void Initialize(Handle<Object> target) {
HandleScope scope;

push_sym = Persistent<String>::New(String::NewSymbol("push"));
touppercase_sym = Persistent<String>::New(String::NewSymbol("toUpperCase"));
convert_sym = Persistent<String>::New(String::NewSymbol("convert"));
iconv_sym = Persistent<String>::New(String::NewSymbol("Iconv"));
canonicalize_sym = Persistent<String>::New(String::NewSymbol("canonicalize"));
encodings_sym = Persistent<String>::New(String::NewSymbol("encodings"));

leftBytesIn_sym = Persistent<String>::New(String::NewSymbol("leftBytesIn"));
leftBytesOut_sym = Persistent<String>::New(String::NewSymbol("leftBytesOut"));
push_sym = Persistent<String>::New(String::NewSymbol("push"));
touppercase_sym = Persistent<String>::New(String::NewSymbol("toUpperCase"));
convert_sym = Persistent<String>::New(String::NewSymbol("convert"));
iconv_sym = Persistent<String>::New(String::NewSymbol("Iconv"));
canonicalize_sym = Persistent<String>::New(String::NewSymbol("canonicalize"));
encodings_sym = Persistent<String>::New(String::NewSymbol("encodings"));

offsetIn_sym = Persistent<String>::New(String::NewSymbol("offsetIn"));
offsetOut_sym = Persistent<String>::New(String::NewSymbol("offsetOut"));
code_sym = Persistent<String>::New(String::NewSymbol("code"));
errno_sym = Persistent<String>::New(String::NewSymbol("errno"));
error_sym = Persistent<String>::New(String::NewSymbol("error"));
Expand Down Expand Up @@ -160,13 +160,15 @@ class Iconv {
Local<Object> target_obj = args[1]->ToObject();
char *source_data = Buffer::Data(source_obj);
size_t source_length = Buffer::Length(source_obj);
size_t source_left = source_length;
char *target_data = Buffer::Data(target_obj);
size_t target_length = Buffer::Length(target_obj);
size_t r = ic->convert(&source_data, &source_length, &target_data, &target_length);
size_t target_left = target_length;
size_t r = ic->convert(&source_data, &source_left, &target_data, &target_left);

Local<Object> obj = Object::New();
obj->Set(leftBytesIn_sym, Integer::NewFromUnsigned(source_length));
obj->Set(leftBytesOut_sym, Integer::NewFromUnsigned(target_length));
obj->Set(offsetIn_sym, Integer::NewFromUnsigned(source_length - source_left));
obj->Set(offsetOut_sym, Integer::NewFromUnsigned(target_length - target_left));
obj->Set(code_sym, Integer::New(r));
if (r == -1) {
obj->Set(error_sym, String::New(strerror(errno)));
Expand All @@ -184,8 +186,8 @@ class Iconv {
return scope.Close(obj);
}
static Persistent<FunctionTemplate> constructor_template;
static Persistent<String> leftBytesIn_sym;
static Persistent<String> leftBytesOut_sym;
static Persistent<String> offsetIn_sym;
static Persistent<String> offsetOut_sym;
static Persistent<String> code_sym;
static Persistent<String> errno_sym;
static Persistent<String> error_sym;
Expand All @@ -194,13 +196,13 @@ class Iconv {
};

Persistent<FunctionTemplate> Iconv::constructor_template;
Persistent<String> Iconv::leftBytesIn_sym;
Persistent<String> Iconv::leftBytesOut_sym;
Persistent<String> Iconv::offsetIn_sym;
Persistent<String> Iconv::offsetOut_sym;
Persistent<String> Iconv::code_sym;
Persistent<String> Iconv::errno_sym;
Persistent<String> Iconv::error_sym;

void init(Handle<Object> target) {
Iconv::Initialize(target);
}
NODE_MODULE(iconv_binding, init)
NODE_MODULE(iconv, init)

0 comments on commit 3d5d2c7

Please sign in to comment.