ulid 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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