Shortening-uuid

June 18, 2017 java uuids

Muchos están familiarizados hoy con los UUIDs, hoy en día son muy utilizados como identificadores debido a su no secuencialidad y su baja probabilidad de colisiones.

Los uuids son una secuencia de 128 bits (16 bytes). La representación usual de estos es con 36 caracteres: 32 caracteres hexadecimales más 4 separadores (por ejemplo: 7625c7e9-38b1-4622-aa71-1ad439c1bced). Los separadores son decorativos, por lo que los caracteres que realmente tienen información son 32 (7625c7e938b14622aa711ad439c1bced). Dado que los caracteres hexadecimales se pueden representar con 4 bits, cada caracter representa 4 bits del UUID, requiriendo 32 caracteres para representar los 128 bits.

Es decir que por cada caracter (1 byte), estamos usando sólo 4 bits para representar el uuid. Por qué no usar los 8 bits? Esto es porque algunos de los caracteres ASCII son de control. Obviamente podríamos inventar algún encoding donde todos los 256 caracteres sean representables gráficamente, pero tendríamos un id con caracteres poco tradicionales.

Se podría usar una representación más compacta? Si, se podría, y además podemos hacer que sea URL friendly (como estos identificadores a menudo deben usarse en URLs sería conveniente usar caracteres válidos para una URL). Si incluímos todos los números (10 caracteres), más todas las letras en minúscula y mayúscula (52 caracteres sin incluír la ñ), tenemos 62 caracteres válidos. Con 6 bits podemos representar 64 valores, con lo cual sumando los caracteres - y _ tenemos un conjunto de 64 caracteres que son válidos en una URL. De hecho este encoding es más conocido como base 64 y su uso para representar UUIDs no la tuve yo. Pueden leer algo en base64 url applications.

Al pasar la representación de hexadecimal (4 bits) a base 64 (6 bits), podemos representar el UUID con 22 caracteres (sobrando 4 bits). Quise hacer algunas pruebas con esta idea y las dejé en short-ids por si a alguien le sirve. Así pude probar las distintas representaciones de un mismo UUID:

7625c7e9-38b1-4622-aa71-1ad439c1bced 7625c7e938b14622aa711ad439c1bced 01110110001001011100011111101001001110001011000101000110001000101010101001110001000110101101010000111001110000011011110011101101 B2JcfpOLFGIqpxGtQ5wbzt

No estoy seguro si hay alguna forma tradicional de obtener la última versión. Pude hacer una prueba usando el java.util.Base64.Encoder y el código de UUID.randomUUID():

        byte[] randomBytes = new byte[16];
        new SecureRandom().nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */

        System.out.println(Base64.getEncoder().withoutPadding().encodeToString(randomBytes));

Obteniendo (por ejemplo) como resultado: qcwbFlNkQDWTZlM2NoG7tg

Luego voy a ver si puedo avanzar e investigar un poco más sobre el tema.


Profile picture

Written by Gastón Fournier Software Engineer at @getunleash working from Cunit, Tarragona, Spain, where I live with my wife and 2 dogs. Find me on Twitter Github LinkedIn