urbit-ob

JavaScript utilities for phonemic base wrangling.
Log | Files | Refs | README

commit fcd249d382dc369870bcc89046595983f679edf4
parent a23d08314790f6edbe7f0df9bc64c00a23d4cfd0
Author: Jared Tobin <jared@jtobin.io>
Date:   Mon,  8 Oct 2018 10:09:11 +1300

Merge pull request #8 from urbit/jt-add-inv-patq

Add hex2patq and patq2hex functions
Diffstat:
Mpackage.json | 17++++++++++-------
Msrc/index.js | 25++++++++++++++++++++++++-
Mtest/core.test.js | 6++++++
Atest/property.js | 38++++++++++++++++++++++++++++++++++++++
4 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/package.json b/package.json @@ -4,7 +4,7 @@ "description": "utilities to convert urbit ship names back and forth between @p and @ud", "main": "dist/index.js", "scripts": { - "test": "jest", + "test": "jest test/*.test.js && mocha test/property.js", "build": "gulp" }, "keywords": [ @@ -15,21 +15,24 @@ "devDependencies": { "@babel/core": "^7.0.0", "@babel/preset-env": "^7.0.0-beta.34", - "isomorphic-webcrypto": "^1.6.0", + "babel-jest": "^23.4.2", + "chai": "^4.2.0", "gulp": "github:gulpjs/gulp#4.0", + "isomorphic-webcrypto": "^1.6.0", + "jest": "^23.5.0", + "jsverify": "^0.8.3", + "mocha": "^5.2.0", + "regenerator-runtime": "^0.12.1", "rollup-plugin-babel": "github:rollup/rollup-plugin-babel", "rollup-plugin-commonjs": "^8.3.0", "rollup-plugin-json": "^3.0.0", "rollup-plugin-node-builtins": "^2.1.2", + "rollup-plugin-node-globals": "^1.2.1", "rollup-plugin-node-resolve": "^3.3.0", "rollup-plugin-replace": "^2.0.0", "rollup-plugin-root-import": "^0.2.3", "rollup-stream": "^1.24.1", - "vinyl-source-stream": "^2.0.0", - "babel-jest": "^23.4.2", - "jest": "^23.5.0", - "regenerator-runtime": "^0.12.1", - "rollup-plugin-node-globals": "^1.2.1" + "vinyl-source-stream": "^2.0.0" }, "dependencies": { "babel-plugin-lodash": "^3.3.4", diff --git a/src/index.js b/src/index.js @@ -4,7 +4,7 @@ const bnjs = require('bn.js') const { reduce, concat, chunk, isUndefined, - isString, every, map } = require('lodash') + isString, every, map, split } = require('lodash') const raku = [ 3077398253, @@ -51,6 +51,12 @@ remlysfynwerrycsugnysnyllyndyndemluxfedsedbecmun\ lyrtesmudnytbyrsenwegfyrmurtelreptegpecnelnevfes\ ` +// split string at the indicated index +const splitAt = (index, str) => [str.slice(0, index), str.slice(index)]; + +// convert a decimal number to a hex string +const dec2hex = dec => dec.toString(16).padStart(2, '0') + // groups suffixes into array of syllables const suffixes = suf.match(/.{1,3}/g) @@ -398,6 +404,21 @@ const patq = (n) => { acc + (acc === '~' ? '' : '-') + name(elem), '~') } +hex2patq = hex => patq(new bnjs(hex, 'hex')) + +patq2hex = str => { + const chunks = split(str.slice(1), '-') + const splat = map(chunks, chunk => { + let syls = splitAt(3, chunk) + let hex = + syls[1] === '' + ? dec2hex(getSuffixIndex(syls[0])) + : dec2hex(getPrefixIndex(syls[0])) + + dec2hex(getSuffixIndex(syls[1])) + return hex + }) + return splat.join('') +} // returns the class of a ship from it's name const tierOfpatp = name => { @@ -544,6 +565,8 @@ module.exports = { patp: patp, patq: patq, + hex2patq: hex2patq, + patq2hex: patq2hex, sein: sein, _clan: clan, diff --git a/test/core.test.js b/test/core.test.js @@ -155,6 +155,12 @@ test('patq correctly encodes 0x1010101010101010102 as @q', () => { expect(ob.patq(input)).toBe(expected); }); +test('hex2patq works', () => { + let hex = '6d7920617765736f6d65207572626974207469636b65742c206920616d20736f206c75636b79'; + let expected = '~tastud-holruc-sidwet-salpel-taswet-holdeg-paddec-davdut-holdut-davwex-balwet-divwen-holdet-holruc-taslun-salpel-holtux-dacwex-baltud'; + expect(ob.hex2patq(hex)).toBe(expected); +}) + test('clan works as expected', () => { expect(ob._clan(new bnjs(0))).toBe('czar'); expect(ob._clan(new bnjs(255))).toBe('czar'); diff --git a/test/property.js b/test/property.js @@ -0,0 +1,38 @@ + +const expect = require('chai').expect +const jsc = require('jsverify') +const ob = require('../src') +const bn = require('bn.js') + +removeLeadingZeroBytes = str => + str.slice(0, 2) === '00' + ? removeLeadingZeroBytes(str.slice(2)) + : str + +eqModLeadingZeroBytes = (s, t) => + removeLeadingZeroBytes(s) === removeLeadingZeroBytes(t) + +describe('@q encoding', () => { + let hexString = jsc.string.smap( + x => Buffer.from(x).toString('hex'), + x => Buffer.from(x, 'hex').toString() + ) + + let patq = hexString.smap( + hex => ob.patq(new bn(hex, 'hex')), + pq => ob.patq2hex(pq) + ) + + it('patq2hex and hex2patq are inverses', () => { + let iso0 = jsc.forall(hexString, hex => + eqModLeadingZeroBytes(ob.patq2hex(ob.hex2patq(hex)), hex) + ) + + let iso1 = jsc.forall(patq, str => + ob.hex2patq(ob.patq2hex(str)) === str + ) + + jsc.assert(iso0) + jsc.assert(iso1) + }) +})