Skip to content
This repository was archived by the owner on Aug 24, 2021. It is now read-only.

Commit 4bc2a05

Browse files
Gozalaachingbrain
andauthored
feat: accept Uint8Arrays input in place of Buffers (#88)
Co-authored-by: Alex Potsides <[email protected]>
1 parent aba7d98 commit 4bc2a05

File tree

3 files changed

+87
-58
lines changed

3 files changed

+87
-58
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
"repository": "github:multiformats/js-multihash",
3333
"dependencies": {
3434
"buffer": "^5.6.0",
35-
"multibase": "^1.0.1",
36-
"varint": "^5.0.0"
35+
"multibase": "^2.0.0",
36+
"varint": "^5.0.0",
37+
"web-encoding": "^1.0.2"
3738
},
3839
"devDependencies": {
3940
"aegir": "^24.0.0",
@@ -67,4 +68,4 @@
6768
"node": ">=10.0.0",
6869
"npm": ">=6.0.0"
6970
}
70-
}
71+
}

src/index.js

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @ts-check
12
/* eslint-disable guard-for-in */
23
/**
34
* Multihash implementation in JavaScript.
@@ -10,7 +11,9 @@ const { Buffer } = require('buffer')
1011
const multibase = require('multibase')
1112
const varint = require('varint')
1213
const { names } = require('./constants')
14+
const { TextDecoder } = require('web-encoding')
1315

16+
const textDecoder = new TextDecoder()
1417
const codes = {}
1518

1619
for (const key in names) {
@@ -22,15 +25,19 @@ exports.codes = Object.freeze(codes)
2225
/**
2326
* Convert the given multihash to a hex encoded string.
2427
*
25-
* @param {Buffer} hash
28+
* @param {Uint8Array} hash
2629
* @returns {string}
2730
*/
2831
exports.toHexString = function toHexString (hash) {
29-
if (!Buffer.isBuffer(hash)) {
30-
throw new Error('must be passed a buffer')
32+
if (!(hash instanceof Uint8Array)) {
33+
throw new Error('must be passed a Uint8Array')
3134
}
3235

33-
return hash.toString('hex')
36+
const buffer = Buffer.isBuffer(hash)
37+
? hash
38+
: Buffer.from(hash.buffer, hash.byteOffset, hash.byteLength)
39+
40+
return buffer.toString('hex')
3441
}
3542

3643
/**
@@ -46,42 +53,44 @@ exports.fromHexString = function fromHexString (hash) {
4653
/**
4754
* Convert the given multihash to a base58 encoded string.
4855
*
49-
* @param {Buffer} hash
56+
* @param {Uint8Array} hash
5057
* @returns {string}
5158
*/
5259
exports.toB58String = function toB58String (hash) {
53-
if (!Buffer.isBuffer(hash)) {
54-
throw new Error('must be passed a buffer')
60+
if (!(hash instanceof Uint8Array)) {
61+
throw new Error('must be passed a Uint8Array')
5562
}
5663

57-
return multibase.encode('base58btc', hash).toString().slice(1)
64+
return textDecoder.decode(multibase.encode('base58btc', hash)).slice(1)
5865
}
5966

6067
/**
6168
* Convert the given base58 encoded string to a multihash.
6269
*
63-
* @param {string|Buffer} hash
70+
* @param {string|Uint8Array} hash
6471
* @returns {Buffer}
6572
*/
6673
exports.fromB58String = function fromB58String (hash) {
67-
let encoded = hash
68-
if (Buffer.isBuffer(hash)) {
69-
encoded = hash.toString()
70-
}
74+
const encoded = hash instanceof Uint8Array
75+
? textDecoder.decode(hash)
76+
: hash
7177

7278
return multibase.decode('z' + encoded)
7379
}
7480

7581
/**
7682
* Decode a hash from the given multihash.
7783
*
78-
* @param {Buffer} buf
84+
* @param {Uint8Array} bytes
7985
* @returns {{code: number, name: string, length: number, digest: Buffer}} result
8086
*/
81-
exports.decode = function decode (buf) {
82-
if (!(Buffer.isBuffer(buf))) {
83-
throw new Error('multihash must be a Buffer')
87+
exports.decode = function decode (bytes) {
88+
if (!(bytes instanceof Uint8Array)) {
89+
throw new Error('multihash must be a Uint8Array')
8490
}
91+
let buf = Buffer.isBuffer(bytes)
92+
? bytes
93+
: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)
8594

8695
if (buf.length < 2) {
8796
throw new Error('multihash too short. must be > 2 bytes.')
@@ -116,7 +125,7 @@ exports.decode = function decode (buf) {
116125
*
117126
* > **Note:** the length is derived from the length of the digest itself.
118127
*
119-
* @param {Buffer} digest
128+
* @param {Uint8Array} digest
120129
* @param {string|number} code
121130
* @param {number} [length]
122131
* @returns {Buffer}
@@ -129,8 +138,8 @@ exports.encode = function encode (digest, code, length) {
129138
// ensure it's a hashfunction code.
130139
const hashfn = exports.coerceCode(code)
131140

132-
if (!(Buffer.isBuffer(digest))) {
133-
throw new Error('digest should be a Buffer')
141+
if (!(digest instanceof Uint8Array)) {
142+
throw new Error('digest should be a Uint8Array')
134143
}
135144

136145
if (length == null) {
@@ -141,11 +150,13 @@ exports.encode = function encode (digest, code, length) {
141150
throw new Error('digest length should be equal to specified length.')
142151
}
143152

144-
return Buffer.concat([
145-
Buffer.from(varint.encode(hashfn)),
146-
Buffer.from(varint.encode(length)),
147-
digest
148-
])
153+
const hash = varint.encode(hashfn)
154+
const len = varint.encode(length)
155+
const buffer = Buffer.alloc(hash.length + len.length + digest.length)
156+
buffer.set(hash, 0)
157+
buffer.set(len, hash.length)
158+
buffer.set(digest, hash.length + len.length)
159+
return buffer
149160
}
150161

151162
/**
@@ -206,8 +217,8 @@ exports.isValidCode = function validCode (code) {
206217
/**
207218
* Check if the given buffer is a valid multihash. Throws an error if it is not valid.
208219
*
209-
* @param {Buffer} multihash
210-
* @returns {undefined}
220+
* @param {Uint8Array} multihash
221+
* @returns {void}
211222
* @throws {Error}
212223
*/
213224
function validate (multihash) {
@@ -218,12 +229,12 @@ exports.validate = validate
218229
/**
219230
* Returns a prefix from a valid multihash. Throws an error if it is not valid.
220231
*
221-
* @param {Buffer} multihash
222-
* @returns {undefined}
232+
* @param {Uint8Array} multihash
233+
* @returns {Buffer}
223234
* @throws {Error}
224235
*/
225236
exports.prefix = function prefix (multihash) {
226237
validate(multihash)
227238

228-
return multihash.slice(0, 2)
239+
return Buffer.from(multihash.buffer, multihash.byteOffset, 2)
229240
}

test/index.spec.js

Lines changed: 42 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const mh = require('../src')
1212
const constants = require('../src/constants')
1313
const validCases = require('./fixtures/valid')
1414
const invalidCases = require('./fixtures/invalid')
15+
const { TextEncoder } = require('web-encoding')
1516

1617
function sample (code, size, hex) {
1718
const toHex = (i) => {
@@ -24,12 +25,28 @@ function sample (code, size, hex) {
2425
return Buffer.from(`${toHex(code)}${toHex(size)}${hex}`, 'hex')
2526
}
2627

28+
const they = (description, test) => {
29+
it(`${description} (Buffer)`, () => test({
30+
encodeText: Buffer.from,
31+
encodeHex: (text) => Buffer.from(text, 'hex')
32+
}))
33+
34+
const textEncoder = new TextEncoder()
35+
it(`${description} (Uint8Array)`, () => test({
36+
encodeText: (text) => textEncoder.encode(text),
37+
encodeHex: (text) => {
38+
const { buffer, byteOffset, byteLength } = Buffer.from(text, 'hex')
39+
return new Uint8Array(buffer, byteOffset, byteLength)
40+
}
41+
}))
42+
}
43+
2744
describe('multihash', () => {
2845
describe('toHexString', () => {
29-
it('valid', () => {
46+
they('valid', ({ encodeHex }) => {
3047
validCases.forEach((test) => {
3148
const code = test.encoding.code
32-
const buf = mh.encode(Buffer.from(test.hex, 'hex'), code)
49+
const buf = mh.encode(encodeHex(test.hex), code)
3350
expect(
3451
mh.toHexString(buf)
3552
).to.be.eql(
@@ -42,16 +59,16 @@ describe('multihash', () => {
4259
expect(
4360
() => mh.toHexString('hello world')
4461
).to.throw(
45-
/must be passed a buffer/
62+
/must be passed a Uint8Array/
4663
)
4764
})
4865
})
4966

5067
describe('fromHexString', () => {
51-
it('valid', () => {
68+
they('valid', ({ encodeHex }) => {
5269
validCases.forEach((test) => {
5370
const code = test.encoding.code
54-
const buf = mh.encode(Buffer.from(test.hex, 'hex'), code)
71+
const buf = mh.encode(encodeHex(test.hex), code)
5572
expect(
5673
mh.fromHexString(buf.toString('hex')).toString('hex')
5774
).to.be.eql(
@@ -62,10 +79,10 @@ describe('multihash', () => {
6279
})
6380

6481
describe('toB58String', () => {
65-
it('valid', () => {
82+
they('valid', ({ encodeHex }) => {
6683
validCases.forEach((test) => {
6784
const code = test.encoding.code
68-
const buf = mh.encode(Buffer.from(test.hex, 'hex'), code)
85+
const buf = mh.encode(encodeHex(test.hex), code)
6986
expect(
7087
mh.toB58String(buf)
7188
).to.be.eql(
@@ -78,15 +95,15 @@ describe('multihash', () => {
7895
expect(
7996
() => mh.toB58String('hello world')
8097
).to.throw(
81-
/must be passed a buffer/
98+
/must be passed a Uint8Array/
8299
)
83100
})
84101
})
85102

86103
describe('fromB58String', () => {
87-
it('valid', () => {
104+
they('valid', ({ encodeHex, encodeText }) => {
88105
const src = 'QmPfjpVaf593UQJ9a5ECvdh2x17XuJYG5Yanv5UFnH3jPE'
89-
const expected = Buffer.from('122013bf801597d74a660453412635edd8c34271e5998f801fac5d700c6ce8d8e461', 'hex')
106+
const expected = encodeHex('122013bf801597d74a660453412635edd8c34271e5998f801fac5d700c6ce8d8e461')
90107

91108
expect(
92109
mh.fromB58String(src)
@@ -95,7 +112,7 @@ describe('multihash', () => {
95112
)
96113

97114
expect(
98-
mh.fromB58String(Buffer.from(src))
115+
mh.fromB58String(encodeText(src))
99116
).to.be.eql(
100117
expected
101118
)
@@ -125,20 +142,20 @@ describe('multihash', () => {
125142
expect(
126143
() => mh.decode('hello')
127144
).to.throw(
128-
/multihash must be a Buffer/
145+
/multihash must be a Uint8Array/
129146
)
130147
})
131148
})
132149

133150
describe('encode', () => {
134-
it('valid', () => {
151+
they('valid', ({ encodeHex }) => {
135152
validCases.forEach((test) => {
136153
const code = test.encoding.code
137154
const name = test.encoding.name
138155
const buf = sample(test.encoding.varint || code, test.size, test.hex)
139156
const results = [
140-
mh.encode(Buffer.from(test.hex, 'hex'), code),
141-
mh.encode(Buffer.from(test.hex, 'hex'), name)
157+
mh.encode(encodeHex(test.hex), code),
158+
mh.encode(encodeHex(test.hex), name)
142159
]
143160

144161
results.forEach((res) => {
@@ -151,7 +168,7 @@ describe('multihash', () => {
151168
})
152169
})
153170

154-
it('invalid', () => {
171+
they('invalid', ({ encodeText }) => {
155172
expect(
156173
() => mh.encode()
157174
).to.throw(
@@ -161,11 +178,11 @@ describe('multihash', () => {
161178
expect(
162179
() => mh.encode('hello', 0x11)
163180
).to.throw(
164-
/digest should be a Buffer/
181+
/digest should be a Uint8Array/
165182
)
166183

167184
expect(
168-
() => mh.encode(Buffer.from('hello'), 0x11, 2)
185+
() => mh.encode(encodeText('hello'), 0x11, 2)
169186
).to.throw(
170187
/length should be equal/
171188
)
@@ -188,7 +205,7 @@ describe('multihash', () => {
188205
).to.throw()
189206
})
190207

191-
const longBuffer = Buffer.alloc(150, 'a')
208+
const longBuffer = Uint8Array.from(Buffer.alloc(150, 'a'))
192209
expect(
193210
() => mh.validate(longBuffer)
194211
).to.throw()
@@ -277,7 +294,7 @@ describe('multihash', () => {
277294
})
278295
})
279296

280-
it('invalid', () => {
297+
they('invalid', ({ encodeText }) => {
281298
const invalidNames = [
282299
'sha256',
283300
'sha9',
@@ -293,7 +310,7 @@ describe('multihash', () => {
293310
})
294311

295312
expect(
296-
() => mh.coerceCode(Buffer.from('hello'))
313+
() => mh.coerceCode(encodeText('hello'))
297314
).to.throw(
298315
/should be a number/
299316
)
@@ -306,14 +323,14 @@ describe('multihash', () => {
306323
})
307324
})
308325

309-
it('prefix', () => {
310-
const multihash = mh.encode(Buffer.from('hey'), 0x11, 3)
326+
they('prefix', ({ encodeText }) => {
327+
const multihash = mh.encode(encodeText('hey'), 0x11, 3)
311328
const prefix = mh.prefix(multihash)
312329
expect(prefix.toString('hex')).to.eql('1103')
313330
})
314331

315-
it('prefix throws on invalid multihash', () => {
316-
const multihash = Buffer.from('definitely not valid')
332+
they('prefix throws on invalid multihash', ({ encodeText }) => {
333+
const multihash = encodeText('definitely not valid')
317334

318335
expect(() => mh.prefix(multihash)).to.throw()
319336
})

0 commit comments

Comments
 (0)