iliana
@iliana

Would you like all the benefits of v7 UUIDs with none of the hexadecimal? Do you wish ULIDs were lowercase and one character shorter? Boy do I have the made-up-just-now standard for you.

Note: Not to be confused with:
  • UwuId, which produces values like 😳d😳,,afa,,d🤗;sa😳sdk😍ghf,ldjddlsa
  • uwuid, which produces values like uwu owo uwu uwu owo owo owo owo uwu uwu owo owo owo uwu uwu uwu owo uwu owo owo owo owo uwu owo owo uwu owo owo owo uwu uwu owo rawr xd owo owo uwu uwu uwu uwu owo owo uwu owo owo uwu owo uwu uwu uwu rawr xd uwu owo uwu uwu uwu owo owo uwu uwu uwu owo owo uwu uwu uwu uwu rawr xd owo uwu owo owo owo uwu owo owo owo uwu uwu uwu uwu uwu uwu uwu rawr xd owo uwu uwu uwu owo uwu owo owo uwu uwu owo owo owo owo owo uwu owo uwu owo owo owo uwu uwu owo uwu uwu uwu owo owo uwu uwu uwu owo uwu owo uwu owo uwu uwu owo owo owo uwu uwu owo uwu uwu uwu
  • UwUID, which produces values like UwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUwUUw
  • UUID, which produces values like 733f8c2e-120d-44c5-b2f0-60ca50a99f99

For clarity, this specification is spelled as if it were an initialism.

what

UWUID is defined as a conversion function from and to v7 UUIDs. That is, a v7 UUID can be converted to a UWUID and back with no loss in specificity.


To convert a v7 UUID to UWUID, encode the 48-bit unix_ts_ms, 12-bit rand_a, and 62-bit rand_b (see draft-ietf-uuidrev-rfc4122bis-01 § 5.7) into the following 125-bit value:

 0                   1                   2
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|                  unix_ts_ms                   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                    unix_ts_ms                     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|        rand_a         |         rand_b          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      rand_b                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      rand_b                       |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

using this Base32 alphabet1 (abcdefghjkmnpqrstuvwxy0123456789; note iloz are excluded to reduce confusion with 0, 1, and 2):

ValueEncodingValueEncodingValueEncodingValueEncoding
0a9k18v275
1b10m19w286
2c11n20x297
3d12p21y308
4e13q220319
5f14r231
6g15s242
7h16t253
8j17u264

To convert a UWUID to a v7 UUID, reverse these steps.

As an example, the UUID 017f22e2-79b0-7cc3-98c4-dc0c0c07398f would convert to the 25-character string abs6ure8qt3t3uug6btgarrpsa.

why

idk i thought this up on the way home from the grocery store.

Converting UUIDs to UWUIDs before display can have some benefits to end users,[dubious, discuss] such as being shorter to type and easier to copy (none of this double-click-and-drag horseshit). Meanwhile, storing these IDs as UUIDs allows you to take advantage of all of the technology built on top of generating, storing, and retrieving them.

Anyway this was stupid. Bye!

addendum: some changes i would make if i were serious

I wrote an almost-complete implementation of this in Rust, which converts the UUID to a big-endian u128 and then does some masks and bitshifts, and then I poked at the LLVM IR output. It's pretty good, but I would make the following changes to this spec to make it a bit easier to work with:

  • No padding before unix_ts_ms, so you can memcpy that straight over from the UUID. (This loses the advantage of being able to just look at a specific encoded prefix of the output, which you can do with v7 UUIDs and ULIDs (first ten characters)).
  • 2 bits of padding between rand_a and rand_b, equaling 0b10, which matches the variant field of the UUID. This allows you to mask and shift all of the random bits once, instead of differently for each field.
  • Which would leave 1 bit of additional padding, either before rand_a or after rand_b. I'm not sure whether there's an advantage to one or the other. (Maybe 0b1 before rand_a, so that you can include the last bit of the version field in the mask?)

Ultimately I decided against incorporating these changes because it would make the ASCII diagram above more painful to look at.


  1. Listen, if Douglas Crockford gets to make his own Base32 alphabet, I get to make one too.


You must log in to comment.

in reply to @iliana's post:

such as being shorter to type and easier to copy (none of this double-click-and-drag horseshit).

this was enough of a reason for me that I have implemented this and put it in "production" (a very silly API I decided to make so my friends can award each other points).