Skip to content

Commit

Permalink
feat: add base256emoji (#187)
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg authored Jun 23, 2022
1 parent 99e94ed commit c6c5c46
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 32 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ import the ones you need yourself.
`base64`, `base64pad`, `base64url`, `base64urlpad` | `multiformats/bases/base64` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) |
`base58btc`, `base58flick4` | `multiformats/bases/base58` | [multiformats/js-multiformats](https://github.com/multiformats/js-multiformats/tree/master/bases) |

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

### Multihash hashers

| hashes | import | repo |
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@
"./bases/base64": {
"import": "./src/bases/base64.js"
},
"./bases/base256emoji": {
"import": "./src/bases/base256emoji.js"
},
"./hashes/hasher": {
"import": "./src/hashes/hasher.js"
},
Expand Down
16 changes: 9 additions & 7 deletions src/bases/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ class Decoder {
constructor (name, prefix, baseDecode) {
this.name = name
this.prefix = prefix
/* c8 ignore next 3 */
if (prefix.codePointAt(0) === undefined) {
throw new Error('Invalid prefix character')
}
/** @private */
this.prefixCodePoint = /** @type {number} */ (prefix.codePointAt(0))
this.baseDecode = baseDecode
}

Expand All @@ -92,14 +98,10 @@ class Decoder {
*/
decode (text) {
if (typeof text === 'string') {
switch (text[0]) {
case this.prefix: {
return this.baseDecode(text.slice(1))
}
default: {
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
}
if (text.codePointAt(0) !== this.prefixCodePoint) {
throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`)
}
return this.baseDecode(text.slice(this.prefix.length))
} else {
throw Error('Can only multibase decode strings')
}
Expand Down
39 changes: 39 additions & 0 deletions src/bases/base256emoji.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { from } from './base.js'

const alphabet = Array.from('🚀🪐☄🛰🌌🌑🌒🌓🌔🌕🌖🌗🌘🌍🌏🌎🐉☀💻🖥💾💿😂❤😍🤣😊🙏💕😭😘👍😅👏😁🔥🥰💔💖💙😢🤔😆🙄💪😉☺👌🤗💜😔😎😇🌹🤦🎉💞✌✨🤷😱😌🌸🙌😋💗💚😏💛🙂💓🤩😄😀🖤😃💯🙈👇🎶😒🤭❣😜💋👀😪😑💥🙋😞😩😡🤪👊🥳😥🤤👉💃😳✋😚😝😴🌟😬🙃🍀🌷😻😓⭐✅🥺🌈😈🤘💦✔😣🏃💐☹🎊💘😠☝😕🌺🎂🌻😐🖕💝🙊😹🗣💫💀👑🎵🤞😛🔴😤🌼😫⚽🤙☕🏆🤫👈😮🙆🍻🍃🐶💁😲🌿🧡🎁⚡🌞🎈❌✊👋😰🤨😶🤝🚶💰🍓💢🤟🙁🚨💨🤬✈🎀🍺🤓😙💟🌱😖👶🥴▶➡❓💎💸⬇😨🌚🦋😷🕺⚠🙅😟😵👎🤲🤠🤧📌🔵💅🧐🐾🍒😗🤑🌊🤯🐷☎💧😯💆👆🎤🙇🍑❄🌴💣🐸💌📍🥀🤢👅💡💩👐📸👻🤐🤮🎼🥵🚩🍎🍊👼💍📣🥂')
const alphabetBytesToChars = /** @type {string[]} */ (alphabet.reduce((p, c, i) => { p[i] = c; return p }, /** @type {string[]} */([])))
const alphabetCharsToBytes = /** @type {number[]} */ (alphabet.reduce((p, c, i) => { p[/** @type {number} */ (c.codePointAt(0))] = i; return p }, /** @type {number[]} */([])))

/**
* @param {Uint8Array} data
* @returns {string}
*/
function encode (data) {
return data.reduce((p, c) => {
p += alphabetBytesToChars[c]
return p
}, '')
}

/**
* @param {string} str
* @returns {Uint8Array}
*/
function decode (str) {
const byts = []
for (const char of str) {
const byt = alphabetCharsToBytes[/** @type {number} */ (char.codePointAt(0))]
if (byt === undefined) {
throw new Error(`Non-base256emoji character: ${char}`)
}
byts.push(byt)
}
return new Uint8Array(byts)
}

export const base256emoji = from({
prefix: '🚀',
name: 'base256emoji',
encode,
decode
})
3 changes: 2 additions & 1 deletion src/basics.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as base32 from './bases/base32.js'
import * as base36 from './bases/base36.js'
import * as base58 from './bases/base58.js'
import * as base64 from './bases/base64.js'
import * as base256emoji from './bases/base256emoji.js'
import * as sha2 from './hashes/sha2.js'
import * as identity from './hashes/identity.js'

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

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

const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64 }
const bases = { ...identityBase, ...base2, ...base8, ...base10, ...base16, ...base32, ...base36, ...base58, ...base64, ...base256emoji }
const hashes = { ...sha2, ...identity }
const codecs = { raw, json }

Expand Down
33 changes: 10 additions & 23 deletions test/test-multibase-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ const encoded = [
['base64', 'mRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'],
['base64pad', 'MRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='],
['base64url', 'uRGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ'],
['base64urlpad', 'URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ==']
['base64urlpad', 'URGVjZW50cmFsaXplIGV2ZXJ5dGhpbmchIQ=='],
['base256emoji', '🚀💛✋💃✋😻😈🥺🤤🍀🌟💐✋😅✋💦✋🥺🏃😈😴🌟😻😝👏👏']
]
},
{
Expand Down Expand Up @@ -63,7 +64,8 @@ const encoded = [
['base64', 'meWVzIG1hbmkgIQ'],
['base64pad', 'MeWVzIG1hbmkgIQ=='],
['base64url', 'ueWVzIG1hbmkgIQ'],
['base64urlpad', 'UeWVzIG1hbmkgIQ==']
['base64urlpad', 'UeWVzIG1hbmkgIQ=='],
['base256emoji', '🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
]
},
{
Expand Down Expand Up @@ -91,7 +93,8 @@ const encoded = [
['base64', 'maGVsbG8gd29ybGQ'],
['base64pad', 'MaGVsbG8gd29ybGQ='],
['base64url', 'uaGVsbG8gd29ybGQ'],
['base64urlpad', 'UaGVsbG8gd29ybGQ=']
['base64urlpad', 'UaGVsbG8gd29ybGQ='],
['base256emoji', '🚀😴✋🍀🍀😓😅✔😓🥺🍀😳']
]
},
{
Expand Down Expand Up @@ -119,7 +122,8 @@ const encoded = [
['base64', 'mAHllcyBtYW5pICE'],
['base64pad', 'MAHllcyBtYW5pICE='],
['base64url', 'uAHllcyBtYW5pICE'],
['base64urlpad', 'UAHllcyBtYW5pICE=']
['base64urlpad', 'UAHllcyBtYW5pICE='],
['base256emoji', '🚀🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
]
},
{
Expand Down Expand Up @@ -147,24 +151,8 @@ const encoded = [
['base64', 'mAAB5ZXMgbWFuaSAh'],
['base64pad', 'MAAB5ZXMgbWFuaSAh'],
['base64url', 'uAAB5ZXMgbWFuaSAh'],
['base64urlpad', 'UAAB5ZXMgbWFuaSAh']
]
},
{
input: 'hello world',
tests: [
['base16', 'f68656c6c6f20776f726c64'],
['base16upper', 'F68656C6C6F20776F726C64'],
['base32', 'bnbswy3dpeb3w64tmmq'],
['base32upper', 'BNBSWY3DPEB3W64TMMQ'],
['base32hex', 'vd1imor3f41rmusjccg'],
['base32hexupper', 'VD1IMOR3F41RMUSJCCG'],
['base32pad', 'cnbswy3dpeb3w64tmmq======'],
['base32padupper', 'CNBSWY3DPEB3W64TMMQ======'],
['base32hexpad', 'td1imor3f41rmusjccg======'],
['base32hexpadupper', 'TD1IMOR3F41RMUSJCCG======'],
['base36', 'kfuvrsivvnfrbjwajo'],
['base36upper', 'KFUVRSIVVNFRBJWAJO']
['base64urlpad', 'UAAB5ZXMgbWFuaSAh'],
['base256emoji', '🚀🚀🚀🏃✋🌈😅🌷🤤😻🌟😅👏']
]
}
]
Expand Down Expand Up @@ -196,7 +184,6 @@ describe('spec test', () => {
return this.skip()
}

console.info('expect', `Non-${base.name} character`)
assert.throws(() => base.decode(base.prefix + '^!@$%!#$%@#y'), `Non-${base.name} character`)
})
}
Expand Down
2 changes: 1 addition & 1 deletion test/test-traversal.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ describe('traversal', () => {
/** @type {[]} */
const links = []
const value = createNode(fromString('test'), links)
const block = await main.encode({ value: value, codec, hasher })
const block = await main.encode({ value, codec, hasher })
const cid = block.cid
const expectedCallArray = [cid.toString()]
/** @type {string[]} */
Expand Down

0 comments on commit c6c5c46

Please sign in to comment.