Skip to content

Instantly share code, notes, and snippets.

@melo
Last active March 12, 2018 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melo/fca85773979822f1107dab15efbad70e to your computer and use it in GitHub Desktop.
Save melo/fca85773979822f1107dab15efbad70e to your computer and use it in GitHub Desktop.
Improvements on ULID generation (perl)
use Benchmark 'cmpthese';
use lib '.';
use ULID ();
use Data::ULID ();
cmpthese(
-5,
{ 'ULID text' => sub { ULID::ulid() },
'Data::ULID text' => sub { Data::ULID::ulid() },
}
);
cmpthese(
-5,
{ 'ULID binary' => sub { ULID::ulid_bin() },
'Data::ULID binary' => sub { Data::ULID::binary_ulid() },
}
);
requires 'Benchmark';
requires 'Data::ULID';
requires 'Crypt::PRNG';
requires 'Time::HiRes';
requires 'Math::BigInt::GMP';
package ULID;
use strict;
use warnings;
use Exporter;
use Crypt::PRNG 'random_bytes';
use Time::HiRes 'time';
use bytes;
our @ISA = qw( Exporter );
our @EXPORT_OK = qw( ulid ulid_bin );
sub ulid {
return _bin2crockford(ulid_bin(@_));
}
sub ulid_bin {
my $t = int(time() * 1000);
return pack('Nn', ($t >> 16), ($t & 0xffff)) . random_bytes(10);
}
our @bin2crockford = qw( 0 1 2 3 4 5 6 7 8 9 A B C D E F G H J K M N P Q R S T V W X Y Z );
sub _bin2crockford {
my @id = unpack('C' x 16, $_[0]);
my $dst = ' ' x 26;
# Encode the timestamp
$dst = $bin2crockford[($id[0] & 224) >> 5];
$dst .= $bin2crockford[$id[0] & 31];
$dst .= $bin2crockford[($id[1] & 248) >> 3];
$dst .= $bin2crockford[(($id[1] & 7) << 2) | (($id[2] & 192) >> 6)];
$dst .= $bin2crockford[($id[2] & 62) >> 1];
$dst .= $bin2crockford[(($id[2] & 1) << 4) | (($id[3] & 240) >> 4)];
$dst .= $bin2crockford[(($id[3] & 15) << 1) | (($id[4] & 128) >> 7)];
$dst .= $bin2crockford[($id[4] & 124) >> 2];
$dst .= $bin2crockford[(($id[4] & 3) << 3) | (($id[5] & 224) >> 5)];
$dst .= $bin2crockford[$id[5] & 31];
# Encode 16 bytes of random
$dst .= $bin2crockford[($id[6] & 248) >> 3];
$dst .= $bin2crockford[(($id[6] & 7) << 2) | (($id[7] & 192) >> 6)];
$dst .= $bin2crockford[($id[7] & 62) >> 1];
$dst .= $bin2crockford[(($id[7] & 1) << 4) | (($id[8] & 240) >> 4)];
$dst .= $bin2crockford[(($id[8] & 15) << 1) | (($id[9] & 128) >> 7)];
$dst .= $bin2crockford[($id[9] & 124) >> 2];
$dst .= $bin2crockford[(($id[9] & 3) << 3) | (($id[10] & 224) >> 5)];
$dst .= $bin2crockford[$id[10] & 31];
$dst .= $bin2crockford[($id[11] & 248) >> 3];
$dst .= $bin2crockford[(($id[11] & 7) << 2) | (($id[12] & 192) >> 6)];
$dst .= $bin2crockford[($id[12] & 62) >> 1];
$dst .= $bin2crockford[(($id[12] & 1) << 4) | (($id[13] & 240) >> 4)];
$dst .= $bin2crockford[(($id[13] & 15) << 1) | (($id[14] & 128) >> 7)];
$dst .= $bin2crockford[($id[14] & 124) >> 2];
$dst .= $bin2crockford[(($id[14] & 3) << 3) | (($id[15] & 224) >> 5)];
$dst .= $bin2crockford[$id[15] & 31];
return $dst;
}
our @crockford2bin = (
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0xFF, 0x12, 0x13, 0xFF, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E,
0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0xFF, 0x12, 0x13,
0xFF, 0x14, 0x15, 0xFF, 0x16, 0x17, 0x18, 0x19, 0x1A, 0xFF, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF,
);
sub _crockford2bin {
my @id = unpack('C' x 26, $_[0]);
my $dst = ' ' x 16;
# 6 bytes timestamp (48 bits)
$dst = chr((($crockford2bin[$id[0]] << 5) | $crockford2bin[$id[1]]));
$dst .= chr((($crockford2bin[$id[2]] << 3) | ($crockford2bin[$id[3]] >> 2)));
$dst .= chr((($crockford2bin[$id[3]] << 6) | ($crockford2bin[$id[4]] << 1) | ($crockford2bin[$id[5]] >> 4)));
$dst .= chr((($crockford2bin[$id[5]] << 4) | ($crockford2bin[$id[6]] >> 1)));
$dst .= chr((($crockford2bin[$id[6]] << 7) | ($crockford2bin[$id[7]] << 2) | ($crockford2bin[$id[8]] >> 3)));
$dst .= chr((($crockford2bin[$id[8]] << 5) | $crockford2bin[$id[9]]));
# 10 bytes of entropy (80 bits)
$dst .= chr((($crockford2bin[$id[10]] << 3) | ($crockford2bin[$id[11]] >> 2)));
$dst .= chr((($crockford2bin[$id[11]] << 6) | ($crockford2bin[$id[12]] << 1) | ($crockford2bin[$id[13]] >> 4)));
$dst .= chr((($crockford2bin[$id[13]] << 4) | ($crockford2bin[$id[14]] >> 1)));
$dst .= chr((($crockford2bin[$id[14]] << 7) | ($crockford2bin[$id[15]] << 2) | ($crockford2bin[$id[16]] >> 3)));
$dst .= chr((($crockford2bin[$id[16]] << 5) | $crockford2bin[$id[17]]));
$dst .= chr((($crockford2bin[$id[18]] << 3) | $crockford2bin[$id[19]] >> 2));
$dst .= chr((($crockford2bin[$id[19]] << 6) | ($crockford2bin[$id[20]] << 1) | ($crockford2bin[$id[21]] >> 4)));
$dst .= chr((($crockford2bin[$id[21]] << 4) | ($crockford2bin[$id[22]] >> 1)));
$dst .= chr((($crockford2bin[$id[22]] << 7) | ($crockford2bin[$id[23]] << 2) | ($crockford2bin[$id[24]] >> 3)));
$dst .= chr((($crockford2bin[$id[24]] << 5) | $crockford2bin[$id[25]]));
return $dst;
}
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment