Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make compact encoding of UUIDs #135

Draft
wants to merge 1 commit into
base: next
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Make compact encoding of UUIDs
aravindet committed Sep 27, 2021
commit 8fa188462d2f09e0a08536eaa1d2f821b236e020
2 changes: 1 addition & 1 deletion src/common/coding/base64.js
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ export function encode(u8Arr) {
}

export function decode(string, start = 0) {
const buffer = new ArrayBuffer(Math.floor(((string.length - start) * 3) / 4));
const buffer = new ArrayBuffer(Math.ceil(((string.length - start) * 3) / 4));
const view = new DataView(buffer);

for (let i = start; i < string.length; i += 4) {
42 changes: 38 additions & 4 deletions src/common/coding/id.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,39 @@
import { customAlphabet } from 'nanoid/non-secure';
import alpha from './alphabet.js';
import { v4 } from 'uuid';
import { encode as b64encode, decode as b64decode } from './base64.js';

// The ID is 20 digits for a similar collision probability as UUID v4
export default customAlphabet(alpha, 20);
function doEncode(u8arr) {
const { buffer, byteOffset, byteLength } = u8arr;
const view = new DataView(buffer, byteOffset, byteLength);
const int3 = view.getUint32(8);
const int4 = view.getUint32(12);
if (int3 >>> 30 !== 2) throw Error('encode_uuid.not_variant_1');

view.setUint32(8, ((int3 & 0x3fffffff) << 2) | (int4 >>> 30));
view.setUint32(12, (int4 & 0x3fffffff) << 2);

return b64encode(u8arr).substr(0, 21);
}

export function id() {
const u8arr = new Uint8Array(16);
v4({}, u8arr);
return doEncode(u8arr);
}

export function encode(u8arr) {
// Make a copy so we aren't modifying the original
return doEncode(new Uint8Array(u8arr));
}

export function decode(id) {
const u8arr = b64decode(id);
const { buffer, byteOffset, byteLength } = u8arr;
const view = new DataView(buffer, byteOffset, byteLength);
const int3 = view.getUint32(8);
const int4 = view.getUint32(12);

view.setUint32(8, (int3 >>> 2) | 0x80000000);
view.setUint32(12, (int4 >>> 2) | (int3 << 30));

return u8arr;
}
2 changes: 1 addition & 1 deletion src/common/coding/index.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@ export {
splitRef,
} from './path.js';

export { default as makeId } from './id.js';
export { id as makeId, encode as encodeId, decode as decodeId } from './id.js';
export { default as decorate } from './decorate.js';
export * from './serialize.js';
export * from './encodeTree.js';
10 changes: 10 additions & 0 deletions src/common/coding/test/id.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { encode, decode } from '../id.js';
import { v4 } from 'uuid';

test('roundtrip', () => {
const bytes = new Uint8Array(16);
v4({}, bytes);
const str = encode(bytes);
const bytes2 = decode(str);
expect(bytes2).toEqual(bytes);
});