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
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' |
|
|
|
|