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

Commit abc1595

Browse files
authored
feat: accept Uint8Arrays in place of Buffers (#61)
1 parent b4412bc commit abc1595

File tree

7 files changed

+148
-50
lines changed

7 files changed

+148
-50
lines changed

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
},
3636
"dependencies": {
3737
"base-x": "^3.0.8",
38-
"buffer": "^5.5.0"
38+
"buffer": "^5.5.0",
39+
"web-encoding": "^1.0.2"
3940
},
4041
"devDependencies": {
4142
"aegir": "^25.0.0",
@@ -62,4 +63,4 @@
6263
"theobat <[email protected]>",
6364
"Henrique Dias <[email protected]>"
6465
]
65-
}
66+
}

src/base.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1+
// @ts-check
12
'use strict'
23
const { Buffer } = require('buffer')
34

5+
/**
6+
* @typedef {Object} Codec
7+
* @property {function(Uint8Array):string} encode
8+
* @property {function(string):Uint8Array} decode
9+
*
10+
* @typedef {function(string):Codec} CodecFactory
11+
*/
12+
413
class Base {
14+
/**
15+
* @param {string} name
16+
* @param {string} code
17+
* @param {CodecFactory} implementation
18+
* @param {string} alphabet
19+
*/
520
constructor (name, code, implementation, alphabet) {
621
this.name = name
722
this.code = code
@@ -10,10 +25,18 @@ class Base {
1025
this.engine = implementation(alphabet)
1126
}
1227

28+
/**
29+
* @param {Uint8Array} buf
30+
* @returns {string}
31+
*/
1332
encode (buf) {
1433
return this.engine.encode(buf)
1534
}
1635

36+
/**
37+
* @param {string} string
38+
* @returns {Uint8Array}
39+
*/
1740
decode (string) {
1841
for (const char of string) {
1942
if (this.alphabet && this.alphabet.indexOf(char) < 0) {

src/constants.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1+
// @ts-check
12
'use strict'
23

34
const baseX = require('base-x')
4-
const { Buffer } = require('buffer')
55
const Base = require('./base.js')
66
const rfc4648 = require('./rfc4648')
7+
const { decodeText, encodeText } = require('./util')
78

89
const identity = () => {
910
return {
10-
encode: (data) => Buffer.from(data).toString(),
11-
decode: (string) => Buffer.from(string)
11+
encode: decodeText,
12+
decode: encodeText
1213
}
1314
}
1415

15-
// name, code, implementation, alphabet
16+
/**
17+
* @typedef {import('./base').CodecFactory} CodecFactory
18+
*
19+
* name, code, implementation, alphabet
20+
* @type {Array<[string, string, CodecFactory, string]>}
21+
*/
1622
const constants = [
1723
['identity', '\x00', identity, ''],
1824
['base2', '0', rfc4648(1), '01'],

src/index.js

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @ts-check
12
/**
23
* Implementation of the [multibase](https://github.com/multiformats/multibase) specification.
34
*
@@ -7,31 +8,37 @@
78

89
const { Buffer } = require('buffer')
910
const constants = require('./constants')
11+
const { decodeText, asBuffer } = require('./util')
1012

1113
/** @typedef {import("./base")} Base */
1214

1315
/**
1416
* Create a new buffer with the multibase varint+code.
1517
*
1618
* @param {string|number} nameOrCode - The multibase name or code number.
17-
* @param {Buffer} buf - The data to be prefixed with multibase.
19+
* @param {Uint8Array} buf - The data to be prefixed with multibase.
1820
* @returns {Buffer}
1921
* @throws {Error} Will throw if the encoding is not supported
2022
*/
2123
function multibase (nameOrCode, buf) {
2224
if (!buf) {
2325
throw new Error('requires an encoded buffer')
2426
}
25-
const enc = encoding(nameOrCode)
26-
validEncode(enc.name, buf)
27-
return Buffer.concat([enc.codeBuf, buf])
27+
const { name, codeBuf } = encoding(nameOrCode)
28+
validEncode(name, buf)
29+
30+
const buffer = Buffer.alloc(codeBuf.length + buf.length)
31+
buffer.set(codeBuf, 0)
32+
buffer.set(buf, codeBuf.length)
33+
34+
return buffer
2835
}
2936

3037
/**
3138
* Encode data with the specified base and add the multibase prefix.
3239
*
3340
* @param {string|number} nameOrCode - The multibase name or code number.
34-
* @param {Buffer} buf - The data to be encoded.
41+
* @param {Uint8Array} buf - The data to be encoded.
3542
* @returns {Buffer}
3643
* @throws {Error} Will throw if the encoding is not supported
3744
*
@@ -43,17 +50,17 @@ function encode (nameOrCode, buf) {
4350
}
4451

4552
/**
46-
* Takes a buffer or string encoded with multibase header, decodes it and
53+
* Takes a Uint8Array or string encoded with multibase header, decodes it and
4754
* returns the decoded buffer
4855
*
49-
* @param {Buffer|string} data
56+
* @param {Uint8Array|string} data
5057
* @returns {Buffer}
5158
* @throws {Error} Will throw if the encoding is not supported
5259
*
5360
*/
5461
function decode (data) {
55-
if (Buffer.isBuffer(data)) {
56-
data = data.toString()
62+
if (ArrayBuffer.isView(data)) {
63+
data = decodeText(data)
5764
}
5865
const prefix = data[0]
5966

@@ -62,18 +69,18 @@ function decode (data) {
6269
data = data.toLowerCase()
6370
}
6471
const enc = encoding(data[0])
65-
return Buffer.from(enc.decode(data.substring(1)))
72+
return asBuffer(enc.decode(data.substring(1)))
6673
}
6774

6875
/**
6976
* Is the given data multibase encoded?
7077
*
71-
* @param {Buffer|string} data
72-
* @returns {boolean}
78+
* @param {Uint8Array|string} data
79+
* @returns {false|string}
7380
*/
7481
function isEncoded (data) {
75-
if (Buffer.isBuffer(data)) {
76-
data = data.toString()
82+
if (data instanceof Uint8Array) {
83+
data = decodeText(data)
7784
}
7885

7986
// Ensure bufOrString is a string
@@ -93,19 +100,19 @@ function isEncoded (data) {
93100
* Validate encoded data
94101
*
95102
* @param {string} name
96-
* @param {Buffer} buf
97-
* @returns {undefined}
103+
* @param {Uint8Array} buf
104+
* @returns {void}
98105
* @throws {Error} Will throw if the encoding is not supported
99106
*/
100107
function validEncode (name, buf) {
101108
const enc = encoding(name)
102-
enc.decode(buf.toString())
109+
enc.decode(decodeText(buf))
103110
}
104111

105112
/**
106113
* Get the encoding by name or code
107114
*
108-
* @param {string} nameOrCode
115+
* @param {string|number} nameOrCode
109116
* @returns {Base}
110117
* @throws {Error} Will throw if the encoding is not supported
111118
*/
@@ -122,13 +129,13 @@ function encoding (nameOrCode) {
122129
/**
123130
* Get encoding from data
124131
*
125-
* @param {string|Buffer} data
132+
* @param {string|Uint8Array} data
126133
* @returns {Base}
127134
* @throws {Error} Will throw if the encoding is not supported
128135
*/
129136
function encodingFromData (data) {
130-
if (Buffer.isBuffer(data)) {
131-
data = data.toString()
137+
if (data instanceof Uint8Array) {
138+
data = decodeText(data)
132139
}
133140

134141
return encoding(data[0])

src/rfc4648.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
// @ts-check
12
'use strict'
23

4+
/** @typedef {import('./base').CodecFactory} CodecFactory */
5+
6+
/**
7+
* @param {string} string
8+
* @param {string} alphabet
9+
* @param {number} bitsPerChar
10+
* @returns {Uint8Array}
11+
*/
312
const decode = (string, alphabet, bitsPerChar) => {
413
// Build the character lookup table:
514
const codes = {}
@@ -46,6 +55,12 @@ const decode = (string, alphabet, bitsPerChar) => {
4655
return out
4756
}
4857

58+
/**
59+
* @param {Uint8Array} data
60+
* @param {string} alphabet
61+
* @param {number} bitsPerChar
62+
* @returns {string}
63+
*/
4964
const encode = (data, alphabet, bitsPerChar) => {
5065
const pad = alphabet[alphabet.length - 1] === '='
5166
const mask = (1 << bitsPerChar) - 1
@@ -80,11 +95,23 @@ const encode = (data, alphabet, bitsPerChar) => {
8095
return out
8196
}
8297

98+
/**
99+
* @param {number} bitsPerChar
100+
* @returns {CodecFactory}
101+
*/
83102
module.exports = (bitsPerChar) => (alphabet) => {
84103
return {
104+
/**
105+
* @param {Uint8Array} input
106+
* @returns {string}
107+
*/
85108
encode (input) {
86109
return encode(input, alphabet, bitsPerChar)
87110
},
111+
/**
112+
* @param {string} input
113+
* @returns {Uint8Array}
114+
*/
88115
decode (input) {
89116
return decode(input, alphabet, bitsPerChar)
90117
}

src/util.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// @ts-check
2+
'use strict'
3+
4+
const { Buffer } = require('buffer')
5+
const { TextEncoder, TextDecoder } = require('web-encoding')
6+
7+
const textDecoder = new TextDecoder()
8+
/**
9+
* @param {ArrayBufferView|ArrayBuffer} bytes
10+
* @returns {string}
11+
*/
12+
const decodeText = (bytes) => textDecoder.decode(bytes)
13+
14+
const textEncoder = new TextEncoder()
15+
/**
16+
* @param {string} text
17+
* @returns {Uint8Array}
18+
*/
19+
const encodeText = (text) => textEncoder.encode(text)
20+
21+
/**
22+
* @param {ArrayBufferView} bytes
23+
* @returns {Buffer}
24+
*/
25+
const asBuffer = ({ buffer, byteLength, byteOffset }) =>
26+
Buffer.from(buffer, byteOffset, byteLength)
27+
28+
module.exports = { decodeText, encodeText, asBuffer }

0 commit comments

Comments
 (0)