Как можно преобразовать тип UUID в тип ULID?
Там немного документации, как преобразовать ULID в UUID, но не так много, когда вам нужно преобразовать UUID в ULID.
Я смотрю на этот генератор/конвертер UUID/ULID https://www.ulidtools.com/
но я не совсем уверен, как мне воспроизвести преобразование UUID в ULID, источник слишком запутан, чтобы я мог его понять.
Я не уверен, с чего даже начать, безопасно ли это преобразование? будет ли это гарантировать уникальное преобразование?
4 ответа
У меня была та же проблема, и я взглянул на ulidtools.com, затем, немного покопавшись в его исходном коде, я обнаружил, что он использует этот пакет за видимым.
import pkg from "id128";
const { Ulid, Uuid4 } = pkg;
const ulid = Ulid.generate();
const ulidToUuid = Uuid4.fromRaw(ulid.toRaw());
const uuidToUlid = Ulid.fromRaw(ulidToUuid.toRaw());
console.table([
{
generated_ulid: ulid.toCanonical(),
converted_to_uuid: ulidToUuid.toCanonical(),
converted_back_to_ulid: uuidToUlid.toCanonical(),
},
]);
Чистый метод Javascript для преобразования:
Преобразование UUID в ULID гарантирует уникальный код. ULID — это уникальные 128-битные идентификаторы, которые генерируются на основе комбинации текущей метки времени.
function convertUUIDtoULID(uuid) {
const uuidBinary = uuid.split("-").map((hex) => parseInt(hex, 16));
return uuidBinary
.slice(0, 8)
.map((byte) => byte.toString(32))
.concat(uuidBinary.slice(8).map((byte) => byte.toString(32)))
.join("");
}
const uuid = "454391df-b950-42ea-a2c0-92d62c215d67";
const ulid = convertUUIDtoULID(uuid);
console.log(ulid);
- Функция принимает строку формата UUID в качестве входных данных.
- Он разбивает строку UUID на каждый символ «-» и преобразует каждую результирующую шестнадцатеричную строку в десятичное число с помощью функции parseInt() с основанием 16.
- Он создает новый массив из первых 8 десятичных чисел из UUID и преобразует каждое число в строку с основанием 32 с помощью функции toString() с основанием 32.
- Он объединяет этот массив с новым массивом оставшихся десятичных чисел из UUID, также преобразованных в строки с основанием 32.
- Результирующий массив строк с основанием 32 объединяется в одну строку, которая возвращается как ULID.
- Пример строки UUID передается в функцию, а результирующая строка ULID записывается в консоль.
Вы также можете использовать [https://www.npmjs.com/package/ulid] для преобразования UUID в ULID.
Основываясь на ответе @ImanHosseiniPour, я пришел к следующему:
ВРЕМЕННАЯ МЕТКА + UUID = ULID
import { Ulid, Uuid4 } from "id128";
import { factory, decodeTime } from 'ulid'
const genUlid = factory();
function convertUuidToUlid(
timestamp: Date,
canonicalUuid: string,
): Ulid {
const uuid = Uuid4.fromCanonical(canonicalUuid);
const convertedUlid = Ulid.fromRaw(uuid.toRaw())
const ulidTimestamp = genUlid(timestamp.valueOf()).slice(0, 10)
const ulidRandom = convertedUlid.toCanonical().slice(10);
return Ulid.fromCanonical(ulidTimestamp + ulidRandom)
}
const timestamp = new Date()
const uuid = 'f0df59ea-bfe2-43a8-98d4-8213348daeb6'
const ulid = convertUuidToUlid(timestamp, uuid)
const originalUuid = Uuid4.fromRaw(ulid.toRaw());
console.table({
timestamp: timestamp.valueOf(),
uuid,
ulid: ulid.toCanonical(),
decodedTime: decodeTime(ulid.toCanonical()),
originalUuid: originalUuid.toCanonical(),
});
Имейте в виду, что обратный инжиниринг ULID в UUID приведет к тому, что два первых фрагмента будут отличаться от оригинала, поскольку мы включили временную метку.
Другие ответы на этот вопрос не удовлетворили мои потребности (нет внешних зависимостей). Вот версия, которая работает с ванильным ECMAScript2018 JS:
/**
* Decodes a hexadecimal string (case-insensitive) into an equivalent Uint8Array.
*
* @param {string} hexString The string to decode
* @returns {Uint8Array} The string decoded into binary
*/
function decodeHex(hexString) {
if (typeof hexString !== 'string' || hexString.length % 2 !== 0) {
throw new Error('Invalid hex string');
}
const decoded = new Uint8Array(hexString.length / 2);
for (let i = 0; i < hexString.length; i += 2) {
const byte = parseInt(hexString.substring(i, i + 2), 16);
decoded[i / 2] = byte;
}
return decoded;
}
/**
* The ULID encoding lookup. Notably excludes I, L, U, and O.
*/
const ULID_ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
/**
* Converts a UUID to an equivalent ULID.
*
* @param {string} uuid The UUID, encoded as a 36-character hex-with-dashes string.
* @returns {string} The equivalent ULID, encoded as a 26-character base32 string.
*/
function uuidToUlid(uuid) {
if (!/^[0-9A-Fa-f]{8}(-[0-9A-Fa-f]{4}){3}-[0-9A-Fa-f]{12}$/i.test(uuid)) {
throw new Error('Invalid UUID.');
}
// Break into sections, excluding dashes.
// Using 0179e73f-ff38-a5e0-e633-48fae1c0bd35 as example...
const section1 = uuid.substring(0, 8); // is 0179e73f
const section2 = uuid.substring(9, 13); // is ff38
const section3 = uuid.substring(14, 18); // is a5e0
const section4 = uuid.substring(19, 23); // is e633
const section5 = uuid.substring(24); // is 48fae1c0bd35
// concatenate the parts, decoded into Uint8Array types. This will have length 16.
const decodedArray = [
...decodeHex(section1),
...decodeHex(section2),
...decodeHex(section3),
...decodeHex(section4),
...decodeHex(section5),
];
// optimized unrolled loop for converting 16 bytes into 26 characters, using
// the ULID lookup to translate from integers [0-25] to valid ULID characters.
// ref. https://github.com/RobThree/NUlid
const ulid = [
ULID_ENCODING[(decodedArray[0] & 224) >> 5],
ULID_ENCODING[decodedArray[0] & 31],
ULID_ENCODING[(decodedArray[1] & 248) >> 3],
ULID_ENCODING[((decodedArray[1] & 7) << 2) | ((decodedArray[2] & 192) >> 6)],
ULID_ENCODING[(decodedArray[2] & 62) >> 1],
ULID_ENCODING[((decodedArray[2] & 1) << 4) | ((decodedArray[3] & 240) >> 4)],
ULID_ENCODING[((decodedArray[3] & 15) << 1) | ((decodedArray[4] & 128) >> 7)],
ULID_ENCODING[(decodedArray[4] & 124) >> 2],
ULID_ENCODING[((decodedArray[4] & 3) << 3) | ((decodedArray[5] & 224) >> 5)],
ULID_ENCODING[decodedArray[5] & 31],
ULID_ENCODING[(decodedArray[6] & 248) >> 3],
ULID_ENCODING[((decodedArray[6] & 7) << 2) | ((decodedArray[7] & 192) >> 6)],
ULID_ENCODING[(decodedArray[7] & 62) >> 1],
ULID_ENCODING[((decodedArray[7] & 1) << 4) | ((decodedArray[8] & 240) >> 4)],
ULID_ENCODING[((decodedArray[8] & 15) << 1) | ((decodedArray[9] & 128) >> 7)],
ULID_ENCODING[(decodedArray[9] & 124) >> 2],
ULID_ENCODING[((decodedArray[9] & 3) << 3) | ((decodedArray[10] & 224) >> 5)],
ULID_ENCODING[decodedArray[10] & 31],
ULID_ENCODING[(decodedArray[11] & 248) >> 3],
ULID_ENCODING[((decodedArray[11] & 7) << 2) | ((decodedArray[12] & 192) >> 6)],
ULID_ENCODING[(decodedArray[12] & 62) >> 1],
ULID_ENCODING[((decodedArray[12] & 1) << 4) | ((decodedArray[13] & 240) >> 4)],
ULID_ENCODING[((decodedArray[13] & 15) << 1) | ((decodedArray[14] & 128) >> 7)],
ULID_ENCODING[(decodedArray[14] & 124) >> 2],
ULID_ENCODING[((decodedArray[14] & 3) << 3) | ((decodedArray[15] & 224) >> 5)],
ULID_ENCODING[decodedArray[15] & 31]
].join('');
return ulid;
}
const uuid = '0179e73f-ff38-a5e0-e633-48fae1c0bd35';
const ulid = uuidToUlid(uuid);
console.log(ulid); // 01F7KKZZSRMQGECCT8ZBGW1F9N
Вот JSFiddle, если вы хотите проверить функциональность.