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