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

feat: more encoding, errors, spec tests #55

Merged
merged 11 commits into from Jun 9, 2020
114 changes: 22 additions & 92 deletions README.md
Expand Up @@ -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 `<script>` tag](#in-the-browser-through-script-tag)
- [Gotchas](#gotchas)
- [Usage](#usage)
- [Example](#example)
- [API](#api)
Expand All @@ -37,10 +35,10 @@ js-multibase

## Install

### In Node.js through npm
### NPM

```bash
> npm install --save multibase
```sh
$ npm install --save multibase
```

The type definitions for this package are available on http://definitelytyped.org/. To install just use:
Expand All @@ -49,23 +47,12 @@ The type definitions for this package are available on http://definitelytyped.or
$ npm install -D @types/multibase
```

### Browser: Browserify, Webpack, other bundlers

The code published to npm that gets loaded on require is in fact an ES5 transpiled version with the right shims added. This means that you can require it and use with your favourite bundler without having to adjust asset management process.

```js
const multibase = require('multibase')
```


### In the Browser through `<script>` tag

Loading this module through a script tag will make the ```Multibase``` obj available in the global namespace.

```html
<script src="https://unpkg.com/multibase/dist/index.min.js"></script>
<!-- OR -->
<script src="https://unpkg.com/multibase/dist/index.js"></script>
```

## Usage
Expand All @@ -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(<nameOrCode>, 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(<nameOrCode>, <buf>)
```

### `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

Expand Down
23 changes: 23 additions & 0 deletions 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 })
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -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"
Expand Down
22 changes: 11 additions & 11 deletions src/base.js
Expand Up @@ -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)
}
}

Expand Down
21 changes: 0 additions & 21 deletions src/base16.js

This file was deleted.

81 changes: 0 additions & 81 deletions src/base32.js

This file was deleted.

44 changes: 0 additions & 44 deletions src/base64.js

This file was deleted.