Skip to content

Instantly share code, notes, and snippets.

@nickdandakis
Last active July 25, 2023 19:06
Show Gist options
  • Save nickdandakis/476bad191f3332ca79d25b15c74dc6a1 to your computer and use it in GitHub Desktop.
Save nickdandakis/476bad191f3332ca79d25b15c74dc6a1 to your computer and use it in GitHub Desktop.
zero dependency ksuid implementation
// KSUIDs should be 20 bytes: 4 for the timestamp and 16 for the payload
const TIMESTAMP_BYTES = 4;
const PAYLOAD_BYTES = 16;
const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES;
function getCurrentTimestamp() {
// KSUID timestamps have a custom epoch that starts in 14e8
const CUSTOM_EPOCH_DIFF = 1_400_000_000;
const timestamp = Math.floor(Date.now() / 1000) - CUSTOM_EPOCH_DIFF;
const timestampArray = new Uint8Array(TIMESTAMP_BYTES);
// Write the timestamp to the buffer in Big Endian format
new DataView(timestampArray.buffer).setUint32(0, timestamp);
return timestampArray;
}
function generatePayload() {
const payloadArray = new Uint8Array(PAYLOAD_BYTES);
window.crypto.getRandomValues(payloadArray);
return payloadArray;
}
function toBase62(uint8Array) {
const characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
let quotient = BigInt(`0x${Array.from(uint8Array).map(b => b.toString(16).padStart(2, '0')).join('')}`);
while (quotient > 0) {
const remainder = Number(quotient % 62n);
quotient /= 62n;
result = characters[remainder] + result;
}
return result.padStart(26, '0');
}
function generateKSUID() {
const ksuidArray = new Uint8Array(TOTAL_BYTES);
ksuidArray.set(getCurrentTimestamp(), 0);
ksuidArray.set(generatePayload(), TIMESTAMP_BYTES);
// Convert to a base62 string for easier handling
return toBase62(ksuidArray);
}
// KSUIDs should be 20 bytes: 4 for the timestamp and 16 for the payload
const TIMESTAMP_BYTES = 4;
const PAYLOAD_BYTES = 16;
const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES;
function isBrowserEnvironment() {
return typeof window !== 'undefined';
}
function getCurrentTimestamp() {
// KSUID timestamps have a custom epoch that starts in 14e8
const CUSTOM_EPOCH_DIFF = 1_400_000_000;
const timestamp = Math.floor(Date.now() / 1000) - CUSTOM_EPOCH_DIFF;
if (isBrowserEnvironment()) {
const timestampArray = new Uint8Array(TIMESTAMP_BYTES);
new DataView(timestampArray.buffer).setUint32(0, timestamp);
return timestampArray;
} else {
const timestampBuffer = Buffer.alloc(TIMESTAMP_BYTES);
timestampBuffer.writeUInt32BE(timestamp, 0);
return timestampBuffer;
}
}
function generatePayload() {
if (isBrowserEnvironment()) {
const payloadArray = new Uint8Array(PAYLOAD_BYTES);
window.crypto.getRandomValues(payloadArray);
return payloadArray;
} else {
return require('crypto').randomBytes(PAYLOAD_BYTES);
}
}
function toBase62(uint8Array) {
const characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
let quotient = BigInt(`0x${Array.from(uint8Array).map(b => b.toString(16).padStart(2, '0')).join('')}`);
while (quotient > 0) {
const remainder = Number(quotient % 62n);
quotient /= 62n;
result = characters[remainder] + result;
}
return result.padStart(26, '0');
}
function generateKSUID() {
const ksuidArray = new (isBrowserEnvironment() ? Uint8Array : Buffer)(TOTAL_BYTES);
ksuidArray.set(getCurrentTimestamp(), 0);
ksuidArray.set(generatePayload(), TIMESTAMP_BYTES);
// Convert to a base62 string for easier handling
return toBase62(ksuidArray);
}
const crypto = require('crypto');
// KSUIDs should be 20 bytes: 4 for the timestamp and 16 for the payload
const TIMESTAMP_BYTES = 4;
const PAYLOAD_BYTES = 16;
const TOTAL_BYTES = TIMESTAMP_BYTES + PAYLOAD_BYTES;
function getCurrentTimestamp() {
// KSUID timestamps have a custom epoch that starts in 14e8
const CUSTOM_EPOCH_DIFF = 1_400_000_000;
const timestamp = Math.floor(Date.now() / 1000) - CUSTOM_EPOCH_DIFF;
const timestampBuffer = Buffer.alloc(TIMESTAMP_BYTES);
// Write the timestamp to the buffer in Big Endian format
timestampBuffer.writeUInt32BE(timestamp, 0);
return timestampBuffer;
}
function generatePayload() {
return crypto.randomBytes(PAYLOAD_BYTES);
}
function toBase62(buffer) {
const characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';
let quotient = BigInt(`0x${buffer.toString('hex')}`);
while (quotient > 0) {
const remainder = Number(quotient % 62n);
quotient /= 62n;
result = characters[remainder] + result;
}
return result;
}
function generateKSUID() {
const ksuidBuffer = Buffer.alloc(TOTAL_BYTES);
getCurrentTimestamp().copy(ksuidBuffer, 0, 0, TIMESTAMP_BYTES);
generatePayload().copy(ksuidBuffer, TIMESTAMP_BYTES, 0, PAYLOAD_BYTES);
// Convert to a base62 string for easier handling
return toBase62(ksuidBuffer);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment