Skip to content

Commit c6c5c46

Browse files
authored
feat: add base256emoji (#187)
Ref: multiformats/multibase#88
1 parent 99e94ed commit c6c5c46

File tree

7 files changed

+66
-32
lines changed

7 files changed

+66
-32
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ import the ones you need yourself.
196196
`base64`, `base64pad`, `base64url`, `base64urlpad` | `multiformats/bases/base64` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) |
197197
`base58btc`, `base58flick4` | `multiformats/bases/base58` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) |
198198

199+
Other (less useful) bases implemented in [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) include: `base2`, `base8`, `base10`, `base36` and `base256emoji`.
200+
199201
### Multihash hashers
200202

201203
| hashes | import | repo |

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@
8080
"./bases/base64": {
8181
"import": "./src/bases/base64.js"
8282
},
83+
"./bases/base256emoji": {
84+
"import": "./src/bases/base256emoji.js"
85+
},
8386
"./hashes/hasher": {
8487
"import": "./src/hashes/hasher.js"
8588
},

src/bases/base.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ class Decoder {
8484
constructor (name, prefix, baseDecode) {
8585
this.name = name
8686
this.prefix = prefix
87+
/* c8 ignore next 3 */
88+
if (prefix.codePointAt(0) === undefined) {
89+
throw new Error('Invalid prefix character')
90+
}
91+
/** @private */
92+
this.prefixCodePoint = /** @type {number} */ (prefix.codePointAt(0))
8793
this.baseDecode = baseDecode
8894
}
8995

@@ -92,14 +98,10 @@ class Decoder {
9298
*/
9399
decode (text) {
94100
if (typeof text === 'string') {
95-
switch (text[0]) {
96-
case this.prefix: {
97-
return this.baseDecode(text.slice(1))
98-
}
99-
default: {
100-
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
101-
}
101+
if (text.codePointAt(0) !== this.prefixCodePoint) {
102+
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
102103
}
104+
return this.baseDecode(text.slice(this.prefix.length))
103105
} else {
104106
throw Error('Can only multibase decode strings')
105107
}

src/bases/base256emoji.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { from } from './base.js'
2+
3+
const alphabet = Array.from('🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥💾💿😂❤😍🤣😊🙏💕😭😘👍😅👏😁🔥🥰💔💖💙😢🤔😆🙄💪😉☺👌🤗💜😔😎😇🌹🤦🎉💞✌✨🤷😱😌🌸🙌😋💗💚😏💛🙂💓🤩😄😀🖤😃💯🙈👇🎶😒🤭❣😜💋👀😪😑💥🙋😞😩😡🤪👊🥳😥🤤👉💃😳✋😚😝😴🌟😬🙃🍀🌷😻😓⭐✅🥺🌈😈🤘💦✔😣🏃💐☹🎊💘😠☝😕🌺🎂🌻😐🖕💝🙊😹🗣💫💀👑🎵🤞😛🔴😤🌼😫⚽🤙☕🏆🤫👈😮🙆🍻🍃🐶💁😲🌿🧡🎁⚡🌞🎈❌✊👋😰🤨😶🤝🚶💰🍓💢🤟🙁🚨💨🤬✈🎀🍺🤓😙💟🌱😖👶🥴▶➡❓💎💸⬇😨🌚🦋😷🕺⚠🙅😟😵👎🤲🤠🤧📌🔵💅🧐🐾🍒😗🤑🌊🤯🐷☎💧😯💆👆🎤🙇🍑❄🌴💣🐸💌📍🥀🤢👅💡💩👐📸👻🤐🤮🎼🥵🚩🍎🍊👼💍📣🥂')
4+
const alphabetBytesToChars = /** @type {string[]} */ (alphabet.reduce((p, c, i) => { p[i] = c; return p }, /** @type {string[]} */([])))
5+
const alphabetCharsToBytes = /** @type {number[]} */ (alphabet.reduce((p, c, i) => { p[/** @type {number} */ (c.codePointAt(0))] = i; return p }, /** @type {number[]} */([])))
6+
7+
/**
8+
* @param {Uint8Array} data
9+
* @returns {string}
10+
*/
11+
function encode (data) {
12+
return data.reduce((p, c) => {
13+
p += alphabetBytesToChars[c]
14+
return p
15+
}, '')
16+
}
17+
18+
/**
19+
* @param {string} str
20+
* @returns {Uint8Array}
21+
*/
22+
function decode (str) {
23+
const byts = []
24+
for (const char of str) {
25+
const byt = alphabetCharsToBytes[/** @type {number} */ (char.codePointAt(0))]
26+
if (byt === undefined) {
27+
throw new Error(`Non-base256emoji character: ${char}`)
28+
}
29+
byts.push(byt)
30+
}
31+
return new Uint8Array(byts)
32+
}
33+
34+
export const base256emoji = from({
35+
prefix: '🚀',
36+
name: 'base256emoji',
37+
encode,
38+
decode
39+
})

src/basics.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as base32 from './bases/base32.js'
99
import * as base36 from './bases/base36.js'
1010
import * as base58 from './bases/base58.js'
1111
import * as base64 from './bases/base64.js'
12+
import * as base256emoji from './bases/base256emoji.js'
1213
import * as sha2 from './hashes/sha2.js'
1314
import * as identity from './hashes/identity.js'
1415

@@ -17,7 +18,7 @@ import * as json from './codecs/json.js'
1718

1819
import { CID, hasher, digest, varint, bytes } from './index.js'
1920

20-
const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64 }
21+
const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64, ...base256emoji }
2122
const hashes = { ...sha2, ...identity }
2223
const codecs = { raw, json }
2324

test/test-multibase-spec.js

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ const encoded = [
3535
['base64', 'mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'],
3636
['base64pad', 'MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='],
3737
['base64url', 'uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'],
38-
['base64urlpad', 'URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ==']
38+
['base64urlpad', 'URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='],
39+
['base256emoji', '🚀💛✋💃✋😻😈🥺🤤🍀🌟💐✋😅✋💦✋🥺🏃😈😴🌟😻😝👏👏']
3940
]
4041
},
4142
{
@@ -63,7 +64,8 @@ const encoded = [
6364
['base64', 'meWVzIG1hbmkgIQ'],
6465
['base64pad', 'MeWVzIG1hbmkgIQ=='],
6566
['base64url', 'ueWVzIG1hbmkgIQ'],
66-
['base64urlpad', 'UeWVzIG1hbmkgIQ==']
67+
['base64urlpad', 'UeWVzIG1hbmkgIQ=='],
68+
['base256emoji', '🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
6769
]
6870
},
6971
{
@@ -91,7 +93,8 @@ const encoded = [
9193
['base64', 'maGVsbG8gd29ybGQ'],
9294
['base64pad', 'MaGVsbG8gd29ybGQ='],
9395
['base64url', 'uaGVsbG8gd29ybGQ'],
94-
['base64urlpad', 'UaGVsbG8gd29ybGQ=']
96+
['base64urlpad', 'UaGVsbG8gd29ybGQ='],
97+
['base256emoji', '🚀😴✋🍀🍀😓😅✔😓🥺🍀😳']
9598
]
9699
},
97100
{
@@ -119,7 +122,8 @@ const encoded = [
119122
['base64', 'mAHllcyBtYW5pICE'],
120123
['base64pad', 'MAHllcyBtYW5pICE='],
121124
['base64url', 'uAHllcyBtYW5pICE'],
122-
['base64urlpad', 'UAHllcyBtYW5pICE=']
125+
['base64urlpad', 'UAHllcyBtYW5pICE='],
126+
['base256emoji', '🚀🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
123127
]
124128
},
125129
{
@@ -147,24 +151,8 @@ const encoded = [
147151
['base64', 'mAAB5ZXMgbWFuaSAh'],
148152
['base64pad', 'MAAB5ZXMgbWFuaSAh'],
149153
['base64url', 'uAAB5ZXMgbWFuaSAh'],
150-
['base64urlpad', 'UAAB5ZXMgbWFuaSAh']
151-
]
152-
},
153-
{
154-
input: 'hello world',
155-
tests: [
156-
['base16', 'f68656c6c6f20776f726c64'],
157-
['base16upper', 'F68656C6C6F20776F726C64'],
158-
['base32', 'bnbswy3dpeb3w64tmmq'],
159-
['base32upper', 'BNBSWY3DPEB3W64TMMQ'],
160-
['base32hex', 'vd1imor3f41rmusjccg'],
161-
['base32hexupper', 'VD1IMOR3F41RMUSJCCG'],
162-
['base32pad', 'cnbswy3dpeb3w64tmmq======'],
163-
['base32padupper', 'CNBSWY3DPEB3W64TMMQ======'],
164-
['base32hexpad', 'td1imor3f41rmusjccg======'],
165-
['base32hexpadupper', 'TD1IMOR3F41RMUSJCCG======'],
166-
['base36', 'kfuvrsivvnfrbjwajo'],
167-
['base36upper', 'KFUVRSIVVNFRBJWAJO']
154+
['base64urlpad', 'UAAB5ZXMgbWFuaSAh'],
155+
['base256emoji', '🚀🚀🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
168156
]
169157
}
170158
]
@@ -196,7 +184,6 @@ describe('spec test', () => {
196184
return this.skip()
197185
}
198186

199-
console.info('expect', `Non-${base.name} character`)
200187
assert.throws(() => base.decode(base.prefix + '^!@$%!#$%@#y'), `Non-${base.name} character`)
201188
})
202189
}

test/test-traversal.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ describe('traversal', () => {
163163
/** @type {[]} */
164164
const links = []
165165
const value = createNode(fromString('test'), links)
166-
const block = await main.encode({ value: value, codec, hasher })
166+
const block = await main.encode({ value, codec, hasher })
167167
const cid = block.cid
168168
const expectedCallArray = [cid.toString()]
169169
/** @type {string[]} */

0 commit comments

Comments
 (0)