Last active
March 12, 2018 10:10
-
-
Save melo/fca85773979822f1107dab15efbad70e to your computer and use it in GitHub Desktop.
Improvements on ULID generation (perl)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() }, | |
} | |
); | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
requires 'Benchmark'; | |
requires 'Data::ULID'; | |
requires 'Crypt::PRNG'; | |
requires 'Time::HiRes'; | |
requires 'Math::BigInt::GMP'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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