urbit-ob

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

commit c91e9175c1ee346af2eb8b90abc90202c96b9aac
parent 86d0d8e4dc7fe04d321ca18d42c68c6f60005dbf
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat, 10 Nov 2018 20:42:19 +1300

Merge pull request #15 from urbit/jt-padding

Preserve leading zeros bytes in patq2hex/hex2patq
Diffstat:
Mpackage.json | 2+-
Msrc/index.js | 34+---------------------------------
Msrc/internal/co.js | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mtest/co.test.js | 17+++++++++++++----
4 files changed, 105 insertions(+), 47 deletions(-)

diff --git a/package.json b/package.json @@ -1,6 +1,6 @@ { "name": "urbit-ob", - "version": "3.0.0", + "version": "3.0.1", "description": "Utilities for Hoon-style atom printing and conversion", "main": "src/index.js", "scripts": { diff --git a/src/index.js b/src/index.js @@ -2,40 +2,8 @@ const co = require('./internal/co') const ob = require('./internal/ob') -/** - * Remove all leading zero bytes from a hex-encoded string. - * @param {string} str a hex encoded string - * @return {string} - */ -const removeLeadingZeroBytes = str => - str.slice(0, 2) === '00' - ? removeLeadingZeroBytes(str.slice(2)) - : str - -/** - * Equality comparison, modulo leading zero bytes. - * @param {string} s a hex-encoded string - * @param {string} t a hex-encoded string - * @return {bool} - */ -const eqModLeadingZeroBytes = (s, t) => - removeLeadingZeroBytes(s) === removeLeadingZeroBytes(t) - -/** - * Equality comparison on @q values. - * @param {string} p a @q-encoded string - * @param {string} q a @q-encoded string - * @return {bool} - */ -const eqPatq = (p, q) => { - const phex = co.patq2hex(p) - const qhex = co.patq2hex(q) - return eqModLeadingZeroBytes(phex, qhex) -} - module.exports = Object.assign( co, - ob, - { eqPatq } + ob ) diff --git a/src/internal/co.js b/src/internal/co.js @@ -126,6 +126,15 @@ const hex2patp = (hex) => patp(new BN(hex, 'hex')) /** + * Convert a Buffer to a @p-encoded string. + * + * @param {Buffer} buf + * @return {String} + */ +const buf2patp = (buf) => + hex2patp(buf.toString('hex')) + +/** * Convert a @p-encoded string to a hex-encoded string. * * @param {String} name @p @@ -147,10 +156,22 @@ const patp2hex = (name) => { '') const bn = new BN(addr, 2) - return ob.fend(bn).toString('hex') + const hex = ob.fend(bn).toString('hex') + return hex.length % 2 !== 0 + ? hex.padStart(hex.length + 1, '0') + : hex } /** + * Convert a @p-encoded string to a Buffer. + * + * @param {String} name + * @return {Buffer} + */ +const patp2buf = name => + Buffer.from(patp2hex(name), 'hex') + +/** * Convert a @p-encoded string to a bignum. * * @param {String} name @p @@ -175,10 +196,18 @@ const patp2dec = name => * @return {String} */ const patq = (arg) => { - const n = new BN(arg) - - const buf = n.toArrayLike(Buffer) + const bn = new BN(arg) + const buf = bn.toArrayLike(Buffer) + return buf2patq(buf) +} +/** + * Convert a Buffer into a @q-encoded string. + * + * @param {Buffer} buf + * @return {String} + */ +const buf2patq = buf => { const chunked = buf.length % 2 !== 0 && buf.length > 1 ? lodash.concat([[buf[0]]], lodash.chunk(buf.slice(1), 2)) @@ -206,11 +235,20 @@ const patq = (arg) => { /** * Convert a hex-encoded string to a @q-encoded string. * + * Note that this preserves leading zero bytes. + * * @param {String} hex * @return {String} */ -const hex2patq = hex => - patq(new BN(hex, 'hex')) +const hex2patq = arg => { + const hex = + arg.length % 2 !== 0 + ? arg.padStart(arg.length + 1, '0') + : arg + + const buf = Buffer.from(hex, 'hex') + return buf2patq(buf) +} /** * Convert a @q-encoded string to a hex-encoded string. @@ -251,6 +289,17 @@ const patq2bn = name => new BN(patq2hex(name), 'hex') /** + * Convert a @q-encoded string to a Buffer. + * + * @param {String} name @q + * @return {Buffer} + */ +const patq2buf = name => { + const hex = patq2hex(name) + return Buffer.from(hex, 'hex') +} + +/** * Convert a @q-encoded string to a decimal-encoded string. * * @param {String} name @q @@ -328,15 +377,47 @@ const isValidPat = name => { return leadingTilde && !wrongLength && sylsExist } +/** + * Remove all leading zero bytes from a sliceable value. + * @param {String, Buffer, Array} + * @return {String} + */ +const removeLeadingZeroBytes = str => + str.slice(0, 2) === '00' + ? removeLeadingZeroBytes(str.slice(2)) + : str + +/** + * Equality comparison, modulo leading zero bytes. + * @param {String, Buffer, Array} + * @param {String, Buffer, Array} + * @return {Bool} + */ +const eqModLeadingZeroBytes = (s, t) => + lodash.isEqual(removeLeadingZeroBytes(s), removeLeadingZeroBytes(t)) + +/** + * Equality comparison on @q values. + * @param {String} p a @q-encoded string + * @param {String} q a @q-encoded string + * @return {Bool} + */ +const eqPatq = (p, q) => { + const phex = patq2hex(p) + const qhex = patq2hex(q) + return eqModLeadingZeroBytes(phex, qhex) +} + module.exports = { patp, patp2hex, - patp2dec, hex2patp, + patp2dec, patq, patq2hex, - patq2dec, hex2patq, + patq2dec, clan, - sein + sein, + eqPatq } diff --git a/test/co.test.js b/test/co.test.js @@ -1,17 +1,18 @@ const BN = require('bn.js') -const { expect } = require('chai'); +const { expect } = require('chai') const jsc = require('jsverify') const { patp, patp2hex, - patp2dec, hex2patp, + patp2dec, patq, patq2hex, - patq2dec, hex2patq, + patq2dec, clan, - sein + sein, + eqPatq } = require('../src/internal/co') const patps = jsc.uint32.smap( @@ -190,3 +191,11 @@ describe('clan/sein', () => { }) +describe('eqPatq', () => { + it('works as expected', () => { + expect(eqPatq('~dozzod-dozzod', '~zod')).to.equal(true) + expect(eqPatq('~dozzod-mardun', '~mardun')).to.equal(true) + expect(eqPatq('~dozzod-mardun', '~mardun-dozzod')).to.equal(false) + }) +}) +