From ec08b0aa6a02eb509e2064948944834524e97c6a Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Wed, 3 Jun 2020 18:23:22 +0100 Subject: [PATCH 01/11] feat: more encoding, errors, spec tests - adds support for all the encoding in https://github.com/multiformats/multibase/blob/master/multibase.csv - better errors showing the invalid chars and inputs - `names` and `codes` export the full object that maps names/codes to base instances - two news methods exported, `encoding` and `encodingFromData` - added all the spec tests https://github.com/multiformats/multibase/tree/master/tests This module now only uses 2 base encoding implementations, 1 generalised rfc4648 and 1 generalised btc like. Note: `base8` deviates from the spec tests outputs but aligns with https://github.com/multiformats/multibase/pull/60 --- README.md | 114 ++++++++-------------------------------- benchmark.js | 23 ++++++++ package.json | 1 + src/base.js | 22 ++++---- src/base16.js | 21 -------- src/base32.js | 81 ---------------------------- src/base64.js | 44 ---------------- src/constants.js | 50 +++++++++++------- src/index.js | 109 ++++++++++++++++++++------------------ src/rfc4648.js | 92 ++++++++++++++++++++++++++++++++ test/constants.spec.js | 4 +- test/multibase.spec.js | 9 ++-- test/spec-test1.spec.js | 76 +++++++++++++++++++++++++++ test/spec-test2.spec.js | 59 +++++++++++++++++++++ test/spec-test3.spec.js | 59 +++++++++++++++++++++ test/spec-test4.spec.js | 59 +++++++++++++++++++++ test/spec-test5.spec.js | 58 ++++++++++++++++++++ test/spec-test6.spec.js | 35 ++++++++++++ 18 files changed, 590 insertions(+), 326 deletions(-) create mode 100644 benchmark.js delete mode 100644 src/base16.js delete mode 100644 src/base32.js delete mode 100644 src/base64.js create mode 100644 src/rfc4648.js create mode 100644 test/spec-test1.spec.js create mode 100644 test/spec-test2.spec.js create mode 100644 test/spec-test3.spec.js create mode 100644 test/spec-test4.spec.js create mode 100644 test/spec-test5.spec.js create mode 100644 test/spec-test6.spec.js diff --git a/README.md b/README.md index 26aeb80..0e61779 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,8 @@ js-multibase ## Table of Contents - [Install](#install) - - [In Node.js through npm](#in-nodejs-through-npm) - - [Browser: Browserify, Webpack, other bundlers](#browser-browserify-webpack-other-bundlers) + - [NPM](#npm) - [In the Browser through ` - - ``` ## Usage @@ -86,111 +73,54 @@ console.log(decodedBuf.toString()) ## API https://multiformats.github.io/js-multibase/ -### `multibase` - Prefixes an encoded buffer with its multibase code +#### `multibase` - Prefixes an encoded buffer with its multibase code ``` const multibased = multibase(, encodedBuf) ``` -### `multibase.encode` - Encodes a buffer into one of the supported encodings, prefixing it with the multibase code +#### `multibase.encode` - Encodes a buffer into one of the supported encodings, prefixing it with the multibase code ```JavaScript const encodedBuf = multibase.encode(, ) ``` -### `multibase.decode` - Decodes a buffer or string +#### `multibase.decode` - Decodes a buffer or string ```JavaScript const decodedBuf = multibase.decode(bufOrString) ``` -### `multibase.isEncoded` - Checks if buffer or string is encoded +#### `multibase.isEncoded` - Checks if buffer or string is encoded ```JavaScript const value = multibase.isEncoded(bufOrString) // value is the name of the encoding if it is encoded, false otherwise ``` -### `multibase.names` - -A frozen `Array` of supported base encoding names. - -### `multibase.codes` - -A frozen `Array` of supported base encoding codes. +#### `multibase.encoding` - Get the encoding by name or code -### Supported Encodings, see [`src/constants.js`](/src/constants.js) - -## Architecture and Encoding/Decoding - -Multibase package defines all the supported bases and the location of their implementation in the constants.js file. A base is a class with a name, a code, an implementation and an alphabet. -```js -class Base { - constructor (name, code, implementation, alphabet) { - //... - } - // ... -} +```JavaScript +const value = multibase.encoding(nameOrCode) +// value is an instance of the corresponding `Base` ``` -The ```implementation``` is an object where the encoding/decoding functions are implemented. It must take one argument, (the alphabet) following the [base-x module](https://github.com/cryptocoinjs/base-x) architecture. - -The ```alphabet``` is the **ordered** set of defined symbols for a given base. -The idea behind this is that several bases may have implementations from different locations/modules so it's useful to have an object (and a summary) of all of them in one location (hence the constants.js). +#### `multibase.encodingFromData` - Get the encoding from data either a `string` or `Buffer` -All the supported bases are currently using the npm [base-x](https://github.com/cryptocoinjs/base-x) module as their implementation. It is using bitwise maipulation to go from one base to another, so this module does not support padding at the moment. +```JavaScript +const value = multibase.encodingFromData(data) +// value is an instance of the corresponding `Base` +``` -## Adding additional bases +#### `multibase.names` -If the base you are looking for is not supported yet in js-multibase and you know a good encoding/decoding algorithm, you can add support for this base easily by editing the constants.js file -(**you'll need to create an issue about that beforehand since a code and a canonical name have to be defined**): +A frozen `Object` of supported base encoding names mapped to the corresponding `Base` instance. -```js -const baseX = require('base-x') -//const newPackage = require('your-package-name') +#### `multibase.codes` -const constants = [ - ['base2', '0', baseX, '01'], - ['base8', '7', baseX, '01234567'], - // ... [ 'your-base-name', 'code-to-be-defined', newPackage, 'alphabet'] -] -``` -The required package defines the implementation of the encoding/decoding process. **It must comply by these rules** : -- `encode` and `decode` functions with to-be-encoded buffer as the only expected argument -- the require call use the `alphabet` given as an argument for the encoding/decoding process - -*If no package is specified , it means the base is not implemented yet* - -Adding a new base requires the tests to be updated. Test files to be updated are : -- constants.spec.js -```js -describe('constants', () => { - it('constants indexed by name', () => { - const names = constants.names - expect(Object.keys(names).length).to.equal(constants-count) // currently 12 - }) - - it('constants indexed by code', () => { - const codes = constants.codes - expect(Object.keys(codes).length).to.equal(constants-count) - }) -}) -``` +A frozen `Object` of supported base encoding codes mapped to the corresponding `Base` instance. -- multibase.spec.js - - if the base is implemented - ```js - const supportedBases = [ - ['base2', 'yes mani !', '01111001011001010111001100100000011011010110000101101110011010010010000000100001'], - ['base8', 'yes mani !', '7171312714403326055632220041'], - ['base10', 'yes mani !', '9573277761329450583662625'], - // ... ['your-base-name', 'what you want', 'expected output'] - ``` - - if the base is not implemented yet - ```js - const supportedBases = [ - // ... ['your-base-name'] - ``` +### Supported Encodings, see [`src/constants.js`](/src/constants.js) ## Contribute diff --git a/benchmark.js b/benchmark.js new file mode 100644 index 0000000..9ef09e5 --- /dev/null +++ b/benchmark.js @@ -0,0 +1,23 @@ +/* eslint-disable no-console */ +'use strict' +const Benchmark = require('benchmark') +const multibase = require('./src') + +const names = Object.keys(multibase.names) +const suite = new Benchmark.Suite() +const input = 'Decentralize everything!!' + +for (const enc of names) { + suite.add(enc, () => { + multibase.encode(enc, Buffer.from(input)) + }) +} + +suite + .on('cycle', function (event) { + console.log(String(event.target)) + }) + .on('complete', function () { + console.log('Fastest is ' + this.filter('fastest').map('name')) + }) + .run({ async: true }) diff --git a/package.json b/package.json index 4a92602..821768c 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ }, "devDependencies": { "aegir": "^22.0.0", + "benchmark": "^2.1.4", "chai": "^4.1.2", "dirty-chai": "^2.0.1", "pre-commit": "^1.2.2" diff --git a/src/base.js b/src/base.js index 1a66059..c23b241 100644 --- a/src/base.js +++ b/src/base.js @@ -4,22 +4,22 @@ class Base { constructor (name, code, implementation, alphabet) { this.name = name this.code = code + this.codeBuf = Buffer.from(this.code) this.alphabet = alphabet - if (implementation && alphabet) { - this.engine = implementation(alphabet) - } - } - - encode (stringOrBuffer) { - return this.engine.encode(stringOrBuffer) + this.engine = implementation(alphabet) } - decode (stringOrBuffer) { - return this.engine.decode(stringOrBuffer) + encode (buf) { + return this.engine.encode(buf) } - isImplemented () { - return this.engine + decode (string) { + for (const char of string) { + if (this.alphabet && this.alphabet.indexOf(char) < 0) { + throw new Error(`invalid ${this.name} character '${char}' in '${string}'`) + } + } + return this.engine.decode(string) } } diff --git a/src/base16.js b/src/base16.js deleted file mode 100644 index f911f80..0000000 --- a/src/base16.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict' -const { Buffer } = require('buffer') - -module.exports = function base16 (alphabet) { - return { - encode (input) { - if (typeof input === 'string') { - return Buffer.from(input).toString('hex') - } - return input.toString('hex') - }, - decode (input) { - for (const char of input) { - if (alphabet.indexOf(char) < 0) { - throw new Error('invalid base16 character') - } - } - return Buffer.from(input, 'hex') - } - } -} diff --git a/src/base32.js b/src/base32.js deleted file mode 100644 index cc38046..0000000 --- a/src/base32.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict' - -function decode (input, alphabet) { - input = input.replace(new RegExp('=', 'g'), '') - const length = input.length - - let bits = 0 - let value = 0 - - let index = 0 - const output = new Uint8Array((length * 5 / 8) | 0) - - for (let i = 0; i < length; i++) { - value = (value << 5) | alphabet.indexOf(input[i]) - bits += 5 - - if (bits >= 8) { - output[index++] = (value >>> (bits - 8)) & 255 - bits -= 8 - } - } - - return output.buffer -} - -function encode (buffer, alphabet) { - const length = buffer.byteLength - const view = new Uint8Array(buffer) - const padding = alphabet.indexOf('=') === alphabet.length - 1 - - if (padding) { - alphabet = alphabet.substring(0, alphabet.length - 1) - } - - let bits = 0 - let value = 0 - let output = '' - - for (let i = 0; i < length; i++) { - value = (value << 8) | view[i] - bits += 8 - - while (bits >= 5) { - output += alphabet[(value >>> (bits - 5)) & 31] - bits -= 5 - } - } - - if (bits > 0) { - output += alphabet[(value << (5 - bits)) & 31] - } - - if (padding) { - while ((output.length % 8) !== 0) { - output += '=' - } - } - - return output -} - -module.exports = function base32 (alphabet) { - return { - encode (input) { - if (typeof input === 'string') { - return encode(Uint8Array.from(input), alphabet) - } - - return encode(input, alphabet) - }, - decode (input) { - for (const char of input) { - if (alphabet.indexOf(char) < 0) { - throw new Error('invalid base32 character') - } - } - - return decode(input, alphabet) - } - } -} diff --git a/src/base64.js b/src/base64.js deleted file mode 100644 index c4608bb..0000000 --- a/src/base64.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict' -const { Buffer } = require('buffer') - -module.exports = function base64 (alphabet) { - // The alphabet is only used to know: - // 1. If padding is enabled (must contain '=') - // 2. If the output must be url-safe (must contain '-' and '_') - // 3. If the input of the output function is valid - // The alphabets from RFC 4648 are always used. - const padding = alphabet.indexOf('=') > -1 - const url = alphabet.indexOf('-') > -1 && alphabet.indexOf('_') > -1 - - return { - encode (input) { - let output = '' - - if (typeof input === 'string') { - output = Buffer.from(input).toString('base64') - } else { - output = input.toString('base64') - } - - if (url) { - output = output.replace(/\+/g, '-').replace(/\//g, '_') - } - - const pad = output.indexOf('=') - if (pad > 0 && !padding) { - output = output.substring(0, pad) - } - - return output - }, - decode (input) { - for (const char of input) { - if (alphabet.indexOf(char) < 0) { - throw new Error('invalid base64 character') - } - } - - return Buffer.from(input, 'base64') - } - } -} diff --git a/src/constants.js b/src/constants.js index 1b6bd37..1addac3 100644 --- a/src/constants.js +++ b/src/constants.js @@ -2,28 +2,40 @@ const Base = require('./base.js') const baseX = require('base-x') -const base16 = require('./base16') -const base32 = require('./base32') -const base64 = require('./base64') +const rfc4648 = require('./rfc4648') + +const identity = () => { + return { + encode: (data) => Buffer.from(data).toString(), + decode: (string) => Buffer.from(string) + } +} // name, code, implementation, alphabet const constants = [ - ['base1', '1', '', '1'], - ['base2', '0', baseX, '01'], - ['base8', '7', baseX, '01234567'], + ['identity', '\u0000', identity, ''], + ['base2', '0', rfc4648(1), '01'], + ['base8', '7', rfc4648(3), '01234567'], ['base10', '9', baseX, '0123456789'], - ['base16', 'f', base16, '0123456789abcdef'], - ['base32', 'b', base32, 'abcdefghijklmnopqrstuvwxyz234567'], - ['base32pad', 'c', base32, 'abcdefghijklmnopqrstuvwxyz234567='], - ['base32hex', 'v', base32, '0123456789abcdefghijklmnopqrstuv'], - ['base32hexpad', 't', base32, '0123456789abcdefghijklmnopqrstuv='], - ['base32z', 'h', base32, 'ybndrfg8ejkmcpqxot1uwisza345h769'], - ['base58flickr', 'Z', baseX, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'], + ['base16', 'f', rfc4648(4), '0123456789abcdef'], + ['base16upper', 'F', rfc4648(4), '0123456789ABCDEF'], + ['base32hex', 'v', rfc4648(5), '0123456789abcdefghijklmnopqrstuv'], + ['base32hexupper', 'V', rfc4648(5), '0123456789ABCDEFGHIJKLMNOPQRSTUV'], + ['base32hexpad', 't', rfc4648(5), '0123456789abcdefghijklmnopqrstuv='], + ['base32hexpadupper', 'T', rfc4648(5), '0123456789ABCDEFGHIJKLMNOPQRSTUV='], + ['base32', 'b', rfc4648(5), 'abcdefghijklmnopqrstuvwxyz234567'], + ['base32upper', 'B', rfc4648(5), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'], + ['base32pad', 'c', rfc4648(5), 'abcdefghijklmnopqrstuvwxyz234567='], + ['base32padupper', 'C', rfc4648(5), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567='], + ['base32z', 'h', rfc4648(5), 'ybndrfg8ejkmcpqxot1uwisza345h769'], + ['base36', 'k', baseX, '0123456789abcdefghijklmnopqrstuvwxyz'], + ['base36upper', 'K', baseX, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'], ['base58btc', 'z', baseX, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'], - ['base64', 'm', base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'], - ['base64pad', 'M', base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='], - ['base64url', 'u', base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'], - ['base64urlpad', 'U', base64, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='] + ['base58flickr', 'Z', baseX, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'], + ['base64', 'm', rfc4648(6), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'], + ['base64pad', 'M', rfc4648(6), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='], + ['base64url', 'u', rfc4648(6), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'], + ['base64urlpad', 'U', rfc4648(6), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_='] ] const names = constants.reduce((prev, tupple) => { @@ -37,6 +49,6 @@ const codes = constants.reduce((prev, tupple) => { }, {}) module.exports = { - names: names, - codes: codes + names, + codes } diff --git a/src/index.js b/src/index.js index 32760ea..82761e6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ /** * Implementation of the [multibase](https://github.com/multiformats/multibase) specification. + * * @module Multibase */ 'use strict' @@ -7,31 +8,22 @@ const { Buffer } = require('buffer') const constants = require('./constants') -exports = module.exports = multibase -exports.encode = encode -exports.decode = decode -exports.isEncoded = isEncoded -exports.names = Object.freeze(Object.keys(constants.names)) -exports.codes = Object.freeze(Object.keys(constants.codes)) +/** @typedef {import("./base")} Base */ /** * Create a new buffer with the multibase varint+code. * * @param {string|number} nameOrCode - The multibase name or code number. * @param {Buffer} buf - The data to be prefixed with multibase. - * @memberof Multibase * @returns {Buffer} */ function multibase (nameOrCode, buf) { if (!buf) { throw new Error('requires an encoded buffer') } - const base = getBase(nameOrCode) - const codeBuf = Buffer.from(base.code) - - const name = base.name - validEncode(name, buf) - return Buffer.concat([codeBuf, buf]) + const enc = encoding(nameOrCode) + validEncode(enc.name, buf) + return Buffer.concat([enc.codeBuf, buf]) } /** @@ -40,91 +32,108 @@ function multibase (nameOrCode, buf) { * @param {string|number} nameOrCode - The multibase name or code number. * @param {Buffer} buf - The data to be encoded. * @returns {Buffer} - * @memberof Multibase */ function encode (nameOrCode, buf) { - const base = getBase(nameOrCode) - const name = base.name + const enc = encoding(nameOrCode) - return multibase(name, Buffer.from(base.encode(buf))) + return Buffer.concat([enc.codeBuf, Buffer.from(enc.encode(buf))]) } /** * Takes a buffer or string encoded with multibase header, decodes it and * returns the decoded buffer * - * @param {Buffer|string} bufOrString + * @param {Buffer|string} data * @returns {Buffer} - * @memberof Multibase * */ -function decode (bufOrString) { - if (Buffer.isBuffer(bufOrString)) { - bufOrString = bufOrString.toString() +function decode (data) { + if (Buffer.isBuffer(data)) { + data = data.toString() } - - const code = bufOrString.substring(0, 1) - bufOrString = bufOrString.substring(1, bufOrString.length) - - if (typeof bufOrString === 'string') { - bufOrString = Buffer.from(bufOrString) + const enc = encoding(data[0]) + if (enc) { + return Buffer.from(enc.decode(data.substring(1))) } - const base = getBase(code) - return Buffer.from(base.decode(bufOrString.toString())) + throw new Error('Unsupported encoding') } /** * Is the given data multibase encoded? * - * @param {Buffer|string} bufOrString + * @param {Buffer|string} data * @returns {boolean} - * @memberof Multibase */ -function isEncoded (bufOrString) { - if (Buffer.isBuffer(bufOrString)) { - bufOrString = bufOrString.toString() +function isEncoded (data) { + if (Buffer.isBuffer(data)) { + data = data.toString() } // Ensure bufOrString is a string - if (Object.prototype.toString.call(bufOrString) !== '[object String]') { + if (Object.prototype.toString.call(data) !== '[object String]') { return false } - const code = bufOrString.substring(0, 1) try { - const base = getBase(code) - return base.name + const enc = encoding(data[0]) + return enc.name } catch (err) { return false } } /** + * Validate encoded data + * * @param {string} name * @param {Buffer} buf - * @private * @returns {undefined} */ function validEncode (name, buf) { - const base = getBase(name) - base.decode(buf.toString()) + const enc = encoding(name) + enc.decode(buf.toString()) } -function getBase (nameOrCode) { - let base +/** + * Get the encoding by name or code + * + * @param {string} nameOrCode + * @returns {Base} + */ +function encoding (nameOrCode) { + let enc if (constants.names[nameOrCode]) { - base = constants.names[nameOrCode] + enc = constants.names[nameOrCode] } else if (constants.codes[nameOrCode]) { - base = constants.codes[nameOrCode] + enc = constants.codes[nameOrCode] } else { - throw new Error('Unsupported encoding') + throw new Error(`Unsupported encoding: ${nameOrCode}`) } - if (!base.isImplemented()) { - throw new Error('Base ' + nameOrCode + ' is not implemented yet') + return enc +} + +/** + * Get encoding from data + * + * @param {string|Buffer} data + * @returns {Base} + */ +function encodingFromData (data) { + if (Buffer.isBuffer(data)) { + data = data.toString() } - return base + return encoding(data[0]) } + +exports = module.exports = multibase +exports.encode = encode +exports.decode = decode +exports.isEncoded = isEncoded +exports.encoding = encoding +exports.encodingFromData = encodingFromData +exports.names = Object.freeze(constants.names) +exports.codes = Object.freeze(constants.codes) diff --git a/src/rfc4648.js b/src/rfc4648.js new file mode 100644 index 0000000..35c9f89 --- /dev/null +++ b/src/rfc4648.js @@ -0,0 +1,92 @@ +'use strict' + +const decode = (string, alphabet, bitsPerChar) => { + // Build the character lookup table: + const codes = {} + for (let i = 0; i < alphabet.length; ++i) { + codes[alphabet[i]] = i + } + + // Count the padding bytes: + let end = string.length + while (string[end - 1] === '=') { + --end + } + + // Allocate the output: + const out = new Uint8Array((end * bitsPerChar / 8) | 0) + + // Parse the data: + let bits = 0 // Number of bits currently in the buffer + let buffer = 0 // Bits waiting to be written out, MSB first + let written = 0 // Next byte to write + for (let i = 0; i < end; ++i) { + // Read one character from the string: + const value = codes[string[i]] + if (value === undefined) { + throw new SyntaxError('Invalid character ' + string[i]) + } + + // Append the bits to the buffer: + buffer = (buffer << bitsPerChar) | value + bits += bitsPerChar + + // Write out some bits if the buffer has a byte's worth: + if (bits >= 8) { + bits -= 8 + out[written++] = 0xff & (buffer >> bits) + } + } + + // Verify that we have received just enough bits: + if (bits >= bitsPerChar || 0xff & (buffer << (8 - bits))) { + throw new SyntaxError('Unexpected end of data') + } + + return out +} + +const encode = (data, alphabet, bitsPerChar) => { + const pad = alphabet.indexOf('=') === alphabet.length - 1 + const mask = (1 << bitsPerChar) - 1 + let out = '' + + let bits = 0 // Number of bits currently in the buffer + let buffer = 0 // Bits waiting to be written out, MSB first + for (let i = 0; i < data.length; ++i) { + // Slurp data into the buffer: + buffer = (buffer << 8) | (0xff & data[i]) + bits += 8 + + // Write out as much as we can: + while (bits > bitsPerChar) { + bits -= bitsPerChar + out += alphabet[mask & (buffer >> bits)] + } + } + + // Partial character: + if (bits) { + out += alphabet[mask & (buffer << (bitsPerChar - bits))] + } + + // Add padding characters until we hit a byte boundary: + if (pad) { + while ((out.length * bitsPerChar) & 7) { + out += '=' + } + } + + return out +} + +module.exports = (bitsPerChar) => (alphabet) => { + return { + encode (input) { + return encode(input, alphabet, bitsPerChar) + }, + decode (input) { + return decode(input, alphabet, bitsPerChar) + } + } +} diff --git a/test/constants.spec.js b/test/constants.spec.js index 302e95c..787d410 100644 --- a/test/constants.spec.js +++ b/test/constants.spec.js @@ -10,11 +10,11 @@ const constants = require('../src/constants.js') describe('constants', () => { it('constants indexed by name', () => { const names = constants.names - expect(Object.keys(names).length).to.equal(16) + expect(Object.keys(names).length).to.equal(23) }) it('constants indexed by code', () => { const codes = constants.codes - expect(Object.keys(codes).length).to.equal(16) + expect(Object.keys(codes).length).to.equal(23) }) }) diff --git a/test/multibase.spec.js b/test/multibase.spec.js index bbd3ef0..d2a19c6 100644 --- a/test/multibase.spec.js +++ b/test/multibase.spec.js @@ -11,10 +11,6 @@ const constants = require('../src/constants.js') const unsupportedBases = [] const supportedBases = [ - ['base2', 'yes mani !', '01111001011001010111001100100000011011010110000101101110011010010010000000100001'], - ['base8', 'yes mani !', '7171312714403326055632220041'], - ['base10', 'yes mani !', '9573277761329450583662625'], - ['base16', 'yes mani !', 'f796573206d616e692021'], ['base16', Buffer.from([0x01]), 'f01'], ['base16', Buffer.from([15]), 'f0f'], @@ -121,6 +117,7 @@ describe('multibase', () => { const base = constants.names[name] describe(name, () => { it('adds multibase code to valid encoded buffer, by name', () => { + console.log('input', input) if (typeof input === 'string') { const buf = Buffer.from(input) const encodedBuf = Buffer.from(base.encode(buf)) @@ -219,7 +216,7 @@ for (const elements of unsupportedBases) { describe('multibase.names', () => { it('includes all base names', () => { Object.keys(constants.names).forEach(name => { - expect(multibase.names).to.include(name) + expect(Object.keys(multibase.names)).to.include(name) }) }) @@ -231,7 +228,7 @@ describe('multibase.names', () => { describe('multibase.codes', () => { it('includes all base codes', () => { Object.keys(constants.codes).forEach(code => { - expect(multibase.codes).to.include(code) + expect(Object.keys(multibase.codes)).to.include(code) }) }) diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js new file mode 100644 index 0000000..94108d1 --- /dev/null +++ b/test/spec-test1.spec.js @@ -0,0 +1,76 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const constants = require('../src/constants.js') +const input = 'Decentralize everything!!' +const encoded = [ + ['identity', '\u0000Decentralize everything!!'], + ['base2', '001000100011001010110001101100101011011100111010001110010011000010110110001101001011110100110010100100000011001010111011001100101011100100111100101110100011010000110100101101110011001110010000100100001'], + // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 + // ['base8', '71043126154533472162302661513646244031273145344745643206455631620441'], + ['base8', '72106254331267164344605543227514510062566312711713506415133463441102'], + ['base10', '9429328951066508984658627669258025763026247056774804621697313'], + ['base16', 'f446563656e7472616c697a652065766572797468696e672121'], + ['base16upper', 'F446563656E7472616C697A652065766572797468696E672121'], + ['base32', 'birswgzloorzgc3djpjssazlwmvzhs5dinfxgoijb'], + ['base32upper', 'BIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJB'], + ['base32hex', 'v8him6pbeehp62r39f9ii0pbmclp7it38d5n6e891'], + ['base32hexupper', 'V8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E891'], + ['base32pad', 'cirswgzloorzgc3djpjssazlwmvzhs5dinfxgoijb'], + ['base32padupper', 'CIRSWGZLOORZGC3DJPJSSAZLWMVZHS5DINFXGOIJB'], + ['base32hexpad', 't8him6pbeehp62r39f9ii0pbmclp7it38d5n6e891'], + ['base32hexpadupper', 'T8HIM6PBEEHP62R39F9II0PBMCLP7IT38D5N6E891'], + ['base32z', 'het1sg3mqqt3gn5djxj11y3msci3817depfzgqejb'], + ['base36', 'k343ixo7d49hqj1ium15pgy1wzww5fxrid21td7l'], + ['base36upper', 'K343IXO7D49HQJ1IUM15PGY1WZWW5FXRID21TD7L'], + ['base58flickr', 'Ztwe7gVTeK8wswS1gf8hrgAua9fcw9reboD'], + ['base58btc', 'zUXE7GvtEk8XTXs1GF8HSGbVA9FCX9SEBPe'], + ['base64', 'mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'], + ['base64pad', 'MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='], + ['base64url', 'uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'], + ['base64urlpad', 'URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='] +] + +describe('spec test1', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + const base = constants.names[name] + + describe(name, () => { + it('should encode buffer by base name', () => { + const out = multibase.encode(name, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should encode buffer by base code', () => { + const out = multibase.encode(base.code, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + + it('should prefix encoded buffer', () => { + const base = constants.names[name] + const data = base.encode(Buffer.from(input)) + + expect(multibase(name, Buffer.from(data)).toString()).to.equal(output) + }) + + it('should fail decode with invalid char', function () { + if (name === 'identity') { + return this.skip() + } + const nonEncodedBuf = Buffer.from(base.code + '^!@$%!#$%@#y') + expect(() => { + multibase.decode(nonEncodedBuf) + }).to.throw(Error, `invalid ${name} character '^' in '^!@$%!#$%@#y'`) + }) + }) + } +}) diff --git a/test/spec-test2.spec.js b/test/spec-test2.spec.js new file mode 100644 index 0000000..2945bc4 --- /dev/null +++ b/test/spec-test2.spec.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const constants = require('../src/constants.js') +const input = 'yes mani !' +const encoded = [ + ['identity', '\u0000yes mani !'], + ['base2', '001111001011001010111001100100000011011010110000101101110011010010010000000100001'], + // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 + // ['base8', '7171312714403326055632220041'], + ['base8', '7362625631006654133464440102'], + ['base10', '9573277761329450583662625'], + ['base16', 'f796573206d616e692021'], + ['base16upper', 'F796573206D616E692021'], + ['base32', 'bpfsxgidnmfxgsibb'], + ['base32upper', 'BPFSXGIDNMFXGSIBB'], + ['base32hex', 'vf5in683dc5n6i811'], + ['base32hexupper', 'VF5IN683DC5N6I811'], + ['base32pad', 'cpfsxgidnmfxgsibb'], + ['base32padupper', 'CPFSXGIDNMFXGSIBB'], + ['base32hexpad', 'tf5in683dc5n6i811'], + ['base32hexpadupper', 'TF5IN683DC5N6I811'], + ['base32z', 'hxf1zgedpcfzg1ebb'], + ['base36', 'k2lcpzo5yikidynfl'], + ['base36upper', 'K2LCPZO5YIKIDYNFL'], + ['base58flickr', 'Z7Pznk19XTTzBtx'], + ['base58btc', 'z7paNL19xttacUY'], + ['base64', 'meWVzIG1hbmkgIQ'], + ['base64pad', 'MeWVzIG1hbmkgIQ=='], + ['base64url', 'ueWVzIG1hbmkgIQ'], + ['base64urlpad', 'UeWVzIG1hbmkgIQ=='] +] + +describe('spec test2', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + const base = constants.names[name] + + describe(name, () => { + it('should encode buffer by base name', () => { + const out = multibase.encode(name, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should encode buffer by base code', () => { + const out = multibase.encode(base.code, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + }) + } +}) diff --git a/test/spec-test3.spec.js b/test/spec-test3.spec.js new file mode 100644 index 0000000..e628987 --- /dev/null +++ b/test/spec-test3.spec.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const constants = require('../src/constants.js') +const input = 'hello world' +const encoded = [ + ['identity', '\u0000hello world'], + ['base2', '00110100001100101011011000110110001101111001000000111011101101111011100100110110001100100'], + // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 + // ['base8', '7064145330661571007355734466144'], + ['base8', '7320625543306744035667562330620'], + ['base10', '9126207244316550804821666916'], + ['base16', 'f68656c6c6f20776f726c64'], + ['base16upper', 'F68656C6C6F20776F726C64'], + ['base32', 'bnbswy3dpeb3w64tmmq'], + ['base32upper', 'BNBSWY3DPEB3W64TMMQ'], + ['base32hex', 'vd1imor3f41rmusjccg'], + ['base32hexupper', 'VD1IMOR3F41RMUSJCCG'], + ['base32pad', 'cnbswy3dpeb3w64tmmq======'], + ['base32padupper', 'CNBSWY3DPEB3W64TMMQ======'], + ['base32hexpad', 'td1imor3f41rmusjccg======'], + ['base32hexpadupper', 'TD1IMOR3F41RMUSJCCG======'], + ['base32z', 'hpb1sa5dxrb5s6hucco'], + ['base36', 'kfuvrsivvnfrbjwajo'], + ['base36upper', 'KFUVRSIVVNFRBJWAJO'], + ['base58flickr', 'ZrTu1dk6cWsRYjYu'], + ['base58btc', 'zStV1DL6CwTryKyV'], + ['base64', 'maGVsbG8gd29ybGQ'], + ['base64pad', 'MaGVsbG8gd29ybGQ='], + ['base64url', 'uaGVsbG8gd29ybGQ'], + ['base64urlpad', 'UaGVsbG8gd29ybGQ='] +] + +describe('spec test3', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + const base = constants.names[name] + + describe(name, () => { + it('should encode buffer by base name', () => { + const out = multibase.encode(name, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should encode buffer by base code', () => { + const out = multibase.encode(base.code, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + }) + } +}) diff --git a/test/spec-test4.spec.js b/test/spec-test4.spec.js new file mode 100644 index 0000000..87912a6 --- /dev/null +++ b/test/spec-test4.spec.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const constants = require('../src/constants.js') +const input = '\x00yes mani !' +const encoded = [ + ['identity', '\u0000\x00yes mani !'], + ['base2', '00000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], + // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 + // ['base8', '7000171312714403326055632220041'], + ['base8', '7000745453462015530267151100204'], + ['base10', '90573277761329450583662625'], + ['base16', 'f00796573206d616e692021'], + ['base16upper', 'F00796573206D616E692021'], + ['base32', 'bab4wk4zanvqw42jaee'], + ['base32upper', 'BAB4WK4ZANVQW42JAEE'], + ['base32hex', 'v01smasp0dlgmsq9044'], + ['base32hexupper', 'V01SMASP0DLGMSQ9044'], + ['base32pad', 'cab4wk4zanvqw42jaee======'], + ['base32padupper', 'CAB4WK4ZANVQW42JAEE======'], + ['base32hexpad', 't01smasp0dlgmsq9044======'], + ['base32hexpadupper', 'T01SMASP0DLGMSQ9044======'], + ['base32z', 'hybhskh3ypiosh4jyrr'], + ['base36', 'k02lcpzo5yikidynfl'], + ['base36upper', 'K02LCPZO5YIKIDYNFL'], + ['base58flickr', 'Z17Pznk19XTTzBtx'], + ['base58btc', 'z17paNL19xttacUY'], + ['base64', 'mAHllcyBtYW5pICE'], + ['base64pad', 'MAHllcyBtYW5pICE='], + ['base64url', 'uAHllcyBtYW5pICE'], + ['base64urlpad', 'UAHllcyBtYW5pICE='] +] + +describe('spec test4', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + const base = constants.names[name] + + describe(name, () => { + it('should encode buffer by base name', () => { + const out = multibase.encode(name, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should encode buffer by base code', () => { + const out = multibase.encode(base.code, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + }) + } +}) diff --git a/test/spec-test5.spec.js b/test/spec-test5.spec.js new file mode 100644 index 0000000..a1025e3 --- /dev/null +++ b/test/spec-test5.spec.js @@ -0,0 +1,58 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const constants = require('../src/constants.js') +const input = '\x00\x00yes mani !' +const encoded = [ + ['identity', '\u0000\x00\x00yes mani !'], + ['base2', '0000000000000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], + // Spec value matches our implementation + ['base8', '700000171312714403326055632220041'], + ['base10', '900573277761329450583662625'], + ['base16', 'f0000796573206d616e692021'], + ['base16upper', 'F0000796573206D616E692021'], + ['base32', 'baaahszltebwwc3tjeaqq'], + ['base32upper', 'BAAAHSZLTEBWWC3TJEAQQ'], + ['base32hex', 'v0007ipbj41mm2rj940gg'], + ['base32hexupper', 'V0007IPBJ41MM2RJ940GG'], + ['base32pad', 'caaahszltebwwc3tjeaqq===='], + ['base32padupper', 'CAAAHSZLTEBWWC3TJEAQQ===='], + ['base32hexpad', 't0007ipbj41mm2rj940gg===='], + ['base32hexpadupper', 'T0007IPBJ41MM2RJ940GG===='], + ['base32z', 'hyyy813murbssn5ujryoo'], + ['base36', 'k002lcpzo5yikidynfl'], + ['base36upper', 'K002LCPZO5YIKIDYNFL'], + ['base58flickr', 'Z117Pznk19XTTzBtx'], + ['base58btc', 'z117paNL19xttacUY'], + ['base64', 'mAAB5ZXMgbWFuaSAh'], + ['base64pad', 'MAAB5ZXMgbWFuaSAh'], + ['base64url', 'uAAB5ZXMgbWFuaSAh'], + ['base64urlpad', 'UAAB5ZXMgbWFuaSAh'] +] + +describe('spec test5', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + const base = constants.names[name] + + describe(name, () => { + it('should encode buffer by base name', () => { + const out = multibase.encode(name, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should encode buffer by base code', () => { + const out = multibase.encode(base.code, Buffer.from(input)) + expect(out.toString()).to.equal(output) + }) + + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + }) + } +}) diff --git a/test/spec-test6.spec.js b/test/spec-test6.spec.js new file mode 100644 index 0000000..d3047c6 --- /dev/null +++ b/test/spec-test6.spec.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('aegir/utils/chai') +const multibase = require('../src') +const input = 'hello world' +const encoded = [ + ['base16', 'f68656c6c6f20776F726C64'], + ['base16upper', 'F68656c6c6f20776F726C64'], + ['base32', 'bnbswy3dpeB3W64TMMQ'], + ['base32upper', 'Bnbswy3dpeB3W64TMMQ'], + ['base32hex', 'vd1imor3f41RMUSJCCG'], + ['base32hexupper', 'Vd1imor3f41RMUSJCCG'], + ['base32pad', 'cnbswy3dpeB3W64TMMQ======'], + ['base32padupper', 'Cnbswy3dpeB3W64TMMQ======'], + ['base32hexpad', 'td1imor3f41RMUSJCCG======'], + ['base32hexpadupper', 'Td1imor3f41RMUSJCCG======'], + ['base36', 'kfUvrsIvVnfRbjWaJo'], + ['base36upper', 'KfUVrSIVVnFRbJWAJo'] +] + +// we dont need to actually test these because we check the chars against the alphabet +describe.skip('spec test6', () => { + for (const e of encoded) { + const name = e[0] + const output = e[1] + + describe(name, () => { + it('should decode string', () => { + const out = multibase.decode(output) + expect(out.toString()).to.equal(input) + }) + }) + } +}) From c8f762996e47403c0c41c4f16c35c7b252c4f31e Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Wed, 3 Jun 2020 18:43:07 +0100 Subject: [PATCH 02/11] chore: linter --- test/multibase.spec.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/multibase.spec.js b/test/multibase.spec.js index d2a19c6..c40228b 100644 --- a/test/multibase.spec.js +++ b/test/multibase.spec.js @@ -117,7 +117,6 @@ describe('multibase', () => { const base = constants.names[name] describe(name, () => { it('adds multibase code to valid encoded buffer, by name', () => { - console.log('input', input) if (typeof input === 'string') { const buf = Buffer.from(input) const encodedBuf = Buffer.from(base.encode(buf)) From 2f88cb39952616cba82d7bc6d60ac8c11c64d7ed Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 4 Jun 2020 09:35:13 +0100 Subject: [PATCH 03/11] fix: improve pad detection --- src/rfc4648.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc4648.js b/src/rfc4648.js index 35c9f89..c615cfa 100644 --- a/src/rfc4648.js +++ b/src/rfc4648.js @@ -47,7 +47,7 @@ const decode = (string, alphabet, bitsPerChar) => { } const encode = (data, alphabet, bitsPerChar) => { - const pad = alphabet.indexOf('=') === alphabet.length - 1 + const pad = alphabet[alphabet.length - 1] === '=' const mask = (1 << bitsPerChar) - 1 let out = '' From da6c03fc47181f6fcf2a83460a0258323a128127 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 4 Jun 2020 09:45:52 +0100 Subject: [PATCH 04/11] fix: remove unnecessary & --- src/rfc4648.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc4648.js b/src/rfc4648.js index c615cfa..b020a5a 100644 --- a/src/rfc4648.js +++ b/src/rfc4648.js @@ -55,7 +55,7 @@ const encode = (data, alphabet, bitsPerChar) => { let buffer = 0 // Bits waiting to be written out, MSB first for (let i = 0; i < data.length; ++i) { // Slurp data into the buffer: - buffer = (buffer << 8) | (0xff & data[i]) + buffer = (buffer << 8) | data[i] bits += 8 // Write out as much as we can: From 61af3471d1382db29aee4ba396532cf5226a2e46 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Thu, 4 Jun 2020 12:04:10 +0100 Subject: [PATCH 05/11] fix: feedback --- src/index.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/index.js b/src/index.js index 82761e6..0f307a7 100644 --- a/src/index.js +++ b/src/index.js @@ -16,6 +16,7 @@ const constants = require('./constants') * @param {string|number} nameOrCode - The multibase name or code number. * @param {Buffer} buf - The data to be prefixed with multibase. * @returns {Buffer} + * @throws {Error} Will throw if the encoding is not supported */ function multibase (nameOrCode, buf) { if (!buf) { @@ -32,6 +33,8 @@ function multibase (nameOrCode, buf) { * @param {string|number} nameOrCode - The multibase name or code number. * @param {Buffer} buf - The data to be encoded. * @returns {Buffer} + * @throws {Error} Will throw if the encoding is not supported + * */ function encode (nameOrCode, buf) { const enc = encoding(nameOrCode) @@ -45,6 +48,7 @@ function encode (nameOrCode, buf) { * * @param {Buffer|string} data * @returns {Buffer} + * @throws {Error} Will throw if the encoding is not supported * */ function decode (data) { @@ -52,11 +56,7 @@ function decode (data) { data = data.toString() } const enc = encoding(data[0]) - if (enc) { - return Buffer.from(enc.decode(data.substring(1))) - } - - throw new Error('Unsupported encoding') + return Buffer.from(enc.decode(data.substring(1))) } /** @@ -89,6 +89,7 @@ function isEncoded (data) { * @param {string} name * @param {Buffer} buf * @returns {undefined} + * @throws {Error} Will throw if the encoding is not supported */ function validEncode (name, buf) { const enc = encoding(name) @@ -100,19 +101,16 @@ function validEncode (name, buf) { * * @param {string} nameOrCode * @returns {Base} + * @throws {Error} Will throw if the encoding is not supported */ function encoding (nameOrCode) { - let enc - if (constants.names[nameOrCode]) { - enc = constants.names[nameOrCode] + return constants.names[nameOrCode] } else if (constants.codes[nameOrCode]) { - enc = constants.codes[nameOrCode] + return constants.codes[nameOrCode] } else { throw new Error(`Unsupported encoding: ${nameOrCode}`) } - - return enc } /** @@ -120,6 +118,7 @@ function encoding (nameOrCode) { * * @param {string|Buffer} data * @returns {Base} + * @throws {Error} Will throw if the encoding is not supported */ function encodingFromData (data) { if (Buffer.isBuffer(data)) { From 6290e548eaacd014f07ae6aff4df079ac5e55ec4 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Sun, 7 Jun 2020 17:30:48 +0100 Subject: [PATCH 06/11] fix: remove unnecessary comments --- test/spec-test1.spec.js | 2 -- test/spec-test2.spec.js | 2 -- test/spec-test3.spec.js | 2 -- test/spec-test4.spec.js | 2 -- test/spec-test5.spec.js | 1 - 5 files changed, 9 deletions(-) diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js index 94108d1..7077cd0 100644 --- a/test/spec-test1.spec.js +++ b/test/spec-test1.spec.js @@ -8,8 +8,6 @@ const input = 'Decentralize everything!!' const encoded = [ ['identity', '\u0000Decentralize everything!!'], ['base2', '001000100011001010110001101100101011011100111010001110010011000010110110001101001011110100110010100100000011001010111011001100101011100100111100101110100011010000110100101101110011001110010000100100001'], - // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 - // ['base8', '71043126154533472162302661513646244031273145344745643206455631620441'], ['base8', '72106254331267164344605543227514510062566312711713506415133463441102'], ['base10', '9429328951066508984658627669258025763026247056774804621697313'], ['base16', 'f446563656e7472616c697a652065766572797468696e672121'], diff --git a/test/spec-test2.spec.js b/test/spec-test2.spec.js index 2945bc4..6dc26e1 100644 --- a/test/spec-test2.spec.js +++ b/test/spec-test2.spec.js @@ -8,8 +8,6 @@ const input = 'yes mani !' const encoded = [ ['identity', '\u0000yes mani !'], ['base2', '001111001011001010111001100100000011011010110000101101110011010010010000000100001'], - // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 - // ['base8', '7171312714403326055632220041'], ['base8', '7362625631006654133464440102'], ['base10', '9573277761329450583662625'], ['base16', 'f796573206d616e692021'], diff --git a/test/spec-test3.spec.js b/test/spec-test3.spec.js index e628987..f7a1728 100644 --- a/test/spec-test3.spec.js +++ b/test/spec-test3.spec.js @@ -8,8 +8,6 @@ const input = 'hello world' const encoded = [ ['identity', '\u0000hello world'], ['base2', '00110100001100101011011000110110001101111001000000111011101101111011100100110110001100100'], - // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 - // ['base8', '7064145330661571007355734466144'], ['base8', '7320625543306744035667562330620'], ['base10', '9126207244316550804821666916'], ['base16', 'f68656c6c6f20776f726c64'], diff --git a/test/spec-test4.spec.js b/test/spec-test4.spec.js index 87912a6..a75dfa7 100644 --- a/test/spec-test4.spec.js +++ b/test/spec-test4.spec.js @@ -8,8 +8,6 @@ const input = '\x00yes mani !' const encoded = [ ['identity', '\u0000\x00yes mani !'], ['base2', '00000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], - // Spec value probably wrong, we use a different implementation that aligns with this PR https://github.com/multiformats/multibase/pull/60 - // ['base8', '7000171312714403326055632220041'], ['base8', '7000745453462015530267151100204'], ['base10', '90573277761329450583662625'], ['base16', 'f00796573206d616e692021'], diff --git a/test/spec-test5.spec.js b/test/spec-test5.spec.js index a1025e3..35e9766 100644 --- a/test/spec-test5.spec.js +++ b/test/spec-test5.spec.js @@ -8,7 +8,6 @@ const input = '\x00\x00yes mani !' const encoded = [ ['identity', '\u0000\x00\x00yes mani !'], ['base2', '0000000000000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], - // Spec value matches our implementation ['base8', '700000171312714403326055632220041'], ['base10', '900573277761329450583662625'], ['base16', 'f0000796573206d616e692021'], From 3a60dca51abd0cb1753c96dc87dcc81ffe27f6ed Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Sun, 7 Jun 2020 17:36:44 +0100 Subject: [PATCH 07/11] fix: use \x00 instead of \u0000 --- src/constants.js | 2 +- test/spec-test5.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/constants.js b/src/constants.js index 1addac3..deb3b4c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -13,7 +13,7 @@ const identity = () => { // name, code, implementation, alphabet const constants = [ - ['identity', '\u0000', identity, ''], + ['identity', '\x00', identity, ''], ['base2', '0', rfc4648(1), '01'], ['base8', '7', rfc4648(3), '01234567'], ['base10', '9', baseX, '0123456789'], diff --git a/test/spec-test5.spec.js b/test/spec-test5.spec.js index 35e9766..044282e 100644 --- a/test/spec-test5.spec.js +++ b/test/spec-test5.spec.js @@ -6,7 +6,7 @@ const multibase = require('../src') const constants = require('../src/constants.js') const input = '\x00\x00yes mani !' const encoded = [ - ['identity', '\u0000\x00\x00yes mani !'], + ['identity', '\x00\x00\x00yes mani !'], ['base2', '0000000000000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], ['base8', '700000171312714403326055632220041'], ['base10', '900573277761329450583662625'], From ece18c891055724b65cb5d5affdcb712176a224e Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Sun, 7 Jun 2020 17:40:28 +0100 Subject: [PATCH 08/11] fix: more of the same --- test/spec-test1.spec.js | 2 +- test/spec-test2.spec.js | 2 +- test/spec-test3.spec.js | 2 +- test/spec-test4.spec.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js index 7077cd0..32d9938 100644 --- a/test/spec-test1.spec.js +++ b/test/spec-test1.spec.js @@ -6,7 +6,7 @@ const multibase = require('../src') const constants = require('../src/constants.js') const input = 'Decentralize everything!!' const encoded = [ - ['identity', '\u0000Decentralize everything!!'], + ['identity', '\x00Decentralize everything!!'], ['base2', '001000100011001010110001101100101011011100111010001110010011000010110110001101001011110100110010100100000011001010111011001100101011100100111100101110100011010000110100101101110011001110010000100100001'], ['base8', '72106254331267164344605543227514510062566312711713506415133463441102'], ['base10', '9429328951066508984658627669258025763026247056774804621697313'], diff --git a/test/spec-test2.spec.js b/test/spec-test2.spec.js index 6dc26e1..80c1f98 100644 --- a/test/spec-test2.spec.js +++ b/test/spec-test2.spec.js @@ -6,7 +6,7 @@ const multibase = require('../src') const constants = require('../src/constants.js') const input = 'yes mani !' const encoded = [ - ['identity', '\u0000yes mani !'], + ['identity', '\x00yes mani !'], ['base2', '001111001011001010111001100100000011011010110000101101110011010010010000000100001'], ['base8', '7362625631006654133464440102'], ['base10', '9573277761329450583662625'], diff --git a/test/spec-test3.spec.js b/test/spec-test3.spec.js index f7a1728..9a9c3ce 100644 --- a/test/spec-test3.spec.js +++ b/test/spec-test3.spec.js @@ -6,7 +6,7 @@ const multibase = require('../src') const constants = require('../src/constants.js') const input = 'hello world' const encoded = [ - ['identity', '\u0000hello world'], + ['identity', '\x00hello world'], ['base2', '00110100001100101011011000110110001101111001000000111011101101111011100100110110001100100'], ['base8', '7320625543306744035667562330620'], ['base10', '9126207244316550804821666916'], diff --git a/test/spec-test4.spec.js b/test/spec-test4.spec.js index a75dfa7..b33145d 100644 --- a/test/spec-test4.spec.js +++ b/test/spec-test4.spec.js @@ -6,7 +6,7 @@ const multibase = require('../src') const constants = require('../src/constants.js') const input = '\x00yes mani !' const encoded = [ - ['identity', '\u0000\x00yes mani !'], + ['identity', '\x00\x00yes mani !'], ['base2', '00000000001111001011001010111001100100000011011010110000101101110011010010010000000100001'], ['base8', '7000745453462015530267151100204'], ['base10', '90573277761329450583662625'], From 6af467f6907fca395c5b372ebd2d06327ac19c43 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Sun, 7 Jun 2020 18:36:37 +0100 Subject: [PATCH 09/11] fix: Make all encodings case-insensitive expect the ones that include upper and lower chars in the alphabet --- src/base.js | 2 +- src/index.js | 6 ++++++ test/spec-test1.spec.js | 2 +- test/spec-test6.spec.js | 3 +-- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/base.js b/src/base.js index c23b241..3b2235a 100644 --- a/src/base.js +++ b/src/base.js @@ -16,7 +16,7 @@ class Base { decode (string) { for (const char of string) { if (this.alphabet && this.alphabet.indexOf(char) < 0) { - throw new Error(`invalid ${this.name} character '${char}' in '${string}'`) + throw new Error(`invalid character '${char}' in '${string}'`) } } return this.engine.decode(string) diff --git a/src/index.js b/src/index.js index 0f307a7..5236d64 100644 --- a/src/index.js +++ b/src/index.js @@ -55,6 +55,12 @@ function decode (data) { if (Buffer.isBuffer(data)) { data = data.toString() } + const prefix = data[0] + + // Make all encodings case-insensitive expect the ones that include upper and lower chars in the alphabet + if (!['z', 'Z', 'm', 'M', 'u', 'U', '\x00'].includes(prefix)) { + data = data.toLocaleLowerCase() + } const enc = encoding(data[0]) return Buffer.from(enc.decode(data.substring(1))) } diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js index 32d9938..c0a630c 100644 --- a/test/spec-test1.spec.js +++ b/test/spec-test1.spec.js @@ -67,7 +67,7 @@ describe('spec test1', () => { const nonEncodedBuf = Buffer.from(base.code + '^!@$%!#$%@#y') expect(() => { multibase.decode(nonEncodedBuf) - }).to.throw(Error, `invalid ${name} character '^' in '^!@$%!#$%@#y'`) + }).to.throw(Error, 'invalid character \'^\' in \'^!@$%!#$%@#y\'') }) }) } diff --git a/test/spec-test6.spec.js b/test/spec-test6.spec.js index d3047c6..6d5e5be 100644 --- a/test/spec-test6.spec.js +++ b/test/spec-test6.spec.js @@ -19,8 +19,7 @@ const encoded = [ ['base36upper', 'KfUVrSIVVnFRbJWAJo'] ] -// we dont need to actually test these because we check the chars against the alphabet -describe.skip('spec test6', () => { +describe('spec test6', () => { for (const e of encoded) { const name = e[0] const output = e[1] From 1fc49d60f1815a644b57b6d624900bb630c5e9cd Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 9 Jun 2020 11:59:26 +0100 Subject: [PATCH 10/11] fix: feedback --- src/index.js | 6 +++--- test/spec-test1.spec.js | 4 +--- test/spec-test2.spec.js | 4 +--- test/spec-test3.spec.js | 4 +--- test/spec-test4.spec.js | 4 +--- test/spec-test5.spec.js | 4 +--- test/spec-test6.spec.js | 5 +---- 7 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/index.js b/src/index.js index 5236d64..a9915c9 100644 --- a/src/index.js +++ b/src/index.js @@ -57,9 +57,9 @@ function decode (data) { } const prefix = data[0] - // Make all encodings case-insensitive expect the ones that include upper and lower chars in the alphabet - if (!['z', 'Z', 'm', 'M', 'u', 'U', '\x00'].includes(prefix)) { - data = data.toLocaleLowerCase() + // Make all encodings case-insensitive except the ones that include upper and lower chars in the alphabet + if (['f', 'F', 'v', 'V', 't', 'T', 'b', 'B', 'c', 'C', 'h', 'k', 'K'].includes(prefix)) { + data = data.toLowerCase() } const enc = encoding(data[0]) return Buffer.from(enc.decode(data.substring(1))) diff --git a/test/spec-test1.spec.js b/test/spec-test1.spec.js index c0a630c..4b40c81 100644 --- a/test/spec-test1.spec.js +++ b/test/spec-test1.spec.js @@ -32,9 +32,7 @@ const encoded = [ ] describe('spec test1', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] + for (const [name, output] of encoded) { const base = constants.names[name] describe(name, () => { diff --git a/test/spec-test2.spec.js b/test/spec-test2.spec.js index 80c1f98..4493997 100644 --- a/test/spec-test2.spec.js +++ b/test/spec-test2.spec.js @@ -32,9 +32,7 @@ const encoded = [ ] describe('spec test2', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] + for (const [name, output] of encoded) { const base = constants.names[name] describe(name, () => { diff --git a/test/spec-test3.spec.js b/test/spec-test3.spec.js index 9a9c3ce..3cfba31 100644 --- a/test/spec-test3.spec.js +++ b/test/spec-test3.spec.js @@ -32,9 +32,7 @@ const encoded = [ ] describe('spec test3', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] + for (const [name, output] of encoded) { const base = constants.names[name] describe(name, () => { diff --git a/test/spec-test4.spec.js b/test/spec-test4.spec.js index b33145d..8c48cc1 100644 --- a/test/spec-test4.spec.js +++ b/test/spec-test4.spec.js @@ -32,9 +32,7 @@ const encoded = [ ] describe('spec test4', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] + for (const [name, output] of encoded) { const base = constants.names[name] describe(name, () => { diff --git a/test/spec-test5.spec.js b/test/spec-test5.spec.js index 044282e..a243d49 100644 --- a/test/spec-test5.spec.js +++ b/test/spec-test5.spec.js @@ -32,9 +32,7 @@ const encoded = [ ] describe('spec test5', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] + for (const [name, output] of encoded) { const base = constants.names[name] describe(name, () => { diff --git a/test/spec-test6.spec.js b/test/spec-test6.spec.js index 6d5e5be..da7ff01 100644 --- a/test/spec-test6.spec.js +++ b/test/spec-test6.spec.js @@ -20,10 +20,7 @@ const encoded = [ ] describe('spec test6', () => { - for (const e of encoded) { - const name = e[0] - const output = e[1] - + for (const [name, output] of encoded) { describe(name, () => { it('should decode string', () => { const out = multibase.decode(output) From 146dfb7bc974bdd26fc09930e85a38fc99e6af73 Mon Sep 17 00:00:00 2001 From: Hugo Dias Date: Tue, 9 Jun 2020 12:02:48 +0100 Subject: [PATCH 11/11] chore: remove commit lint --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3df7565..c7f74c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ jobs: include: - stage: check script: - - npx aegir commitlint --travis - npx aegir dep-check - npm run lint