@substrate-system/bencode
Version:
Bencode de/encoder
294 lines (255 loc) • 9.1 kB
text/typescript
import { test } from '@substrate-system/tapzero'
import { arr2text } from '@substrate-system/uint8-util'
import bencode from '../src/index.js'
import data from './data.js'
bencode.encode._floatConversionDetected = true
test('should always return a Buffer', function (t) {
t.plan(5)
t.ok(ArrayBuffer.isView(bencode.encode({})), 'its a buffer for empty dicts')
t.ok(ArrayBuffer.isView(bencode.encode('test')), 'its a buffer for strings')
t.ok(ArrayBuffer.isView(bencode.encode([3, 2])), 'its a buffer for lists')
t.ok(ArrayBuffer.isView(bencode.encode({ a: 'b', 3: 6 })),
'its a buffer for big dicts')
t.ok(ArrayBuffer.isView(bencode.encode(123)), 'its a buffer for numbers')
})
test('should sort dictionaries', function (t) {
t.plan(1)
const data = { string: 'Hello World', integer: 12345 }
t.equal(
Buffer.from(bencode.encode(data)!).toString(),
'd7:integeri12345e6:string11:Hello Worlde'
)
})
test('should force keys to be strings', function (t) {
t.plan(1)
const data = {
12: 'Hello World',
34: 12345
}
t.equal(
Buffer.from(bencode.encode(data)!).toString(),
'd2:1211:Hello World2:34i12345ee'
)
})
test('should encode a Map as dictionary', function (t) {
t.plan(1)
const data = new Map<string|number|Buffer, string|number|Buffer>([
[12, 'Hello World'],
['34', 12345],
[Buffer.from('buffer key'), Buffer.from('buffer value')]
])
t.equal(
Buffer.from(bencode.encode(data)!).toString(),
'd2:1211:Hello World2:34i12345e10:buffer key12:buffer valuee'
)
})
test('should be able to encode a positive integer', function (t) {
t.plan(1)
t.equal(Buffer.from(bencode.encode(123)!).toString(), 'i123e')
})
test('should be able to encode a negative integer', function (t) {
t.plan(1)
t.equal(Buffer.from(bencode.encode(-123)!).toString(), 'i-123e')
})
test('should be able to encode a positive float (as int)', function (t) {
t.plan(1)
t.equal(Buffer.from(bencode.encode(123.5)!).toString(), 'i123e')
})
test('should be able to encode a negative float (as int)', function (t) {
t.plan(1)
t.equal(Buffer.from(bencode.encode(-123.5)!).toString(), 'i-123e')
})
test(
'should be able to safely encode numbers between -/+ 2 ^ 53 (as ints)',
(t) => {
const JAVASCRIPT_INT_BITS = 53
const MAX_JAVASCRIPT_INT = Math.pow(2, JAVASCRIPT_INT_BITS)
t.plan((JAVASCRIPT_INT_BITS - 1) * 6 + 3)
t.equal(Buffer.from(bencode.encode(0)!).toString(), 'i' + 0 + 'e')
for (let exp = 1; exp < JAVASCRIPT_INT_BITS; ++exp) {
const val = Math.pow(2, exp)
// try the positive and negative
t.equal(Buffer.from(bencode.encode(val)!).toString(), 'i' + val + 'e')
t.equal(Buffer.from(bencode.encode(-val)!).toString(), 'i-' + val + 'e')
// try the value, one above and one below, both positive and negative
const above = val + 1
const below = val - 1
t.equal(Buffer.from(bencode.encode(above)!).toString(), 'i' + above + 'e')
t.equal(Buffer.from(bencode.encode(-above)!).toString(), 'i-' + above + 'e')
t.equal(Buffer.from(bencode.encode(below)!).toString(), 'i' + below + 'e')
t.equal(Buffer.from(bencode.encode(-below)!).toString(), 'i-' + below + 'e')
}
t.equal(
Buffer.from(bencode.encode(MAX_JAVASCRIPT_INT)!).toString(),
'i' + MAX_JAVASCRIPT_INT + 'e'
)
t.equal(
Buffer.from(bencode.encode(-MAX_JAVASCRIPT_INT)!).toString(),
'i-' + MAX_JAVASCRIPT_INT + 'e'
)
}
)
test('should be able to encode a previously problematice 64 bit int', function (t) {
t.plan(1)
t.equal(
Buffer.from(bencode.encode(2433088826)!).toString(),
'i' + 2433088826 + 'e'
)
})
test('should be able to encode a negative 64 bit int', function (t) {
t.plan(1)
t.equal(
Buffer.from(bencode.encode(-0xffffffff)!).toString(),
'i-' + 0xffffffff + 'e'
)
})
test('should be able to encode a positive 64 bit float (as int)', function (t) {
t.plan(1)
t.equal(
Buffer.from(bencode.encode(0xffffffff + 0.5)!).toString(),
'i' + 0xffffffff + 'e'
)
})
test('should be able to encode a negative 64 bit float (as int)', function (t) {
t.plan(1)
t.equal(
Buffer.from(bencode.encode(-0xffffffff - 0.5)!).toString(),
'i-' + 0xffffffff + 'e'
)
})
test('should be able to encode a string', function (t) {
t.plan(2)
t.equal(
Buffer.from(bencode.encode('asdf')!).toString(),
'4:asdf'
)
t.equal(
Buffer.from(bencode.encode(':asdf:')!).toString(),
'6::asdf:'
)
})
test('should be able to encode a unicode string', function (t) {
t.plan(2)
t.deepEqual(
bencode.encode(data.binStringData.toString()),
new Uint8Array(data.binResultData)
)
t.deepEqual(
bencode.encode(data.binStringData.toString()),
new Uint8Array(data.binResultData)
)
})
test('should be able to encode a buffer', function (t) {
t.plan(2)
t.equal(
Buffer.from(bencode.encode(Buffer.from('asdf'))!).toString(),
'4:asdf'
)
t.equal(
Buffer.from(bencode.encode(Buffer.from(':asdf:'))!).toString(),
'6::asdf:'
)
})
test('should be able to encode an array', function (t) {
t.plan(2)
t.equal(
Buffer.from(bencode.encode([32, 12])!).toString(),
'li32ei12ee'
)
t.equal(
Buffer.from(bencode.encode([':asdf:'])!).toString(),
'l6::asdf:e'
)
})
test('should be able to encode a Set as a list', function (t) {
t.plan(2)
t.equal(
Buffer.from(bencode.encode(new Set([32, 12]))!).toString(),
'li32ei12ee'
)
t.equal(
Buffer.from(bencode.encode(new Set([':asdf:']))!).toString(),
'l6::asdf:e'
)
})
test('should be able to encode an object', function (t) {
t.plan(3)
t.equal(
Buffer.from(bencode.encode({ a: 'bc' })!).toString(),
'd1:a2:bce'
)
t.equal(
Buffer.from(bencode.encode({ a: '45', b: 45 })!).toString(),
'd1:a2:451:bi45ee'
)
t.equal(
Buffer.from(bencode.encode({ a: Buffer.from('bc') })!).toString(),
'd1:a2:bce'
)
})
test('should encode new Number(1) as number', function (t) {
t.plan(1)
const data = new Number(1) // eslint-disable-line
const result = bencode.decode(bencode.encode(data)!)
const expected = 1
t.equal(result, expected)
})
test('should encode new Boolean(true) as number', function (t) {
t.plan(1)
const data = new Boolean(true) // eslint-disable-line
const result = bencode.decode(bencode.encode(data)!)
const expected = 1
t.equal(result, expected)
})
test('should encode Uint8Array as buffer', function (t) {
t.plan(1)
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])
const result = bencode.decode(bencode.encode(data)!)
const expected = new Uint8Array(data.buffer)
t.deepEqual(result, expected)
})
test('should encode Uint32Array as buffer', function (t) {
t.plan(1)
const data = new Uint32Array([0xF, 0xFF, 0xFFF, 0xFFFF, 0xFFFFF, 0xFFFFFF, 0xFFFFFFF, 0xFFFFFFFF])
const result = bencode.decode(bencode.encode(data)!)
const expected = new Uint8Array(data.buffer)
t.deepEqual(result, expected)
})
test('should encode ArrayBuffer as buffer', function (t) {
t.plan(1)
const data = new Uint32Array([0xF, 0xFF, 0xFFF, 0xFFFF, 0xFFFFF, 0xFFFFFF, 0xFFFFFFF, 0xFFFFFFFF])
const result = bencode.decode(bencode.encode(data.buffer)!)
const expected = new Uint8Array(data.buffer)
t.deepEqual(result, expected)
})
test('should encode Float32Array as buffer', function (t) {
t.plan(1)
const data = new Float32Array([1.2, 2.3, 3.4, 4.5, 5.6, 6.7, 7.8, 8.9, 9.0])
const result = bencode.decode(bencode.encode(data)!)
const expected = new Uint8Array(data.buffer)
t.deepEqual(result, expected)
})
test('should encode DataView as buffer', function (t) {
t.plan(1)
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])
const view = new DataView(data.buffer)
const result = bencode.decode(bencode.encode(view)!)
const expected = new Uint8Array(data.buffer)
t.deepEqual(result, expected)
})
test('should encode Uint8Array subarray properly', function (t) {
t.plan(1)
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])
const subData = data.subarray(5)
const result = bencode.decode(bencode.encode(subData)!)
const expected = new Uint8Array(subData.buffer, subData.byteOffset, subData.byteLength)
t.deepEqual(result, expected)
})
test('should encode large numbers with full digits', function (t) {
t.plan(1)
const data = 340282366920938463463374607431768211456
t.deepEqual(
arr2text(bencode.encode(data)!),
'i340282366920938463463374607431768211456e'
)
})