Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I shorten a UUID to a specific length?

Tags:

uuid

ruby

I'd like to use a UUID for database records but if I'm using it for the URL I'd like it to be 5 to 8 characters.

I know I need to use SecureRandom and base64, but how do I specify the length I need?

like image 892
asaignment Avatar asked Oct 28 '14 18:10

asaignment


People also ask

Can a UUID be shortened?

You can use the short-uuid package to generate v4 UUIDs and shorten them for your application's use cases.

How do I limit the UUID length?

You can use base64 encoding and reduce it to 22 characters. If you use base94 you can get it does to 20 characters. If you use the whole range of valid chars fro \u0000 to \ufffd you can reduce it to just 9 characters or 17 bytes. If you don't care about Strings you can use 16, 8-bit bytes.

Does UUID have fixed length?

UUIDs are fixed length. UUIDs are 128-bits in binary. (32 hex digits x 4 bits per hex digit = 128-bits). UUIDs may also be represented in decimal or binary format.

Is UUID always 36?

It's 36 characters (32 hex digits + 4 dashes).


2 Answers

You can't get a real UUID down to 5-8 characters, as another answer points out, but you can shorten them somewhat. UUIDs are 128-bit integers which works out to 32 hex digits. You can easily store 6 bits per character and cut the length down to 22 characters, which is what base 64 encoding is. Standard base 64 encoding uses upper and lower case letters, digits, and "+" and "/" to finish it out. If you replace "+" and "/" with "-" and "_" you will end up with a string that doesn't have to be url encoded. You can do it like this (using UUIDTools to create the UUID):

uuid = UUIDTools::UUID.random_create
str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)

To get your value back out:

(str + "==\n").tr('-_','+/').unpack('m*').first if str =~ /^[a-zA-Z0-9_\-]{22}$/

That's assuming the UUID can be put into a raw format where it's a string of 16 8-bit characters. Here's an irb session showing a real example:

2.1.1 :016 > uuid=UUIDTools::UUID.random_create
 => #<UUID:0x83f1e98c UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 
2.1.1 :017 > uuid.raw
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :018 > str = [uuid.raw].pack('m*').tr('+/','-_').slice(0..21)
 => "INB7bFKvTlOv6ms60NDGJw" 
2.1.1 :019 > uuid2 =  (str + "==\n").tr('-_','+/').unpack('m*').first
 => " \xD0{lR\xAFNS\xAF\xEAk:\xD0\xD0\xC6'" 
2.1.1 :022 > UUIDTools::UUID.parse_raw(uuid2)
 => #<UUID:0x849e6b44 UUID:20d07b6c-52af-4e53-afea-6b3ad0d0c627> 

I use this method on various web sites where I typically use Postgres to generate UUIDs as primary keys for tables and pass them as ids. It doesn't save a lot of space but it does make some URLs fit on one 80 character line where a full UUID in standard format wouldn't. With dashes, a standard UUID is 36 characters so 22 is about 2/3 the size.

like image 196
Michael Chaney Avatar answered Sep 29 '22 02:09

Michael Chaney


I created two single line function based on Michael Chaney

def encode(uuid)     
  [uuid.tr('-', '').scan(/../).map(&:hex).pack('c*')].pack('m*').tr('+/', '-_').slice(0..21)                                                   
end
def decode(short_id)
 (short_id.tr('-_', '+/') + '==').unpack('m0').first.unpack('H8H4H4H4H12').join('-')                                                                                                                                                                                                                                                           
end

uuid = "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                                                                            
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2"                                                                                                                                                                          
encode(uuid)      
=> "NVv1Af_qT1qp4hYHTeb88g"        
decode(_)         
=> "355bf501-ffea-4f5a-a9e2-16074de6fcf2
like image 23
Luc Boissaye Avatar answered Sep 29 '22 04:09

Luc Boissaye