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

Commit

Permalink
fix: replace node buffers with uint8arrays (#92)
Browse files Browse the repository at this point in the history
Removes node `Buffer`s in favour of `Uint8Array`s.

BREAKING CHANGE:

- Where node `Buffer`s were returned, now `Uint8Array`s are
  • Loading branch information
achingbrain authored Aug 3, 2020
1 parent b90be95 commit b5dbaca
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 77 deletions.
21 changes: 10 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
js-multihash
============
# js-multihash <!-- omit in toc -->

[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
[![](https://img.shields.io/badge/project-multiformats-blue.svg?style=flat-square)](https://github.com/multiformats/multiformats)
Expand All @@ -17,18 +16,18 @@ It is extended by [js-multihashing](https://github.com/multiformats/js-multihash
and [js-multihashing-async](https://github.com/multiformats/js-multihashing-async),
so give those a look as well.

## Lead Maintainer
## Lead Maintainer <!-- omit in toc -->

[Hugo Dias](http://github.com/hugomrdias/)

## Table of Contents
## Table of Contents <!-- omit in toc -->

- [Install](#install)
- [In Node.js through npm](#in-nodejs-through-npm)
- [Browser: Browserify, Webpack, other bundlers](#browser-browserify-webpack-other-bundlers)
- [In the Browser through `<script>` tag](#in-the-browser-through-script-tag)
- [Using npm](#using-npm)
- [Using a `<script>` tag](#using-a-script-tag)
- [Usage](#usage)
- [API](#api)
- [Update Constants](#update-constants)
- [Contribute](#contribute)
- [License](#license)

Expand Down Expand Up @@ -60,17 +59,17 @@ Loading this module through a script tag will make the ```Multihashes``` obj ava

```js
> var multihash = require('multihashes')
> var buf = new Buffer('0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', 'hex')
> var bytes = Uint8Array.from([0, 1, 2, 3...])

> var encoded = multihash.encode(buf, 'sha1')
> var encoded = multihash.encode(bytes, 'sha1')
> console.log(encoded)
<Buffer 11 14 0b ee c7 b5 ea 3f 0f db c9 5d 0d d4 7f 3c 5b c2 75 da 8a 33>
<Uint8Array 11 14 0b ee c7 b5 ea 3f 0f db c9 5d 0d d4 7f 3c 5b c2 75 da 8a 33>

> multihash.decode(encoded)
{ code: 17,
name: 'sha1',
length: 20,
digest: <Buffer 0b ee c7 b5 ea 3f 0f db c9 5d 0d d4 7f 3c 5b c2 75 da 8a 33> }
digest: <Uint8Array 0b ee c7 b5 ea 3f 0f db c9 5d 0d d4 7f 3c 5b c2 75 da 8a 33> }
```

## API
Expand Down
11 changes: 4 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,12 @@
"main": "src/index.js",
"repository": "github:multiformats/js-multihash",
"dependencies": {
"buffer": "^5.6.0",
"multibase": "^2.0.0",
"varint": "^5.0.0",
"web-encoding": "^1.0.2"
"multibase": "^3.0.0",
"uint8arrays": "^1.0.0",
"varint": "^5.0.0"
},
"devDependencies": {
"aegir": "^24.0.0",
"chai": "^4.1.2",
"dirty-chai": "^2.0.1",
"aegir": "^25.0.0",
"ipfs-utils": "^2.3.1"
},
"contributors": [
Expand Down
50 changes: 20 additions & 30 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
*/
'use strict'

const { Buffer } = require('buffer')
const multibase = require('multibase')
const varint = require('varint')
const { names } = require('./constants')
const { TextDecoder } = require('web-encoding')
const uint8ArrayToString = require('uint8arrays/to-string')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayConcat = require('uint8arrays/concat')

const textDecoder = new TextDecoder()
const codes = {}
Expand All @@ -33,21 +34,17 @@ exports.toHexString = function toHexString (hash) {
throw new Error('must be passed a Uint8Array')
}

const buffer = Buffer.isBuffer(hash)
? hash
: Buffer.from(hash.buffer, hash.byteOffset, hash.byteLength)

return buffer.toString('hex')
return uint8ArrayToString(hash, 'base16')
}

/**
* Convert the given hex encoded string to a multihash.
*
* @param {string} hash
* @returns {Buffer}
* @returns {Uint8Array}
*/
exports.fromHexString = function fromHexString (hash) {
return Buffer.from(hash, 'hex')
return uint8ArrayFromString(hash, 'base16')
}

/**
Expand All @@ -68,7 +65,7 @@ exports.toB58String = function toB58String (hash) {
* Convert the given base58 encoded string to a multihash.
*
* @param {string|Uint8Array} hash
* @returns {Buffer}
* @returns {Uint8Array}
*/
exports.fromB58String = function fromB58String (hash) {
const encoded = hash instanceof Uint8Array
Expand All @@ -82,41 +79,38 @@ exports.fromB58String = function fromB58String (hash) {
* Decode a hash from the given multihash.
*
* @param {Uint8Array} bytes
* @returns {{code: number, name: string, length: number, digest: Buffer}} result
* @returns {{code: number, name: string, length: number, digest: Uint8Array}} result
*/
exports.decode = function decode (bytes) {
if (!(bytes instanceof Uint8Array)) {
throw new Error('multihash must be a Uint8Array')
}
let buf = Buffer.isBuffer(bytes)
? bytes
: Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength)

if (buf.length < 2) {
if (bytes.length < 2) {
throw new Error('multihash too short. must be > 2 bytes.')
}

const code = varint.decode(buf)
const code = varint.decode(bytes)
if (!exports.isValidCode(code)) {
throw new Error(`multihash unknown function code: 0x${code.toString(16)}`)
}
buf = buf.slice(varint.decode.bytes)
bytes = bytes.slice(varint.decode.bytes)

const len = varint.decode(buf)
const len = varint.decode(bytes)
if (len < 0) {
throw new Error(`multihash invalid length: ${len}`)
}
buf = buf.slice(varint.decode.bytes)
bytes = bytes.slice(varint.decode.bytes)

if (buf.length !== len) {
throw new Error(`multihash length inconsistent: 0x${buf.toString('hex')}`)
if (bytes.length !== len) {
throw new Error(`multihash length inconsistent: 0x${uint8ArrayToString(bytes, 'base16')}`)
}

return {
code,
name: codes[code],
length: len,
digest: buf
digest: bytes
}
}

Expand All @@ -128,7 +122,7 @@ exports.decode = function decode (bytes) {
* @param {Uint8Array} digest
* @param {string|number} code
* @param {number} [length]
* @returns {Buffer}
* @returns {Uint8Array}
*/
exports.encode = function encode (digest, code, length) {
if (!digest || code === undefined) {
Expand All @@ -152,11 +146,7 @@ exports.encode = function encode (digest, code, length) {

const hash = varint.encode(hashfn)
const len = varint.encode(length)
const buffer = Buffer.alloc(hash.length + len.length + digest.length)
buffer.set(hash, 0)
buffer.set(len, hash.length)
buffer.set(digest, hash.length + len.length)
return buffer
return uint8ArrayConcat([hash, len, digest], hash.length + len.length + digest.length)
}

/**
Expand Down Expand Up @@ -230,11 +220,11 @@ exports.validate = validate
* Returns a prefix from a valid multihash. Throws an error if it is not valid.
*
* @param {Uint8Array} multihash
* @returns {Buffer}
* @returns {Uint8Array}
* @throws {Error}
*/
exports.prefix = function prefix (multihash) {
validate(multihash)

return Buffer.from(multihash.buffer, multihash.byteOffset, 2)
return multihash.subarray(0, 2)
}
47 changes: 18 additions & 29 deletions test/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@
/* eslint max-nested-callbacks: off */
'use strict'

const chai = require('chai')
const dirtyChai = require('dirty-chai')
chai.use(dirtyChai)
const expect = chai.expect
const { expect } = require('aegir/utils/chai')
const multibase = require('multibase')
const { Buffer } = require('buffer')
const mh = require('../src')
const constants = require('../src/constants')
const validCases = require('./fixtures/valid')
const invalidCases = require('./fixtures/invalid')
const { TextEncoder } = require('web-encoding')
const uint8ArrayToString = require('uint8arrays/to-string')
const uint8ArrayFromString = require('uint8arrays/from-string')
const uint8ArrayEquals = require('uint8arrays/equals')

function sample (code, size, hex) {
const toHex = (i) => {
Expand All @@ -22,22 +20,13 @@ function sample (code, size, hex) {
const h = i.toString(16)
return h.length % 2 === 1 ? `0${h}` : h
}
return Buffer.from(`${toHex(code)}${toHex(size)}${hex}`, 'hex')
return uint8ArrayFromString(`${toHex(code)}${toHex(size)}${hex}`, 'base16')
}

const they = (description, test) => {
it(`${description} (Buffer)`, () => test({
encodeText: Buffer.from,
encodeHex: (text) => Buffer.from(text, 'hex')
}))

const textEncoder = new TextEncoder()
it(`${description} (Uint8Array)`, () => test({
encodeText: (text) => textEncoder.encode(text),
encodeHex: (text) => {
const { buffer, byteOffset, byteLength } = Buffer.from(text, 'hex')
return new Uint8Array(buffer, byteOffset, byteLength)
}
it(description, () => test({
encodeText: (text) => uint8ArrayFromString(text),
encodeHex: (text) => uint8ArrayFromString(text, 'base16')
}))
}

Expand All @@ -50,7 +39,7 @@ describe('multihash', () => {
expect(
mh.toHexString(buf)
).to.be.eql(
buf.toString('hex')
uint8ArrayToString(buf, 'base16')
)
})
})
Expand All @@ -70,9 +59,9 @@ describe('multihash', () => {
const code = test.encoding.code
const buf = mh.encode(encodeHex(test.hex), code)
expect(
mh.fromHexString(buf.toString('hex')).toString('hex')
uint8ArrayToString(mh.fromHexString(uint8ArrayToString(buf, 'base16')), 'base16')
).to.be.eql(
buf.toString('hex')
uint8ArrayToString(buf, 'base16')
)
})
})
Expand All @@ -86,7 +75,7 @@ describe('multihash', () => {
expect(
mh.toB58String(buf)
).to.be.eql(
multibase.encode('base58btc', buf).toString().slice(1)
uint8ArrayToString(multibase.encode('base58btc', buf)).slice(1)
)
})
})
Expand Down Expand Up @@ -125,7 +114,7 @@ describe('multihash', () => {
const code = test.encoding.code
const buf = sample(test.encoding.varint || code, test.size, test.hex)
const name = test.encoding.name
const d1 = Buffer.from(test.hex, 'hex')
const d1 = uint8ArrayFromString(test.hex, 'base16')
const length = d1.length

const r = mh.decode(buf)
Expand All @@ -134,7 +123,7 @@ describe('multihash', () => {
expect(r.code).to.equal(code)
expect(r.name).to.equal(name)
expect(r.length).to.equal(length)
expect(d1.equals(d2)).to.equal(true)
expect(uint8ArrayEquals(d1, d2)).to.equal(true)
})
})

Expand All @@ -160,9 +149,9 @@ describe('multihash', () => {

results.forEach((res) => {
expect(
res.toString('hex')
uint8ArrayToString(res, 'base16')
).to.be.eql(
buf.toString('hex')
uint8ArrayToString(buf, 'base16')
)
})
})
Expand Down Expand Up @@ -205,7 +194,7 @@ describe('multihash', () => {
).to.throw()
})

const longBuffer = Uint8Array.from(Buffer.alloc(150, 'a'))
const longBuffer = new Uint8Array(150).fill(0)
expect(
() => mh.validate(longBuffer)
).to.throw()
Expand Down Expand Up @@ -326,7 +315,7 @@ describe('multihash', () => {
they('prefix', ({ encodeText }) => {
const multihash = mh.encode(encodeText('hey'), 0x11, 3)
const prefix = mh.prefix(multihash)
expect(prefix.toString('hex')).to.eql('1103')
expect(uint8ArrayToString(prefix, 'base16')).to.eql('1103')
})

they('prefix throws on invalid multihash', ({ encodeText }) => {
Expand Down

0 comments on commit b5dbaca

Please sign in to comment.