Skip to content

Instantly share code, notes, and snippets.

@intech
Created February 3, 2024 16:04
Show Gist options
  • Save intech/4071f83573d00a2ac47ac04c7e32ddc5 to your computer and use it in GitHub Desktop.
Save intech/4071f83573d00a2ac47ac04c7e32ddc5 to your computer and use it in GitHub Desktop.
UUIDv5 serialization and deserialization
/* Code compression - compresses given [A-Za-z0-9]+ string to hex, and/or to
* UUIDv5, and decopresses it back.
* Used by Rehau piece code compression, to generate unique product UUID based
* on code value.
* Can't use ordinary hex, as "16-bytes string".toString("hex") would result in
* 32 symbol hex string, which is too long to be encoded into UUID.
*/
const char0 = '0'.charCodeAt(0);
const char9 = '9'.charCodeAt(0);
const num_range = char9 - char0 + 1;
const chara = 'a'.charCodeAt(0);
const charz = 'z'.charCodeAt(0);
const let_range = charz - chara + 1;
const charA = 'A'.charCodeAt(0);
const charZ = 'Z'.charCodeAt(0);
const up_let_range = charZ - charA + 1;
const sym_to_int = (sym) => {
let c = 0;
const s = sym.charCodeAt(0);
if(s >= char0 && s <= char9) {
c = s - char0;
} else if(s >= charA && s <= charZ) {
c = s - charA + num_range;
} else if(s >= chara && s <= charz) {
c = s - chara + num_range + up_let_range;
} else {
c = -1;
}
return c;
};
const decompress_sym = (c) => {
let s = -1;
if(c < num_range) {
s = char0 + c;
} else if(c < (num_range + up_let_range)) {
s = charA + c - num_range;
} else if(c < (num_range + up_let_range + let_range)) {
s = chara + c - num_range - up_let_range;
}
return String.fromCharCode(s);
};
const compress_ints = (ints, bits_per_int) => {
const buffer = Buffer.alloc(bits_per_int * ints.length / 8);
for(let i = 0; i < ints.length; ++i) {
const n = ints[i];
for(let b = 0; b < bits_per_int; ++b) {
const bv = n & (1 << b);
if(bv) {
const byteidx = Math.floor((i * bits_per_int + b) / 8);
const bit = (i * bits_per_int + b) % 8;
buffer[byteidx] |= (1 << bit);
}
}
}
return buffer;
}
const decompress_buffer = (buffer, bits_per_int) => {
const ints = [];
const num_ints = Math.floor(buffer.length * 8 / bits_per_int);
for(let i = 0; i < num_ints; ++i) {
let num = 0;
for(let b = 0; b < bits_per_int; ++b) {
const byteidx = Math.floor((i * bits_per_int + b) / 8);
const bit = (i * bits_per_int + b) % 8;
const bv = buffer[byteidx] & (1 << bit);
if(bv) {
num |= 1 << b;
}
}
ints.push(num);
}
return ints;
};
const to_uuid_str = (version, val) => {
val = val + "000000";
return val.substr(0,8) + '-' +
val.substr(8,4) + '-' +
(version + val.substr(12,3)) + '-' +
'8' + val.substr(15,3) + '-' +
val.substr(18,12);
}
const from_uuid_str = (u) => {
return u.substr(0, 8) +
u.substr(9, 4) +
u.substr(15, 3) +
u.substr(20, 3) +
u.substr(24, 6);
};
export const compressToHex = (code) => {
const syms = code.split('').map((c) => sym_to_int(c));
const buffer = compress_ints(syms, 6);
return buffer.toString("hex");
};
export const compressToUUID = (code) => {
return to_uuid_str(5, compressToHex(code));
};
export const decompressFromHex = (hex) => {
const compressed_buffer = Buffer.from(hex, "hex");
const decompressed_ints = decompress_buffer(compressed_buffer, 6);
const decompressed = decompressed_ints.map((d) => decompress_sym(d));
return decompressed.join('');
};
export const decompressFromUUID = (uuid) => {
if(uuid.substr(30) !== "000000") return undefined;
return decompressFromHex(from_uuid_str(uuid));
};
export const hexToUUID = (hex) => {
return to_uuid_str(5, hex);
};
export const hexFromUUID = (uuid) => {
return from_uuid_str(uuid);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment