You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

192 lines
4.4 KiB

var assert = require('./util').assert
var base = require('./base')
var codecs = require('./codecs')
var bytewise = exports
//
// expose type information
//
var sorts = bytewise.sorts = base.sorts
bytewise.bound = base.bound
bytewise.compare = base.compare
bytewise.equal = base.equal
//
// generate a buffer with type's byte prefix from source value
//
function serialize(type, source, options) {
var codec = type.codec
if (!codec)
return postEncode(new Buffer([ type.byte ]), options)
var buffer = codec.encode(source, bytewise)
if (options && options.nested && codec.escape)
buffer = codec.escape(buffer)
var hint = typeof codec.length === 'number' ? (codec.length + 1) : void 0
var buffers = [ new Buffer([ type.byte ]), buffer ]
return postEncode(Buffer.concat(buffers, hint), options)
}
//
// core encode logic
//
bytewise.encode = function(source, options) {
// check for invalid/incomparable values
assert(!base.invalid(source), 'Invalid value')
// encode bound types (ranges)
var boundary = base.bound.getBoundary(source)
if (boundary)
return boundary.encode(source, bytewise)
// encode standard value-typed sorts
var order = base.order
var sort
for (var i = 0, length = order.length; i < length; ++i) {
sort = sorts[order[i]]
if (sort.is(source)) {
// loop over any subsorts defined on sort
// TODO: clean up
var subsorts = sort.sorts || { '': sort }
for (key in subsorts) {
var subsort = subsorts[key]
if (subsort.is(source))
return serialize(subsort, source, options)
}
// source is an unsupported subsort
assert(false, 'Unsupported sort value')
}
}
// no type descriptor found
assert(false, 'Unknown value')
}
//
// core decode logic
//
bytewise.decode = function (buffer, options) {
// attempt to decode string input using configurable codec
if (typeof buffer === 'string') {
buffer = bytewise.stringCodec.encode(buffer)
}
assert(!buffer || !buffer.undecodable, 'Encoded value not decodable')
var byte = buffer[0]
var type = bytewise.getType(byte)
assert(type, 'Invalid encoding: ' + buffer)
// if type provides a decoder it is passed the base type system as second arg
var codec = type.codec
if (codec) {
var decoded = codec.decode(buffer.slice(1), bytewise)
if (options && options.nested && codec.unescape)
decoded = codec.unescape(decoded)
return postDecode(decoded, options)
}
// nullary types without a codec must provide a value for their decoded form
assert('value' in type, 'Unsupported encoding: ' + buffer)
return postDecode(type.value, options)
}
//
// process top level
//
function postEncode(encoded, options) {
if (options === null)
return encoded
return bytewise.postEncode(encoded, options)
}
//
// invoked after encoding with encoded buffer instance
//
bytewise.postEncode = function (encoded, options) {
// override buffer toString method to default to hex to help coercion issues
// TODO: just return pure buffer, do this toString hackery in bytewise
encoded.toString = function (encoding) {
if (!encoding)
return bytewise.stringCodec.decode(encoded)
return Buffer.prototype.toString.apply(encoded, arguments)
}
return encoded
}
function postDecode(decoded, options) {
if (options === null)
return decoded
return bytewise.postDecode(decoded, options)
}
//
// invoked after decoding with decoded value
//
bytewise.postDecode = function (decoded, options) {
return decoded
}
//
// registry mapping byte prefixes to type descriptors
//
var PREFIX_REGISTRY
function registerType(type) {
var byte = type && type.byte
if (byte == null)
return
if (byte in PREFIX_REGISTRY)
assert.deepEqual(type, PREFIX_REGISTRY[byte], 'Duplicate prefix: ' + byte)
PREFIX_REGISTRY[type.byte] = type
}
function registerTypes(types) {
for (var key in types) {
registerType(types[key])
}
}
//
// look up type descriptor associated with a given byte prefix
//
bytewise.getType = function (byte) {
// construct and memoize byte prefix registry on first run
if (!PREFIX_REGISTRY) {
PREFIX_REGISTRY = {}
// register sorts
var sort
for (var key in sorts) {
sort = sorts[key]
// if sort has subsorts register these instead
sort.sorts ? registerTypes(sort.sorts) : registerType(sort)
}
}
return PREFIX_REGISTRY[byte]
}
bytewise.buffer = true
bytewise.stringCodec = codecs.HEX
bytewise.type = 'bytewise-core'