Skip to content

Commit d5f6d03

Browse files
author
Alan Shaw
committed
feat: encode + tests
1 parent 7090f5d commit d5f6d03

File tree

5 files changed

+123
-100
lines changed

5 files changed

+123
-100
lines changed

.circleci/config.yml

Lines changed: 0 additions & 15 deletions
This file was deleted.

package-lock.json

Lines changed: 31 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "pull-length-prefixed",
2+
"name": "it-length-prefixed",
33
"version": "1.3.2",
4-
"description": "Streaming length prefixed buffers with pull-streams",
4+
"description": "Streaming length prefixed buffers with async iterables",
55
"main": "src/index.js",
66
"scripts": {
77
"test": "aegir test -t node -t browser",
@@ -12,44 +12,40 @@
1212
"release-minor": "aegir release --type minor -t node -t browser",
1313
"release-major": "aegir release --type major -t node -t browser",
1414
"build": "aegir build",
15-
"coverage": "npx nyc -s npm run test:node -- --bail",
15+
"coverage": "npx nyc --reporter=text --reporter=lcov npm run test:node -- --bail",
1616
"coverage-publish": "npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov"
1717
},
1818
"repository": {
1919
"type": "git",
20-
"url": "git+https://github.com/dignifiedquire/pull-length-prefixed.git"
20+
"url": "git+https://github.com/alanshaw/it-length-prefixed.git"
2121
},
2222
"keywords": [
2323
"varint",
24-
"pull-stream",
24+
"async",
25+
"iterable",
26+
"iterator",
2527
"length-prefixed-stream",
2628
"length-prefixed"
2729
],
28-
"author": "Friedel Ziegelmayer <dignifiedquire@gmail.com>",
30+
"author": "Alan Shaw",
2931
"license": "MIT",
3032
"bugs": {
31-
"url": "https://github.com/dignifiedquire/pull-length-prefixed/issues"
33+
"url": "https://github.com/alanshaw/it-length-prefixed/issues"
3234
},
33-
"homepage": "https://github.com/dignifiedquire/pull-length-prefixed#readme",
35+
"homepage": "https://github.com/alanshaw/it-length-prefixed#readme",
3436
"dependencies": {
3537
"pull-pushable": "^2.2.0",
3638
"pull-reader": "^1.3.1",
37-
"safe-buffer": "^5.1.2",
3839
"varint": "^5.0.0"
3940
},
4041
"devDependencies": {
4142
"aegir": "^18.2.1",
4243
"chai": "^4.2.0",
44+
"it-pipe": "^1.0.1",
4345
"pull-block": "^1.4.0",
44-
"pull-stream": "^3.6.9"
45-
},
46-
"contributors": [
47-
"Dmitriy Ryajov <dryajov@gmail.com>",
48-
"Jacob Heun <jacobheun@gmail.com>",
49-
"Jacob Heun <jake@andyet.net>",
50-
"Maciej Krüger <mkg20001@gmail.com>",
51-
"Projjol Banerji <probaner23@gmail.com>",
52-
"Richard Littauer <richard.littauer@gmail.com>",
53-
"dignifiedquire <dignifiedquire@gmail.com>"
54-
]
46+
"pull-stream": "^3.6.10",
47+
"random-bytes": "^1.0.0",
48+
"random-int": "^2.0.0",
49+
"streaming-iterables": "^4.1.0"
50+
}
5551
}

src/encode.js

Lines changed: 24 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,35 @@
11
'use strict'
22

3-
const Buffer = require('safe-buffer').Buffer
3+
const Varint = require('varint')
4+
const BufferList = require('bl')
45

5-
module.exports = encode
6-
7-
const poolSize = 10 * 1024
8-
9-
function encode (opts) {
10-
opts = Object.assign({
11-
fixed: false
12-
}, opts || {})
13-
14-
// Only needed for varint
15-
const varint = require('varint')
16-
let pool = opts.fixed ? null : createPool()
17-
let used = 0
18-
19-
let ended = false
20-
let first = true
21-
22-
return (read) => (end, cb) => {
23-
if (end) ended = end
24-
if (ended) return cb(ended)
25-
26-
read(null, (end, data) => {
27-
if (end) ended = end
28-
if (ended && !first) {
29-
return cb(ended)
30-
}
31-
32-
first = false
6+
const MIN_POOL_SIZE = 147 // Varint.encode(Number.MAX_VALUE).length
7+
const DEFAULT_POOL_SIZE = 10 * 1024
338

34-
if (!ended && !Buffer.isBuffer(data)) {
35-
ended = new Error('data must be a buffer')
36-
return cb(ended)
37-
}
9+
function encode (options) {
10+
options = options || {}
11+
options.poolSize = Math.max(options.poolSize || DEFAULT_POOL_SIZE, MIN_POOL_SIZE)
3812

39-
const dataLength = ended ? 0 : data.length
13+
return source => (async function * () {
14+
let pool = Buffer.alloc(options.poolSize)
15+
let poolOffset = 0
4016

41-
let encodedLength
42-
if (opts.fixed) {
43-
encodedLength = Buffer.alloc(4)
44-
encodedLength.writeInt32BE(dataLength, 0) // writes exactly 4 bytes
45-
} else {
46-
varint.encode(dataLength, pool, used)
47-
used += varint.encode.bytes
48-
encodedLength = pool.slice(used - varint.encode.bytes, used)
17+
for await (const chunk of source) {
18+
Varint.encode(chunk.length, pool, poolOffset)
19+
poolOffset += Varint.encode.bytes
20+
const encodedLength = pool.slice(poolOffset - Varint.encode.bytes, poolOffset)
4921

50-
if (pool.length - used < 100) {
51-
pool = createPool()
52-
used = 0
53-
}
22+
if (pool.length - poolOffset < MIN_POOL_SIZE) {
23+
pool = Buffer.alloc(options.poolSize)
24+
poolOffset = 0
5425
}
5526

56-
if (ended) {
57-
return cb(null, encodedLength)
58-
}
59-
60-
cb(null, Buffer.concat([
61-
encodedLength,
62-
data
63-
], (opts.fixed ? 4 : varint.encode.bytes) + dataLength))
64-
})
65-
}
27+
yield new BufferList().append(encodedLength).append(chunk)
28+
// yield Buffer.concat([encodedLength, chunk])
29+
}
30+
})()
6631
}
6732

68-
function createPool () {
69-
return Buffer.alloc(poolSize)
70-
}
33+
module.exports = encode
34+
module.exports.MIN_POOL_SIZE = MIN_POOL_SIZE
35+
module.exports.DEFAULT_POOL_SIZE = DEFAULT_POOL_SIZE

test/encode.spec.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const pipe = require('it-pipe')
5+
const { expect } = require('chai')
6+
const randomInt = require('random-int')
7+
const randomBytes = require('random-bytes')
8+
const { map, collect } = require('streaming-iterables')
9+
const Varint = require('varint')
10+
11+
const lp = require('../')
12+
const { MIN_POOL_SIZE } = lp.encode
13+
14+
const times = (n, fn) => Array.from(Array(n), fn)
15+
const someBytes = n => randomBytes(randomInt(1, n || 32))
16+
17+
describe('encode', () => {
18+
it('should encode length as prefix', async () => {
19+
const input = await Promise.all(times(randomInt(1, 10), someBytes))
20+
const output = await pipe(input, lp.encode(), map(c => c.slice()), collect)
21+
output.forEach((o, i) => {
22+
const length = Varint.decode(o)
23+
expect(length).to.equal(input[i].length)
24+
expect(o.slice(Varint.decode.bytes)).to.deep.equal(input[i])
25+
})
26+
})
27+
28+
it('should encode zero length as prefix', async () => {
29+
const input = [Buffer.alloc(0)]
30+
const output = await pipe(input, lp.encode(), map(c => c.slice()), collect)
31+
output.forEach((o, i) => {
32+
const length = Varint.decode(o)
33+
expect(length).to.equal(input[i].length)
34+
expect(o.slice(Varint.decode.bytes)).to.deep.equal(input[i])
35+
})
36+
})
37+
38+
it('should re-allocate buffer pool when empty', async () => {
39+
const input = await Promise.all(times(MIN_POOL_SIZE * 2, someBytes))
40+
const output = await pipe(
41+
input,
42+
lp.encode({ poolSize: MIN_POOL_SIZE * 1.5 }),
43+
map(c => c.slice()),
44+
collect
45+
)
46+
output.forEach((o, i) => {
47+
const length = Varint.decode(o)
48+
expect(length).to.equal(input[i].length)
49+
expect(o.slice(Varint.decode.bytes)).to.deep.equal(input[i])
50+
})
51+
})
52+
})

0 commit comments

Comments
 (0)