ulid 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a2e09c0725c50d9cdce66e66ca46f2c7fbdcf5b
4
- data.tar.gz: 203d9b4b4c466561af9733512d1b3c118e77b851
3
+ metadata.gz: ac96b57e8fb7ee4afc17adb9070d46828667155a
4
+ data.tar.gz: f06ece64720bd8d8b5323ee69583e8ae3340562d
5
5
  SHA512:
6
- metadata.gz: e94a295f1b71498c1c21381f99e9b3ec888ba2a51acee99618985eecc35f6e94eff14baae6a459c54088bea252833ba0a11ff66f9b663fb9ac4a614b1260a52f
7
- data.tar.gz: fc48c4ab0975c5d4a72d36c013bef2ebf3910574d42f2cea42a0e781ca7cfb6b3f60bff217e79b110bfcf38edc7f1dccd5c8e3520c460c6fb73432a8e3851658
6
+ metadata.gz: b28852c6c6e9ac75f331f6330338b9b49c36bc12f346e7d3cddc84f8704cfe7ced0c129c893222f9437855f97b829d1bb29573a94418c387f20afb1c8641dfde
7
+ data.tar.gz: d5d3ae42a1b1ed786ba3b87e1c9844fe6f89b41521a5e96782cda05a4beb8436c955ebb306517a1cf121f9eeb47a66822cbe91a8898653bdb5d08adfedd24335
data/Gemfile CHANGED
@@ -10,4 +10,6 @@ end
10
10
  group :test do
11
11
  gem "minitest"
12
12
  gem "mocha"
13
+ gem "timecop"
14
+ gem "base32"
13
15
  end
data/Gemfile.lock CHANGED
@@ -1,12 +1,13 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ulid (0.0.2)
4
+ ulid (0.1.0)
5
5
  sysrandom (>= 1.0.0, < 2.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ base32 (0.3.2)
10
11
  byebug (9.0.5)
11
12
  coderay (1.1.1)
12
13
  metaclass (0.0.4)
@@ -23,16 +24,19 @@ GEM
23
24
  pry (~> 0.10)
24
25
  rake (10.5.0)
25
26
  slop (3.6.0)
26
- sysrandom (1.0.2)
27
+ sysrandom (1.0.3)
28
+ timecop (0.8.1)
27
29
 
28
30
  PLATFORMS
29
31
  ruby
30
32
 
31
33
  DEPENDENCIES
34
+ base32
32
35
  minitest
33
36
  mocha
34
37
  pry-byebug
35
38
  rake (~> 10.0)
39
+ timecop
36
40
  ulid!
37
41
 
38
42
  BUNDLED WITH
data/Rakefile CHANGED
@@ -1,7 +1,17 @@
1
1
  require "bundler/gem_tasks"
2
2
  require 'rake/testtask'
3
+ require 'benchmark'
4
+ require 'ulid'
3
5
 
4
6
  Rake::TestTask.new do |t|
5
7
  t.libs += ["spec", "lib"]
6
8
  t.test_files = FileList['spec/**/*_spec.rb']
7
9
  end
10
+
11
+ desc "Benchmark base32 ULID generation (default 100,000 iterations)"
12
+ task :benchmark, [:iterations] do |t, args|
13
+ iterations = (args[:iterations] || 100_000).to_i
14
+ Benchmark.bm do |b|
15
+ b.report("#{iterations} iterations") { iterations.times { ULID.generate } }
16
+ end
17
+ end
data/lib/ext/time.rb ADDED
@@ -0,0 +1,5 @@
1
+ class Time
2
+ def self.now_100usec
3
+ (now.to_f * 10_000).to_i
4
+ end
5
+ end
data/lib/ulid.rb CHANGED
@@ -1,7 +1,5 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), "/**/*.rb")).sort.each { |f| require f }
2
+
1
3
  module ULID
2
- def self.generate
3
- Generator.new.generate
4
- end
4
+ extend Generator
5
5
  end
6
-
7
- Dir.glob(File.join(File.dirname(__FILE__), "/ulid/**/*.rb")).sort.each { |f| require f }
@@ -1,36 +1,41 @@
1
1
  require 'sysrandom'
2
2
 
3
3
  module ULID
4
- class Generator
4
+ module Generator
5
5
  ENCODING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' # Crockford's Base32
6
- TIME_LENGTH = 10
7
- RANDOM_LENGTH = 16
6
+ RANDOM_BYTES = 10
7
+ ENCODED_LENGTH = 26
8
+ BIT_LENGTH = 128
9
+ BITS_PER_B32_CHAR = 5
10
+
11
+ MASK = 0x1f
8
12
 
9
13
  def generate
10
- encode_time(milliseconds_since_epoch, TIME_LENGTH) + encode_random(RANDOM_LENGTH)
14
+ input = octo_word
15
+ (1..ENCODED_LENGTH).to_a.reduce("") do |s, n|
16
+ shift = BIT_LENGTH - BITS_PER_B32_CHAR * n
17
+ s + ENCODING[(input >> shift) & MASK]
18
+ end
11
19
  end
12
20
 
13
- private
21
+ def generate_bytes
22
+ time_48bit + random_bytes
23
+ end
14
24
 
15
- def encode_time(now, length)
16
- length.times.reduce('') do |output|
17
- mod = now % ENCODING.length
18
- now = (now - mod) / ENCODING.length
25
+ private
19
26
 
20
- output << ENCODING[mod]
21
- end.reverse
27
+ def octo_word
28
+ (hi, lo) = generate_bytes.unpack("Q>Q>")
29
+ (hi << 64) | lo
22
30
  end
23
31
 
24
- def encode_random(length)
25
- length.times.reduce("") do |output|
26
- random = Sysrandom.random_number(ENCODING.length)
27
-
28
- output << ENCODING[random]
29
- end.reverse
32
+ def time_48bit
33
+ hundred_micro_time = Time.now_100usec
34
+ [hundred_micro_time].pack("Q>")[2..-1]
30
35
  end
31
36
 
32
- def milliseconds_since_epoch
33
- (Time.now.to_f * 1000).to_i
37
+ def random_bytes
38
+ Sysrandom.random_bytes(RANDOM_BYTES)
34
39
  end
35
40
  end
36
41
  end
data/lib/ulid/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ULID
2
- VERSION = '0.0.3'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -1,17 +1,51 @@
1
1
  require 'spec_helper'
2
+ require 'timecop'
3
+ require 'base32'
2
4
 
3
5
  describe ULID do
4
- it "ensures it has 26 chars" do
5
- ulid = ULID.generate
6
+ describe "textual representation" do
7
+ it "ensures it has 26 chars" do
8
+ ulid = ULID.generate
6
9
 
7
- ulid.length.must_equal 26
10
+ ulid.length.must_equal 26
11
+ end
12
+
13
+ it "is sortable" do
14
+ ulid_1, ulid_2 = nil
15
+ Timecop.freeze do
16
+ ulid_1 = ULID.generate
17
+ Timecop.travel Time.now + 1
18
+ ulid_2 = ULID.generate
19
+ end
20
+ assert ulid_2 > ulid_1
21
+ end
22
+
23
+ it "is valid Crockford Base32" do
24
+ Base32.table = ULID::Generator::ENCODING
25
+ ulid = ULID.generate
26
+ decoded = Base32.decode(ulid)
27
+ encoded = Base32.encode(decoded)[0...26]
28
+ assert encoded == ulid
29
+ end
8
30
  end
9
31
 
10
- it "is sortable" do
11
- ulid_1 = ULID.generate
12
- ulid_2 = ULID.generate
32
+ describe "underlying binary" do
33
+
34
+ it "encodes the timestamp in the high 48 bits" do
35
+ Timecop.freeze do
36
+ now_100usec = Time.now_100usec
37
+ bytes = ULID.generate_bytes
38
+ ts = ("\x0\x0" + bytes[0...6]).unpack("Q>").first
39
+ assert ts == now_100usec
40
+ end
41
+ end
13
42
 
14
- assert ulid_2 > ulid_1
43
+ it "encodes the remaining 80 bits as random" do
44
+ random_bytes = Sysrandom.random_bytes(ULID::Generator::RANDOM_BYTES)
45
+ ULID.stubs(:random_bytes).returns(random_bytes)
46
+ bytes = ULID.generate_bytes
47
+ assert bytes[6..-1] == random_bytes
48
+ end
15
49
  end
16
- end
17
50
 
51
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ulid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Sales
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-05 00:00:00.000000000 Z
11
+ date: 2016-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sysrandom
@@ -43,6 +43,7 @@ files:
43
43
  - LICENSE
44
44
  - README.md
45
45
  - Rakefile
46
+ - lib/ext/time.rb
46
47
  - lib/ulid.rb
47
48
  - lib/ulid/generator.rb
48
49
  - lib/ulid/version.rb