Compare commits

...

1 Commits

Author SHA1 Message Date
vrancurel a3307b1d80 ft: S3C-4456 tiny version IDs
For some applications like OSE.
2021-06-09 16:12:07 -07:00
4 changed files with 130 additions and 1 deletions

View File

@ -6,6 +6,10 @@
// - rep_group_id 07 bytes replication group identifier
// - other_information arbitrary user input, such as a unique string
const base62Integer = require('base62');
const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const base62String = require('base-x')(BASE62);
// the lengths of the components in bytes
const LENGTH_TS = 14; // timestamp: epoch in ms
const LENGTH_SEQ = 6; // position in ms slot
@ -152,4 +156,61 @@ function decode(str) {
}
}
module.exports = { generateVersionId, getInfVid, encode, decode };
/* tiny version Ids constants:
*
* Note: base62Integer() cannot encode integers larger than 15 digits
* so we assume that TV_TOTAL <= 30 and we cut it in half. Please
* revisit if TV_TOTAL is greater than 30.
*/
const TV_TOTAL = LENGTH_TS + LENGTH_SEQ;
const TV_HALF = TV_TOTAL / 2;
const TV_EPAD = '0'.repeat(Math.ceil(TV_HALF * (Math.log(10) / Math.log(62))));
const TV_DPAD = '0'.repeat(TV_HALF);
/**
* Encode a versionId to obscure internal information contained
* in a version ID (less than 40 bytes).
*
* @param {string} str - the versionId to encode
* @return {string} - the encoded tinyVersionId
*/
function tinyEncode(str) {
const part1 = Number(str.substring(0, TV_HALF));
const part2 = Number(str.substring(TV_HALF, TV_TOTAL));
const part3 = Buffer.from(str.substring(TV_TOTAL));
const enc1 = base62Integer.encode(part1);
const enc2 = base62Integer.encode(part2);
const enc3 = base62String.encode(part3);
return (TV_EPAD + enc1).slice(-TV_EPAD.length) +
(TV_EPAD + enc2).slice(-TV_EPAD.length) +
enc3;
}
/**
* Decode a tinyVersionId. May return an error if the input string is
* invalid hex string or results in an invalid value.
*
* @param {string} str - the encoded tinyVersionId to decode
* @return {(string|Error)} - the decoded versionId or an error
*/
function tinyDecode(str) {
try {
let start = 0;
const enc1 = str.substring(start, start + TV_EPAD.length);
const orig1 = base62Integer.decode(enc1);
start += TV_EPAD.length;
const enc2 = str.substring(start, start + TV_EPAD.length);
const orig2 = base62Integer.decode(enc2);
start += TV_EPAD.length;
const enc3 = str.substring(start);
const orig3 = base62String.decode(enc3);
return (TV_DPAD + orig1.toString()).slice(-TV_DPAD.length) +
(TV_DPAD + orig2.toString()).slice(-TV_DPAD.length) +
orig3.toString();
} catch (err) {
// in case of exceptions caused by base62 libs
return err;
}
}
module.exports = { generateVersionId, getInfVid, encode, decode, tinyEncode, tinyDecode };

View File

@ -22,6 +22,8 @@
"agentkeepalive": "^4.1.3",
"ajv": "6.12.2",
"async": "~2.1.5",
"base62": "2.0.1",
"base-x": "3.0.8",
"debug": "~2.6.9",
"diskusage": "^1.1.1",
"ioredis": "4.9.5",

View File

@ -39,4 +39,53 @@ describe('test generating versionIds', () => {
assert.strictEqual(vids.length, count);
assert.deepStrictEqual(vids, decoded);
});
it('simple tiny version test', () => {
const vid = '98376906954349999999RG001 145.20.5';
const encoded = VID.tinyEncode(vid);
assert.strictEqual(encoded, 'aJLWKz4Ko9IjBBgXKj5KQT2G9UHv0g7P');
const decoded = VID.tinyDecode(encoded);
assert.strictEqual(vid, decoded);
});
it('tiny version test with smaller part1 number', () => {
const vid = '00000000054349999999RG001 145.20.5';
const encoded = VID.tinyEncode(vid);
const decoded = VID.tinyDecode(encoded);
assert.strictEqual(vid, decoded);
});
it('tiny version test with smaller part2 number', () => {
const vid = '98376906950000099999RG001 145.20.5';
const encoded = VID.tinyEncode(vid);
const decoded = VID.tinyDecode(encoded);
assert.strictEqual(vid, decoded);
});
it('tiny version test with smaller part3', () => {
const vid = '98376906950000099999R1 145.20.5';
const encoded = VID.tinyEncode(vid);
const decoded = VID.tinyDecode(encoded);
assert.strictEqual(vid, decoded);
});
it('tiny version test with smaller part3 - 2', () => {
const vid = '98376906950000099999R1x';
const encoded = VID.tinyEncode(vid);
const decoded = VID.tinyDecode(encoded);
assert.strictEqual(vid, decoded);
});
it('error case: when invalid tiny key part 3 has invalid base62 character', () => {
const invalidTinyVersionId = 'aJLWKz4Ko9IjBBgXKj5KQT.G9UHv0g7P';
const decoded = VID.tinyDecode(invalidTinyVersionId);
assert(decoded instanceof Error);
});
it('should encode and decode tiny versionIds', () => {
const encoded = vids.map(vid => VID.tinyEncode(vid));
const decoded = encoded.map(vid => VID.tinyDecode(vid));
assert.strictEqual(vids.length, count);
assert.deepStrictEqual(vids, decoded);
});
});

View File

@ -250,6 +250,18 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
base-x@3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
dependencies:
safe-buffer "^5.0.1"
base62@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/base62/-/base62-2.0.1.tgz#729cfe179ed34c61e4a489490105b44ce4ea1197"
integrity sha512-4t4WQK7mdbcWzqEBiq6tfo2qDCeIZGXvjifJZyxHIVcjQkZJxpFtu/pa2Va69OouCkg6izZ08hKnPxroeDyzew==
base64-arraybuffer@0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
@ -2191,6 +2203,11 @@ rx-lite@^3.1.2:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
integrity sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=
safe-buffer@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"