f4b6a3 / uuid-creator

UUID Creator is a Java library for generating Universally Unique Identifiers.
MIT License
410 stars 44 forks source link

UUID Slugs #30

Closed buko closed 3 years ago

buko commented 3 years ago

First off, great lib. The functionality provided and the quality of the code are impressive.

I wonder if you'd be interested in supporting UUID Slugs?

UUID Slugs are a base64 encoding of UUIDs that can be safely included in URLs and even file names. This sort of functionality is very commonly required and I think instead of continuing to replicate it in project after project it should be implemented once in a project like uuid-creator.

A UUID Slug is the string generated by (url safe) Base64 encoding a UUID and then stripping the padding resulting in a 22-character string that can be safely used in filenames and URLs. This functionality isn't directly related to creation but could be stored in a sub-package like f4b6a3.uuid.slug.

See also:

fabiolimace commented 3 years ago

I like the idea. I'm working on it too.

I think it would be great if the uuid-creator library could encode the UUID for many types of encoding in the most efficient way. Therefore, I am implementing a new utility class that supports all RFC-4648 encodings: base16,base32, base32hex,base64andbase64url. It will also support Crockford base32.

The next version will bring a new class like this:

~public final class UuidEncoder {~

~public static String toBase16(UUID uuid);~ ~public static UUID fromBase16(String string);~

~public static String toBase32(UUID uuid);~ ~public static UUID fromBase32(String string);~

~public static String toBase32Hex(UUID uuid);~ ~public static UUID fromBase32Hex(String string);~

~public static String toBase32Crockford(UUID uuid);~ ~public static UUID fromBase32Crockford(String string);~

~public static String toBase64(UUID uuid);~ ~public static UUID fromBase64(String string);~

~public static String toBase64Url(UUID uuid);~ ~public static UUID fromBase64Url(String string);~ ~}~

If you need a slug generator based on UUIDs encoded to base64url, all you'll have to do is this:

~public class SlugGenerator {~ ~public static String getSlug() {~ ~UUID uuid = UuidCreator.getRandomBased();~ ~return UuidEncoder.toBase64Url(uuid);~ ~}~ ~}~ ~

Thanks again!

buko commented 3 years ago

See #32 . Rather than having lots of static methods you could introduce a single generic interface and provide lots of different implementations of the UuidCodec class. This would work well especially with client code that uses dependency injection.

fabiolimace commented 3 years ago

Yesterday I implemented the UUID Slug codec. I also created many base-n codecs that can be used by the slug codec. The default base-n codec is base-64-url.

The codec shifts the version and variant nibbles to the beginning of the byte array. It is similar to the "NICE" slug in the libs you listed. It shifts the 4 bits of the version to the positions 0-3. And then shifts the 4 bits of the variant to the positions 4-7. The result of the shifts is that the first character of the slug is a letter (when using base-32 and base-64 codecs). There's no loss of entropy, since the bits are just rearranged.

Please see the following examples.

This example prints 10 slugs encoded to base-32:

UuidCodec<String> codec = new SlugCodec(new Base32Codec());

for (int i = 0; i < 10; i++) {
    String slug = codec.encode(UUID.randomUUID());
    System.out.println(slug);
}
jco2wkrcocfgkz5sdqmwvaucpu
jkx7luz3bpqgk35fkkknpp7xhy
jm77u64l47ioytip3tvrkq4ebm
jeda5ppl6wdgjznjuwsdyqzmjy
jbba5wmovd2brjib3etimixuae
jmre7sbtmurx6bj7vbjf7socgq
jhfubeb3b3nr22jixyknrs37r4
jktiy6gb264lcbvajiacofduvm
jogs66tzffskcm6q34hk5czexm
jarpdm7bb7xpwvpagjzz67x6dm
^ prefix 'j'

This example prints 10 slugs encoded to base-64-url (default base-n codec):

UuidCodec<String> codec = new SlugCodec(); // the default is base-64

for (int i = 0; i < 10; i++) {
    String slug = codec.encode(UUID.randomUUID());
    System.out.println(slug);
}
SxLz-eYd_rlhxM8Ysjy4qw
S-zud0QOiqM4oBIJYAcgiw
SBeLJs-GnPn1sdobBVzJ7w
SUIeotMfqBvqkJtuZ0W5ZA
SKgYYHVbYrfXEoMgAF-2-w
SQ8yu3Bu1QSfeIgdFcjbXA
S_75xI8xoOSPts0D8lWhCw
SKMWqCHgIIJ3euKJyqljAA
StErqQXhU2LfDpuADEtqKA
S_3tgV2qaBFGjdFBIHrxtA
^ prefix 'S'
fabiolimace commented 3 years ago

Added support to case insensitive base-n codecs.

Changed the base-16 and base-32 codecs to:

  1. Encode to lower case // YELLING IS RUDE!
  2. Decode from lower and upper cases
fabiolimace commented 3 years ago

@buko , I forgot to ask what you think about the codecs. Let me know if you find any problems with them or make a pull request if you wish.

If you approve them, a new version in the next weekend.

buko commented 3 years ago

This stuff looks great, even better than I hoped. The only thing missing is Javadoc; you might just want to add a link to the github issues that explains what the codecs do.

Another question is threadsafety. It looks like all the code works with immutable lookup tables and should be threadsafe but you might want to make that explicit. Java should have a standard @ThreadSafe annotation to make this clear but it doesn't.

Personal preference: most of the codecs, like UuidSlugCodec, UuidNameCodec, and the well-defined base encodings should be final. Not a big deal either way but I wouldn't expect these classes to ever be subclassed.

The name of https://github.com/f4b6a3/uuid-creator/blob/773c8b1e8a3b23e423e7b05e44a3fd1523ef3ee6/src/main/java/com/github/f4b6a3/uuid/codec/UuidStringCodec.java is a bit confusing. Maybe use a clearer name like 'CanonicalStringCodec' and include a link to Wikipedia that explains what the canonical string format of a UUID is.

It's really nice to see all this functionality collected in one place under the MIT License. Great job!

fabiolimace commented 3 years ago

@buko

Version 3.4.0 has been released.

Thank you very much for your help!