OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-booking / node_modules / compact-encoding / test.js

1185 lines · javascript ref:
const enc = require('./')
const test = require('brittle')
const b4a = require('b4a')

test('uint', function (t) {
  const state = enc.state()

  enc.uint.preencode(state, 42)
  t.alike(state, enc.state(0, 1))
  enc.uint.preencode(state, 4200)
  t.alike(state, enc.state(0, 4))
  enc.uint.preencode(state, Number.MAX_SAFE_INTEGER)
  t.alike(state, enc.state(0, 13))

  state.buffer = b4a.alloc(state.end)
  enc.uint.encode(state, 42)
  t.alike(
    state,
    enc.state(1, 13, b4a.from([42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
  )
  enc.uint.encode(state, 4200)
  t.alike(
    state,
    enc.state(4, 13, b4a.from([42, 0xfd, 104, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
  )
  enc.uint.encode(state, Number.MAX_SAFE_INTEGER)
  t.alike(
    state,
    enc.state(
      13,
      13,
      b4a.from([42, 0xfd, 104, 16, 0xff, 255, 255, 255, 255, 255, 255, 31, 0])
    )
  )

  state.start = 0
  t.is(enc.uint.decode(state), 42)
  t.is(enc.uint.decode(state), 4200)
  t.is(enc.uint.decode(state), Number.MAX_SAFE_INTEGER)
  t.is(state.start, state.end)

  t.exception(() => enc.uint.decode(state))
})

test('int', function (t) {
  const state = enc.state()

  enc.int.preencode(state, 42)
  t.alike(state, enc.state(0, 1))
  enc.int.preencode(state, -4200)
  t.alike(state, enc.state(0, 4))

  state.buffer = b4a.alloc(state.end)
  enc.int.encode(state, 42)
  t.alike(state, enc.state(1, 4, b4a.from([84, 0, 0, 0])))
  enc.int.encode(state, -4200)
  t.alike(state, enc.state(4, 4, b4a.from([84, 0xfd, 207, 32])))

  state.start = 0
  t.is(enc.int.decode(state), 42)
  t.is(enc.int.decode(state), -4200)
  t.is(state.start, state.end)

  t.exception(() => enc.int.decode(state))
})

test('float64', function (t) {
  const state = enc.state()

  enc.float64.preencode(state, 162.2377294)
  t.alike(state, enc.state(0, 8))

  state.buffer = b4a.alloc(state.end)
  t.alike(state, enc.state(0, 8, b4a.from([0, 0, 0, 0, 0, 0, 0, 0])))
  enc.float64.encode(state, 162.2377294)
  t.alike(
    state,
    enc.state(8, 8, b4a.from([0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40]))
  )

  state.start = 0
  t.is(enc.float64.decode(state), 162.2377294)
  t.is(state.start, state.end)

  t.exception(() => enc.float64.decode(state))

  // alignement
  state.start = 0
  state.end = 0
  state.buffer = null

  enc.int.preencode(state, 0)
  enc.float64.preencode(state, 162.2377294)
  t.alike(state, enc.state(0, 9))

  state.buffer = b4a.alloc(state.end)
  t.alike(state, enc.state(0, 9, b4a.from([0, 0, 0, 0, 0, 0, 0, 0, 0])))
  enc.int.encode(state, 0)
  enc.float64.encode(state, 162.2377294)
  t.alike(
    state,
    enc.state(
      9,
      9,
      b4a.from([0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40])
    )
  )

  state.start = 0
  t.is(enc.int.decode(state), 0)
  t.is(enc.float64.decode(state), 162.2377294)
  t.is(state.start, state.end)

  // subarray
  const buf = b4a.alloc(10)
  state.start = 0
  state.buffer = buf.subarray(1)
  t.alike(state, enc.state(0, 9, b4a.from([0, 0, 0, 0, 0, 0, 0, 0, 0])))
  enc.int.encode(state, 0)
  enc.float64.encode(state, 162.2377294)
  t.alike(
    state,
    enc.state(
      9,
      9,
      b4a.from([0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40])
    )
  )
  t.alike(buf, b4a.from([0, 0, 0x87, 0xc9, 0xaf, 0x7a, 0x9b, 0x47, 0x64, 0x40]))

  state.start = 0
  t.is(enc.int.decode(state), 0)
  t.is(enc.float64.decode(state), 162.2377294)
  t.is(state.start, state.end)

  // 0
  state.start = 0
  state.end = 0
  state.buffer = null

  enc.float64.preencode(state, 162.2377294)
  state.buffer = b4a.alloc(state.end)
  enc.float64.encode(state, 0)
  t.alike(state, enc.state(8, 8, b4a.from([0, 0, 0, 0, 0, 0, 0, 0])))

  state.start = 0
  t.is(enc.float64.decode(state), 0)
  t.is(state.start, state.end)

  // Infinity
  state.start = 0
  state.end = 0
  state.buffer = null

  enc.float64.preencode(state, Infinity)
  state.buffer = b4a.alloc(state.end)
  enc.float64.encode(state, Infinity)
  t.alike(state, enc.state(8, 8, b4a.from([0, 0, 0, 0, 0, 0, 0xf0, 0x7f])))

  state.start = 0
  t.is(enc.float64.decode(state), Infinity)
  t.is(state.start, state.end)

  // Edge cases
  state.start = 0
  state.end = 0
  state.buffer = null

  enc.float64.preencode(state, 0.1 + 0.2)
  state.buffer = b4a.alloc(state.end)
  enc.float64.encode(state, 0.1 + 0.2)
  t.alike(
    state,
    enc.state(8, 8, b4a.from([0x34, 0x33, 0x33, 0x33, 0x33, 0x33, 0xd3, 0x3f]))
  )

  state.start = 0
  t.is(enc.float64.decode(state), 0.1 + 0.2)
  t.is(state.start, state.end)
})

test('biguint64', function (t) {
  const state = enc.state()

  const n = 0x0102030405060708n

  enc.biguint64.preencode(state, n)
  t.alike(state, enc.state(0, 8))

  state.buffer = b4a.alloc(state.end)
  enc.biguint64.encode(state, n)
  t.alike(
    state,
    enc.state(8, 8, b4a.from([0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1]))
  )

  state.start = 0
  t.is(enc.biguint64.decode(state), n)
  t.is(state.start, state.end)

  t.exception(() => enc.biguint64.decode(state))
})

test('bigint64', function (t) {
  const state = enc.state()

  const n = -0x0102030405060708n

  enc.bigint64.preencode(state, n)
  t.alike(state, enc.state(0, 8))

  state.buffer = b4a.alloc(state.end)
  enc.bigint64.encode(state, n)
  t.alike(
    state,
    enc.state(8, 8, b4a.from([0xf, 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2]))
  )

  state.start = 0
  t.is(enc.bigint64.decode(state), n)
  t.is(state.start, state.end)

  t.exception(() => enc.bigint64.decode(state))
})

test('biguint', function (t) {
  const state = enc.state()

  const n = 0x0102030405060708090a0b0cn

  enc.biguint.preencode(state, n)
  t.alike(state, enc.state(0, 17))

  state.buffer = b4a.alloc(state.end)
  enc.biguint.encode(state, n)
  t.alike(
    state,
    enc.state(
      17,
      17,
      b4a.from([
        2, 0xc, 0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0,
        0x0, 0x0
      ])
    )
  )

  state.start = 0
  t.is(enc.biguint.decode(state), n)
  t.is(state.start, state.end)

  t.exception(() => enc.biguint.decode(state))
})

test('bigint', function (t) {
  const state = enc.state()

  const n = -0x0102030405060708090a0b0cn

  enc.bigint.preencode(state, n)
  t.alike(state, enc.state(0, 17))

  state.buffer = b4a.alloc(state.end)
  enc.bigint.encode(state, n)
  t.alike(
    state,
    enc.state(
      17,
      17,
      b4a.from([
        2, 0x17, 0x16, 0x14, 0x12, 0x10, 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0x0,
        0x0, 0x0, 0x0
      ])
    )
  )

  state.start = 0
  t.is(enc.bigint.decode(state), n)
  t.is(state.start, state.end)

  t.exception(() => enc.bigint.decode(state))
})

test('buffer', function (t) {
  const state = enc.state()

  enc.buffer.preencode(state, b4a.from('hi'))
  t.alike(state, enc.state(0, 3))
  enc.buffer.preencode(state, b4a.from('hello'))
  t.alike(state, enc.state(0, 9))
  enc.buffer.preencode(state, null)
  t.alike(state, enc.state(0, 10))

  state.buffer = b4a.alloc(state.end)
  enc.buffer.encode(state, b4a.from('hi'))
  t.alike(
    state,
    enc.state(3, 10, b4a.from('\x02hi\x00\x00\x00\x00\x00\x00\x00'))
  )
  enc.buffer.encode(state, b4a.from('hello'))
  t.alike(state, enc.state(9, 10, b4a.from('\x02hi\x05hello\x00')))
  enc.buffer.encode(state, null)
  t.alike(state, enc.state(10, 10, b4a.from('\x02hi\x05hello\x00')))

  state.start = 0
  t.alike(enc.buffer.decode(state), b4a.from('hi'))
  t.alike(enc.buffer.decode(state), b4a.from('hello'))
  t.is(enc.buffer.decode(state), null)
  t.is(state.start, state.end)

  t.exception(() => enc.buffer.decode(state))
})

test('arraybuffer', function (t) {
  const state = enc.state()

  const b1 = new ArrayBuffer(4)
  b4a.from(b1).fill('a')

  const b2 = new ArrayBuffer(8)
  b4a.from(b2).fill('b')

  enc.arraybuffer.preencode(state, b1)
  t.alike(state, enc.state(0, 5))
  enc.arraybuffer.preencode(state, b2)
  t.alike(state, enc.state(0, 14))

  state.buffer = b4a.alloc(state.end)
  enc.arraybuffer.encode(state, b1)
  t.alike(
    state,
    enc.state(5, 14, b4a.from('\x04aaaa\x00\x00\x00\x00\x00\x00\x00\x00\x00'))
  )
  enc.arraybuffer.encode(state, b2)
  t.alike(state, enc.state(14, 14, b4a.from('\x04aaaa\x08bbbbbbbb')))

  state.start = 0
  t.alike(enc.arraybuffer.decode(state), b1)
  t.alike(enc.arraybuffer.decode(state), b2)
  t.is(state.start, state.end)

  t.exception(() => enc.arraybuffer.decode(state))
})

test('raw', function (t) {
  const state = enc.state()

  enc.raw.preencode(state, b4a.from('hi'))
  t.alike(state, enc.state(0, 2))

  state.buffer = b4a.alloc(state.end)
  enc.raw.encode(state, b4a.from('hi'))
  t.alike(state, enc.state(2, 2, b4a.from('hi')))

  state.start = 0
  t.alike(enc.raw.decode(state), b4a.from('hi'))
  t.is(state.start, state.end)
})

test('raw uint8array', function (t) {
  const state = enc.state()

  enc.raw.uint8array.preencode(state, Uint8Array.of(1, 2))
  t.alike(state, enc.state(0, 2))
  enc.raw.uint8array.preencode(state, Uint8Array.of(3, 4))
  t.alike(state, enc.state(0, 4))

  state.buffer = b4a.alloc(state.end)
  enc.raw.uint8array.encode(state, Uint8Array.of(1, 2))
  t.alike(state, enc.state(2, 4, b4a.from([1, 2, 0, 0])))
  enc.raw.uint8array.encode(state, Uint8Array.of(3, 4))
  t.alike(state, enc.state(4, 4, b4a.from([1, 2, 3, 4])))

  state.start = 0
  t.alike(enc.raw.uint8array.decode(state), Uint8Array.of(1, 2, 3, 4))
  t.alike(enc.raw.uint8array.decode(state), Uint8Array.of())
})

test('uint16array', function (t) {
  const state = enc.state()

  enc.uint16array.preencode(state, new Uint16Array([1, 2, 3]))
  t.alike(state, enc.state(0, 7))

  state.buffer = b4a.alloc(state.end)
  enc.uint16array.encode(state, new Uint16Array([1, 2, 3]))
  t.alike(state, enc.state(7, 7, b4a.from([3, 1, 0, 2, 0, 3, 0])))

  state.start = 0
  t.alike(enc.uint16array.decode(state), new Uint16Array([1, 2, 3]))
  t.is(state.start, state.end)

  t.exception(() => enc.uint16array.decode(state))
})

test('uint32array', function (t) {
  const state = enc.state()

  enc.uint32array.preencode(state, new Uint32Array([1]))
  t.alike(state, enc.state(0, 5))
  enc.uint32array.preencode(state, new Uint32Array([42, 43]))
  t.alike(state, enc.state(0, 14))

  state.buffer = b4a.alloc(state.end)
  enc.uint32array.encode(state, new Uint32Array([1]))
  t.alike(
    state,
    enc.state(5, 14, b4a.from([1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]))
  )
  enc.uint32array.encode(state, new Uint32Array([42, 43]))
  t.alike(
    state,
    enc.state(14, 14, b4a.from([1, 1, 0, 0, 0, 2, 42, 0, 0, 0, 43, 0, 0, 0]))
  )

  state.start = 0
  t.alike(enc.uint32array.decode(state), new Uint32Array([1]))
  t.alike(enc.uint32array.decode(state), new Uint32Array([42, 43]))
  t.is(state.start, state.end)

  t.exception(() => enc.uint32array.decode(state))
})

test('int16array', function (t) {
  const state = enc.state()

  enc.int16array.preencode(state, new Int16Array([1, -2, 3]))
  t.alike(state, enc.state(0, 7))

  state.buffer = b4a.alloc(state.end)
  enc.int16array.encode(state, new Int16Array([1, -2, 3]))
  t.alike(state, enc.state(7, 7, b4a.from([3, 1, 0, 0xfe, 0xff, 3, 0])))

  state.start = 0
  t.alike(enc.int16array.decode(state), new Int16Array([1, -2, 3]))
  t.is(state.start, state.end)

  t.exception(() => enc.int16array.decode(state))
})

test('int32array', function (t) {
  const state = enc.state()

  enc.int32array.preencode(state, new Int32Array([1, -2, 3]))
  t.alike(state, enc.state(0, 13))

  state.buffer = b4a.alloc(state.end)
  enc.int32array.encode(state, new Int32Array([1, -2, 3]))
  t.alike(
    state,
    enc.state(
      13,
      13,
      b4a.from([3, 1, 0, 0, 0, 0xfe, 0xff, 0xff, 0xff, 3, 0, 0, 0])
    )
  )

  state.start = 0
  t.alike(enc.int32array.decode(state), new Int32Array([1, -2, 3]))
  t.is(state.start, state.end)

  t.exception(() => enc.int32array.decode(state))
})

test('biguint64array', function (t) {
  const state = enc.state()

  const arr = new BigUint64Array([0x01020304n, 0x05060708n, 0x090a0b0cn])

  enc.biguint64array.preencode(state, arr)
  t.alike(state, enc.state(0, 25))

  state.buffer = b4a.alloc(state.end)
  enc.biguint64array.encode(state, arr)
  t.alike(
    state,
    enc.state(
      25,
      25,
      b4a.from([
        3, 0x4, 0x3, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x8, 0x7, 0x6, 0x5, 0x0, 0x0,
        0x0, 0x0, 0xc, 0xb, 0xa, 0x9, 0x0, 0x0, 0x0, 0x0
      ])
    )
  )

  state.start = 0
  t.alike(enc.biguint64array.decode(state), arr)
  t.is(state.start, state.end)

  t.exception(() => enc.biguint64array.decode(state))
})

test('bigint64array', function (t) {
  const state = enc.state()

  const arr = new BigInt64Array([-0x01020304n, 0x05060708n, -0x090a0b0cn])

  enc.bigint64array.preencode(state, arr)
  t.alike(state, enc.state(0, 25))

  state.buffer = b4a.alloc(state.end)
  enc.bigint64array.encode(state, arr)
  t.alike(
    state,
    enc.state(
      25,
      25,
      b4a.from([
        3, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x8, 0x7, 0x6, 0x5,
        0x0, 0x0, 0x0, 0x0, 0xf4, 0xf4, 0xf5, 0xf6, 0xff, 0xff, 0xff, 0xff
      ])
    )
  )

  state.start = 0
  t.alike(enc.bigint64array.decode(state), arr)
  t.is(state.start, state.end)

  t.exception(() => enc.bigint64array.decode(state))
})

test('float32array', function (t) {
  const state = enc.state()

  enc.float32array.preencode(state, new Float32Array([1.1, -2.2, 3.3]))
  t.alike(state, enc.state(0, 13))

  state.buffer = b4a.alloc(state.end)
  enc.float32array.encode(state, new Float32Array([1.1, -2.2, 3.3]))
  t.alike(
    state,
    enc.state(
      13,
      13,
      b4a.from([
        3, 0xcd, 0xcc, 0x8c, 0x3f, 0xcd, 0xcc, 0x0c, 0xc0, 0x33, 0x33, 0x53,
        0x40
      ])
    )
  )

  state.start = 0
  t.alike(enc.float32array.decode(state), new Float32Array([1.1, -2.2, 3.3]))
  t.is(state.start, state.end)

  t.exception(() => enc.float32array.decode(state))
})

test('float64array', function (t) {
  const state = enc.state()

  enc.float64array.preencode(state, new Float64Array([1.1, -2.2, 3.3]))
  t.alike(state, enc.state(0, 25))

  state.buffer = b4a.alloc(state.end)
  enc.float64array.encode(state, new Float64Array([1.1, -2.2, 3.3]))
  t.alike(
    state,
    enc.state(
      25,
      25,
      b4a.from([
        3, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xf1, 0x3f, 0x9a, 0x99, 0x99,
        0x99, 0x99, 0x99, 0x01, 0xc0, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x0a,
        0x40
      ])
    )
  )

  state.start = 0
  t.alike(enc.float64array.decode(state), new Float64Array([1.1, -2.2, 3.3]))
  t.is(state.start, state.end)

  t.exception(() => enc.float64array.decode(state))
})

test('string', function (t) {
  const state = enc.state()

  enc.string.preencode(state, '🌾')
  t.alike(state, enc.state(0, 5))
  enc.string.preencode(state, 'høsten er fin')
  t.alike(state, enc.state(0, 20))

  state.buffer = b4a.alloc(state.end)
  enc.string.encode(state, '🌾')
  t.alike(
    state,
    enc.state(
      5,
      20,
      b4a.from(
        '\x04🌾\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
      )
    )
  )
  enc.string.encode(state, 'høsten er fin')
  t.alike(state, enc.state(20, 20, b4a.from('\x04🌾\x0ehøsten er fin')))

  state.start = 0
  t.is(enc.string.decode(state), '🌾')
  t.is(enc.string.decode(state), 'høsten er fin')
  t.is(state.start, state.end)

  t.exception(() => enc.string.decode(state))
})

test('raw string', function (t) {
  const state = enc.state()

  enc.raw.string.preencode(state, 'hello')
  t.alike(state, enc.state(0, 5))
  enc.raw.string.preencode(state, ' world')
  t.alike(state, enc.state(0, 11))

  state.buffer = b4a.alloc(state.end)
  enc.raw.string.encode(state, 'hello')
  enc.raw.string.encode(state, ' world')
  t.alike(state, enc.state(11, 11, b4a.from('hello world')))

  state.start = 0
  t.is(enc.raw.string.decode(state), 'hello world')
  t.is(enc.raw.string.decode(state), '')
})

test('fixed32', function (t) {
  const state = enc.state()

  enc.fixed32.preencode(state, b4a.alloc(32).fill('a'))
  t.alike(state, enc.state(0, 32))
  enc.fixed32.preencode(state, b4a.alloc(32).fill('b'))
  t.alike(state, enc.state(0, 64))

  state.buffer = b4a.alloc(state.end)
  enc.fixed32.encode(state, b4a.alloc(32).fill('a'))
  t.alike(state, enc.state(32, 64, b4a.alloc(64).fill('a', 0, 32)))
  enc.fixed32.encode(state, b4a.alloc(32).fill('b'))
  t.alike(
    state,
    enc.state(64, 64, b4a.alloc(64).fill('a', 0, 32).fill('b', 32, 64))
  )

  state.start = 0
  t.alike(enc.fixed32.decode(state), b4a.alloc(32).fill('a'))
  t.alike(enc.fixed32.decode(state), b4a.alloc(32).fill('b'))
  t.is(state.start, state.end)

  t.exception(() => enc.fixed32.decode(state))
})

test('fixed64', function (t) {
  const state = enc.state()

  enc.fixed64.preencode(state, b4a.alloc(64).fill('a'))
  t.alike(state, enc.state(0, 64))
  enc.fixed64.preencode(state, b4a.alloc(64).fill('b'))
  t.alike(state, enc.state(0, 128))

  state.buffer = b4a.alloc(state.end)
  enc.fixed64.encode(state, b4a.alloc(64).fill('a'))
  t.alike(state, enc.state(64, 128, b4a.alloc(128).fill('a', 0, 64)))
  enc.fixed64.encode(state, b4a.alloc(64).fill('b'))
  t.alike(
    state,
    enc.state(128, 128, b4a.alloc(128).fill('a', 0, 64).fill('b', 64, 128))
  )

  state.start = 0
  t.alike(enc.fixed64.decode(state), b4a.alloc(64).fill('a'))
  t.alike(enc.fixed64.decode(state), b4a.alloc(64).fill('b'))
  t.is(state.start, state.end)

  t.exception(() => enc.fixed64.decode(state))
})

test('fixed n', function (t) {
  const state = enc.state()
  const fixed = enc.fixed(3)

  fixed.preencode(state, b4a.alloc(3).fill('a'))
  t.alike(state, enc.state(0, 3))
  fixed.preencode(state, b4a.alloc(3).fill('b'))
  t.alike(state, enc.state(0, 6))

  state.buffer = b4a.alloc(state.end)
  fixed.encode(state, b4a.alloc(3).fill('a'))
  t.alike(state, enc.state(3, 6, b4a.alloc(6).fill('a', 0, 3)))
  fixed.encode(state, b4a.alloc(3).fill('b'))
  t.alike(state, enc.state(6, 6, b4a.alloc(6).fill('a', 0, 3).fill('b', 3, 6)))

  state.start = 0
  t.alike(fixed.decode(state), b4a.alloc(3).fill('a'))
  t.alike(fixed.decode(state), b4a.alloc(3).fill('b'))
  t.is(state.start, state.end)

  t.exception(() => fixed.decode(state))
  state.start = 4
  t.exception(() => fixed.decode(state))
})

test('error for incorrect buffer sizes when encoding fixed-length buffers', function (t) {
  const smallbuf = b4a.from('aa', 'hex')
  const bigBuf = b4a.from('aa'.repeat(500), 'hex')

  t.exception(() => enc.encode(enc.fixed32, smallbuf), /Incorrect buffer size/)
  t.exception(() => enc.encode(enc.fixed64, smallbuf), /Incorrect buffer size/)
  t.exception(
    () => enc.encode(enc.fixed(100), smallbuf),
    /Incorrect buffer size/
  )

  t.exception(() => enc.encode(enc.fixed32, bigBuf), /Incorrect buffer size/)
  t.exception(() => enc.encode(enc.fixed64, bigBuf), /Incorrect buffer size/)
  t.exception(() => enc.encode(enc.fixed(100), bigBuf), /Incorrect buffer size/)
})

test('array', function (t) {
  const state = enc.state()
  const arr = enc.array(enc.bool)

  arr.preencode(state, [true, false, true])
  t.alike(state, enc.state(0, 4))
  arr.preencode(state, [false, false, true, true])
  t.alike(state, enc.state(0, 9))

  state.buffer = b4a.alloc(state.end)
  arr.encode(state, [true, false, true])
  t.alike(state, enc.state(4, 9, b4a.from([3, 1, 0, 1, 0, 0, 0, 0, 0])))
  arr.encode(state, [false, false, true, true])
  t.alike(state, enc.state(9, 9, b4a.from([3, 1, 0, 1, 4, 0, 0, 1, 1])))

  state.start = 0
  t.alike(arr.decode(state), [true, false, true])
  t.alike(arr.decode(state), [false, false, true, true])
  t.is(state.start, state.end)

  t.exception(() => arr.decode(state))
})

test('raw array', function (t) {
  const state = enc.state()
  const arr = enc.raw.array(enc.bool)

  arr.preencode(state, [true])
  t.alike(state, enc.state(0, 1))
  arr.preencode(state, [true])
  t.alike(state, enc.state(0, 2))

  state.buffer = b4a.alloc(state.end)
  arr.encode(state, [true])
  t.alike(state, enc.state(1, 2, b4a.from([1, 0])))
  arr.encode(state, [true])
  t.alike(state, enc.state(2, 2, b4a.from([1, 1])))

  state.start = 0
  t.alike(arr.decode(state), [true, true])
  t.alike(arr.decode(state), [])
})

test('json', function (t) {
  const state = enc.state()

  enc.json.preencode(state, { a: 1, b: 2 })
  t.alike(state, enc.state(0, 14))

  state.buffer = b4a.alloc(state.end)
  enc.json.encode(state, { a: 1, b: 2 })
  t.alike(
    state,
    enc.state(14, 14, b4a.concat([b4a.from([13]), b4a.from('{"a":1,"b":2}')]))
  )

  state.start = 0
  t.alike(enc.json.decode(state), { a: 1, b: 2 })

  t.exception(() => enc.json.decode(state))
})

test('raw json', function (t) {
  const state = enc.state()

  enc.raw.json.preencode(state, { a: 1, b: 2 })
  t.alike(state, enc.state(0, 13))

  state.buffer = b4a.alloc(state.end)
  enc.raw.json.encode(state, { a: 1, b: 2 })
  t.alike(state, enc.state(13, 13, b4a.from('{"a":1,"b":2}')))

  state.start = 0
  t.alike(enc.raw.json.decode(state), { a: 1, b: 2 })

  t.exception(() => enc.json.decode(state))
})

test('lexint: big numbers', function (t) {
  t.plan(1)

  let prev = enc.encode(enc.lexint, 0)

  let n
  let skip = 1

  for (n = 1; n < Number.MAX_VALUE; n += skip) {
    const cur = enc.encode(enc.lexint, n)
    if (b4a.compare(cur, prev) < 1) break
    prev = cur
    skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256)))
  }
  t.is(n, Infinity)
})

test('lexint: range precision', function (t) {
  t.plan(2)
  const a = 1e55
  const b = 1.0000000000001e55
  const ha = enc.encode(enc.lexint, a).toString('hex')
  const hb = enc.encode(enc.lexint, b).toString('hex')
  t.not(a, b)
  t.not(ha, hb)
})

test('lexint: range precision', function (t) {
  let prev = enc.encode(enc.lexint, 0)
  const skip = 0.000000001e55
  for (let i = 0, n = 1e55; i < 1000; n = 1e55 + skip * ++i) {
    const cur = enc.encode(enc.lexint, n)
    if (b4a.compare(cur, prev) < 1) t.fail('cur <= prev')
    prev = cur
  }
  t.ok(true)
  t.end()
})

test('lexint: small numbers', function (t) {
  let prev = enc.encode(enc.lexint, 0)
  for (let n = 1; n < 256 * 256 * 16; n++) {
    const cur = enc.encode(enc.lexint, n)
    if (b4a.compare(cur, prev) < 1) t.fail('cur <= prev')
    prev = cur
  }
  t.ok(true)
  t.end()
})

test('lexint: throws', function (t) {
  t.exception(() => {
    enc.decode(enc.lexint, b4a.alloc(1, 251))
  })

  let num = 252

  const state = enc.state()

  enc.lexint.preencode(state, num)
  state.buffer = b4a.alloc(state.end - state.start)
  enc.lexint.encode(state, num)

  t.exception(() => {
    enc.decode(
      enc.lexint,
      state.buffer.subarray(0, state.buffer.byteLength - 2)
    )
  })

  num <<= 8

  state.start = 0
  state.end = 0
  state.buffer = null

  enc.lexint.preencode(state, num)
  state.buffer = b4a.alloc(state.end - state.start)
  enc.lexint.encode(state, num)

  t.exception(() => {
    enc.decode(
      enc.lexint,
      state.buffer.subarray(0, state.buffer.byteLength - 2)
    )
  })

  num <<= 8

  state.start = 0
  state.end = 0
  state.buffer = null

  enc.lexint.preencode(state, num)
  state.buffer = b4a.alloc(state.end - state.start)
  enc.lexint.encode(state, num)

  t.exception(() => {
    enc.decode(
      enc.lexint,
      state.buffer.subarray(0, state.buffer.byteLength - 2)
    )
  })

  num *= 256

  state.start = 0
  state.end = 0
  state.buffer = null

  enc.lexint.preencode(state, num)
  state.buffer = b4a.alloc(state.end - state.start)
  enc.lexint.encode(state, num)

  t.exception(() => {
    enc.decode(
      enc.lexint,
      state.buffer.subarray(0, state.buffer.byteLength - 2)
    )
  })

  num *= 256 * 256

  state.start = 0
  state.end = 0
  state.buffer = null

  enc.lexint.preencode(state, num)
  state.buffer = b4a.alloc(state.end - state.start)
  enc.lexint.encode(state, num)

  t.exception(() => {
    enc.decode(
      enc.lexint,
      state.buffer.subarray(0, state.buffer.byteLength - 2)
    )
  })

  t.end()
})

test('lexint: unpack', function (t) {
  let n
  let skip = 1

  for (n = 1; n < Number.MAX_VALUE; n += skip) {
    const cur = enc.encode(enc.lexint, n)
    compare(n, enc.decode(enc.lexint, cur))
    skip = 1 + Math.pow(245, Math.ceil(Math.log(n) / Math.log(256)))
  }
  t.is(n, Infinity)
  t.end()

  function compare(a, b) {
    const desc = a + ' !=~ ' + b
    if (/e\+\d+$/.test(a) || /e\+\d+$/.test(b)) {
      if (
        String(a).slice(0, 8) !== String(b).slice(0, 8) ||
        /e\+(\d+)$/.exec(a)[1] !== /e\+(\d+)$/.exec(b)[1]
      ) {
        t.fail(desc)
      }
    } else {
      if (
        String(a).slice(0, 8) !== String(b).slice(0, 8) ||
        String(a).length !== String(b).length
      ) {
        t.fail(desc)
      }
    }
  }
})

test('date', function (t) {
  const d = new Date()

  t.alike(enc.decode(enc.date, enc.encode(enc.date, d)), d)
})

test('any', function (t) {
  const o = {
    hello: 'world',
    num: 42,
    neg: -42,
    arr: [{ yes: 1 }, { no: false }],
    nest: {},
    today: new Date(),
    float: 0.54
  }

  t.alike(enc.decode(enc.any, enc.encode(enc.any, o)), o)

  const arr = new Array(3)

  t.alike(enc.decode(enc.any, enc.encode(enc.any, arr)), [null, null, null])
})

test('framed', function (t) {
  const e = enc.frame(enc.uint)
  t.alike(enc.encode(e, 42), b4a.from([0x01, 0x2a]))
  t.alike(enc.decode(e, b4a.from([0x01, 0x2a])), 42)
  t.alike(enc.encode(e, 4200), b4a.from([0x03, 0xfd, 0x68, 0x10]))
  t.alike(enc.decode(e, b4a.from([0x03, 0xfd, 0x68, 0x10])), 4200)
})

test('port', function (t) {
  const p = 0x1234
  const buf = Buffer.from([0x34, 0x12])

  t.alike(enc.encode(enc.port, p), buf)
  t.alike(enc.decode(enc.port, buf), p)
})

test('ipv4', function (t) {
  const ip = '1.2.3.4'
  const buf = Buffer.from([1, 2, 3, 4])

  t.alike(enc.encode(enc.ipv4, ip), buf)
  t.alike(enc.decode(enc.ipv4, buf), ip)
})

test('ipv4 + port', function (t) {
  const host = '1.2.3.4'
  const port = 1234

  t.alike(
    enc.decode(enc.ipv4Address, enc.encode(enc.ipv4Address, { host, port })),
    {
      host,
      family: 4,
      port
    }
  )
})

test('ipv6', function (t) {
  const ip = '1:2:3:4:5:6:7:8'
  const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8])

  t.alike(enc.encode(enc.ipv6, ip), buf)
  t.alike(enc.decode(enc.ipv6, buf), ip)

  t.test('abbreviated', function (t) {
    const buf = Buffer.from([0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 8])

    t.alike(enc.encode(enc.ipv6, '1:2::7:8'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '1:2:0:0:0:0:7:8')
  })

  t.test('prefix abbreviated', function (t) {
    const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 7, 0, 8])

    t.alike(enc.encode(enc.ipv6, '::5:6:7:8'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:5:6:7:8')
  })

  t.test('suffix abbreviated', function (t) {
    const buf = Buffer.from([0, 1, 0, 2, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0])

    t.alike(enc.encode(enc.ipv6, '1:2:3:4::'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '1:2:3:4:0:0:0:0')
  })

  t.test('any', function (t) {
    const buf = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

    t.alike(enc.encode(enc.ipv6, '::'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:0')
  })

  t.test('lowercase hex', function (t) {
    const buf = Buffer.from([
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd
    ])

    t.alike(enc.encode(enc.ipv6, '::abcd'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd')
  })

  t.test('uppercase hex', function (t) {
    const buf = Buffer.from([
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xab, 0xcd
    ])

    t.alike(enc.encode(enc.ipv6, '::ABCD'), buf)
    t.alike(enc.decode(enc.ipv6, buf), '0:0:0:0:0:0:0:abcd')
  })
})

test('ipv6 + port', function (t) {
  const host = '1:2:3:4:5:6:7:8'
  const port = 1234

  t.alike(
    enc.decode(enc.ipv6Address, enc.encode(enc.ipv6Address, { host, port })),
    {
      host,
      family: 6,
      port
    }
  )
})

test('dual ip', function (t) {
  {
    const host = '1.2.3.4'

    t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv4')
  }
  {
    const host = '1:2:3:4:5:6:7:8'

    t.alike(enc.decode(enc.ip, enc.encode(enc.ip, host)), host, 'ipv6')
  }
})

test('dual ip + port', function (t) {
  const port = 1234

  {
    const host = '1.2.3.4'

    t.alike(
      enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })),
      {
        host,
        family: 4,
        port
      },
      'ipv4'
    )
  }
  {
    const host = '1:2:3:4:5:6:7:8'

    t.alike(
      enc.decode(enc.ipAddress, enc.encode(enc.ipAddress, { host, port })),
      {
        host,
        family: 6,
        port
      },
      'ipv6'
    )
  }
})

test('record', function (t) {
  const encoding = enc.record(enc.string, enc.string)

  t.alike(
    enc.decode(encoding, enc.encode(encoding, { a: 'hello', b: 'world' })),
    Object.assign(Object.create(null), {
      a: 'hello',
      b: 'world'
    })
  )
})

test('record - nested', function (t) {
  const encoding = enc.record(enc.string, enc.record(enc.string, enc.string))

  t.alike(
    enc.decode(
      encoding,
      enc.encode(encoding, {
        a: { b: 'record' },
        c: { d: 'nested', e: 'test' }
      })
    ),
    Object.assign(Object.create(null), {
      a: Object.assign(Object.create(null), { b: 'record' }),
      c: Object.assign(Object.create(null), { d: 'nested', e: 'test' })
    })
  )
})

test('stringRecord', function (t) {
  t.alike(
    enc.decode(
      enc.stringRecord,
      enc.encode(enc.stringRecord, { a: 'hello', b: 'world' })
    ),
    Object.assign(Object.create(null), {
      a: 'hello',
      b: 'world'
    })
  )
})