pipette
Version:
Stream and pipe utilities for Node
183 lines (151 loc) • 4.59 kB
JavaScript
// Copyright 2012 The Obvious Corporation.
/*
* Utilities for handing character encodings
*/
/*
* Modules used
*/
;
var typ = require("typ");
var consts = require("./consts");
/*
* Module variables
*/
/**
* Special encoding value indicating default passthrough or `utf8`
* behavior (depending on context).
*/
var NO_ENCODING = "no-encoding";
/*
* Helper functions
*/
/**
* Gets the "fixed" name for the given encoding, or `undefined` if the
* named encoding is not considered valid as an argument to
* `Stream.setEncoding()`.
*/
function fixEncoding(encodingName) {
switch (encodingName) {
case consts.ASCII:
case consts.BASE64:
case consts.HEX:
case consts.UCS2:
case consts.UTF8: {
return encodingName;
}
// Node 0.6 doesn't understand the name "utf16le", but it's
// defined as an alias for "ucs2" in Node 0.8. This handles that
// confusion.
case consts.UTF16LE: {
return consts.UCS2;
}
// Be explicit here about what `undefined` translates to. This
// makes it so an `undefined` returned from this function has the
// natural meaning of "bogus".
case undefined: {
return NO_ENCODING;
}
}
return undefined;
}
/**
* Asserts that the named encoding is considered valid as an argument
* to `Stream.setEncoding()`, returning the fixed version if so.
*/
function fixValidEncoding(encodingName) {
var result = fixEncoding(encodingName);
if (!result) {
throw new Error("Invalid encoding name: " + encodingName);
}
return result;
}
/**
* Encodes the given `value` with the named `encoding`, per the usual
* contracts (see elsewhere in this file). This assumes the encoding
* is valid.
*/
function encodeValuePrevalidated(value, encodingName) {
if (typ.isBuffer(value)) {
return value;
}
if (!typ.isString(value)) {
value = value.toString();
}
return new Buffer(
value, (encodingName === NO_ENCODING) ? consts.UTF8 : encodingName);
}
/*
* Exported bindings
*/
/**
* Returns whether the given encoding name is valid.
*/
function isValidName(encodingName) {
return fixEncoding(encodingName) !== undefined;
}
/**
* Construct a Codec instance, which manages conversions between buffers
* and strings, with the possibility of just passing buffers through.
* The instance starts out as a pass-through (that is, with encoding
* set to be `undefined`).
*/
function Codec(initialEncoding) {
this.encodingName = undefined;
this.setEncoding(initialEncoding);
}
/**
* Sets the encoding to use. The valid arguments are the ones named in
* the Node docs (v0.6.*) for `Stream.setEncoding()`, plus `undefined`
* which indicates that no string conversion should be performed.
*/
Codec.prototype.setEncoding = function setEncoding(encodingName) {
this.encodingName = fixValidEncoding(encodingName);
}
/**
* Encodes the given value according to the currently-defined
* encoding.
*
* If `value` is a buffer, it is returned as-is. If `value` is a
* string, this returns a buffer consisting of the encoded string. If
* `value` is anything else, it is first converted to a string with
* `toString()` and then from that to a buffer.
*/
Codec.prototype.encode = function encode(value) {
return encodeValuePrevalidated(value, this.encodingName);
}
/**
* Decodes the given buffer according to the currently-defined
* encoding. This returns the decoded string, or returns the buffer
* as-is if the encoding was set to `undefined`.
*/
Codec.prototype.decode = function decode(buffer) {
var encoding = this.encodingName;
return (encoding === NO_ENCODING) ? buffer : buffer.toString(encoding);
}
/**
* Emits the given buffer as a `data` event from the given emitter, first
* decoding it according to the currently-defined encoding.
*/
Codec.prototype.emitData = function emitData(emitter, buffer) {
emitter.emit(consts.DATA, this.decode(buffer));
}
Object.freeze(Codec);
Object.freeze(Codec.prototype);
/**
* Encodes the given `value` with the named `encoding`, validating
* that the encoding is proper.
*
* If `value` is a buffer, it is returned as-is. If `value` is a
* string, this returns a buffer consisting of the encoded string. If
* `value` is anything else, it is first converted to a string with
* `toString()` and then from that to a buffer.
*/
function encodeValue(value, encoding) {
encoding = fixValidEncoding(encoding);
return encodeValuePrevalidated(value, encoding);
}
module.exports = {
Codec: Codec,
encodeValue: encodeValue,
isValidName: isValidName
};