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'