Tiny UUID

Universally unique IDs are great. Used in a database in place of the primary key you don't need to care for conflicts anymore, if more than one application instance is involved. But also inside of an app they are comfortable object identifiers.

In general the version 4 described in RFC 4122 is used, which is composed by time related data and random bytes. For macOS and iOS NSUUID provides a simple to use interface. For Javascript projects node-uuid is widely used.

A drawback is that it is usually is used in form of a string. An incrementing integer instead is very likely managed more efficient and faster by databases and systems.

UUIDs are mostly presented in a hexadecimal notation like f81d4fae-7dec-11d0-a765-00a0c91e6bf6 which is a 36 characters long string. In binary representation it is 16 bytes or 128 bits long. For most environments this does not fit into a regular integer type anymore.

Since each byte counts if you have lot of data it makes sense to put some effort into reducing the size. Stripping the - already saves 4 bytes. But to safe most, without getting into difficulties about string encoding and the like, Base64 looks like a good choice.

But characters like + and / and = become painful when used in URLs or as file names. So let's replace + by - and / by _ as Python does for ages already. Then strip the trailing == and we get a 22 characters long string, which is 1.375 times the size of the most compact representation in binary form and significantly smaller than the original hex representation.

Here is the implementation in Objective-C:

+ (NSString *)uuid {
    uuid_t uuid;
    [NSUUID.UUID getUUIDBytes:uuid];
    return [[[[[NSData dataWithBytes:uuid length:16]
               base64EncodedStringWithOptions:0]
              stringByReplacingOccurrencesOfString:@"=" withString:@""]
             stringByReplacingOccurrencesOfString:@"+" withString:@"-"]
            stringByReplacingOccurrencesOfString:@"/" withString:@"_"];    
}

And this one if for Javascript:

import uuid from 'uuid'

const rxUUIDReplace = /[+=\/]/g
const mapUUIDReplace = {
  '+': '-',
  '/': '_',
  '=': ''
}

export function UUID() {
  const array = []
  uuid.v4(null, array)
  return Buffer.from(array)
    .toString('base64')
    .replace(rxUUIDReplace, m => mapUUIDReplace[m])
}

Happy uniqueness ;)