base62-rb 0.3.0 → 0.3.1
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 +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/benchmarks/comparison.rb +1 -3
- data/benchmarks/decode_bench.rb +161 -0
- data/benchmarks/encode_bench.rb +129 -0
- data/lib/base62-rb.rb +29 -29
- data/lib/base62/version.rb +1 -1
- data/test/base62_test.rb +22 -22
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 299f76e841055f3d30cc34ff3795eac2e212e283
|
4
|
+
data.tar.gz: 80b78e4e05f4c5740050e753cca18e7def48fe7b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 214f81b6a1919b075db0924bbd1f80dedab436f14a4b699421f838d2eeae24f0e2b17626b3e10d2706ae9cad3770dc8063e5a487f1fcc1f734aae787cb81ef8d
|
7
|
+
data.tar.gz: d1b0000f0512a29e98b3f5f0556079b3f81e8ccfe39cb30013e4d4532dd083dee4ba85ebc0b5ae7b8eb8b68427a993df30c12122843ed4d780e791b95e2eb229
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Base62 in Ruby
|
2
2
|
|
3
|
-
A simple and fast implementation of base62 in pure Ruby without too much sugar and magic.
|
3
|
+
A simple and fast implementation of base62 in pure Ruby without too much sugar and magic.
|
4
4
|
|
5
5
|
It uses character set: `0-9`, `a-z`, `A-Z` for encoding and decoding.
|
6
6
|
|
@@ -41,4 +41,4 @@ See the comparison [here](benchmarks/comparison.rb)
|
|
41
41
|
|
42
42
|
## License
|
43
43
|
|
44
|
-
MIT License - Copyright (c)
|
44
|
+
MIT License - Copyright (c) 2016 Steven Yue
|
data/benchmarks/comparison.rb
CHANGED
@@ -15,7 +15,6 @@ time = Benchmark.measure do
|
|
15
15
|
end
|
16
16
|
puts time # => 3.280000 0.010000 3.290000 ( 3.311349))
|
17
17
|
|
18
|
-
|
19
18
|
# gem radix62 v1.0.1
|
20
19
|
# https://github.com/matiaskorhonen/radix62
|
21
20
|
require 'radix62'
|
@@ -28,7 +27,6 @@ time = Benchmark.measure do
|
|
28
27
|
end
|
29
28
|
puts time # => 6.790000 0.020000 6.810000 ( 6.842925)
|
30
29
|
|
31
|
-
|
32
30
|
# gem base62 v1.0.0
|
33
31
|
# https://github.com/jtzemp/base62
|
34
32
|
require 'base62'
|
@@ -39,4 +37,4 @@ time = Benchmark.measure do
|
|
39
37
|
raise "Assertion error!" unless i == decode
|
40
38
|
end
|
41
39
|
end
|
42
|
-
puts time # => 10.370000 0.030000 10.400000 ( 10.452976)
|
40
|
+
puts time # => 10.370000 0.030000 10.400000 ( 10.452976)
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
KEYS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
4
|
+
KEYS_HASH = KEYS.each_char.with_index.inject({}){|h,(k,v)| h[k]=v;h}
|
5
|
+
BASE = KEYS.length
|
6
|
+
|
7
|
+
### Decode Method Candidates =====
|
8
|
+
|
9
|
+
# The one that is used in base62-rb library
|
10
|
+
# Use 'while' loop, and define a variable i as the counter
|
11
|
+
def decode(str)
|
12
|
+
num = 0
|
13
|
+
i = 0
|
14
|
+
len = str.length - 1
|
15
|
+
while i < str.length
|
16
|
+
pow = BASE ** (len - i)
|
17
|
+
num += KEYS_HASH[str[i]] * pow
|
18
|
+
i += 1
|
19
|
+
end
|
20
|
+
return num
|
21
|
+
end
|
22
|
+
|
23
|
+
# Use KEYS.index to get the index
|
24
|
+
# instead of using a predefined Hash Map(KEYS_HASH)
|
25
|
+
def decode1(str)
|
26
|
+
num = 0
|
27
|
+
i = 0
|
28
|
+
while i < str.length
|
29
|
+
pow = BASE ** (str.length - i -1)
|
30
|
+
num += KEYS.index(str[i]) * pow
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
return num
|
34
|
+
end
|
35
|
+
|
36
|
+
# Use 'for...in...' loop
|
37
|
+
def decode2(str)
|
38
|
+
num = 0
|
39
|
+
for i in 0...str.length
|
40
|
+
pow = BASE ** (str.length - i -1)
|
41
|
+
num += KEYS_HASH[str[i]] * pow
|
42
|
+
end
|
43
|
+
return num
|
44
|
+
end
|
45
|
+
|
46
|
+
# Use String#each_char, and #with_index
|
47
|
+
# Use block to do the calcuation
|
48
|
+
def decode3(str)
|
49
|
+
num = 0
|
50
|
+
str.each_char.with_index do |char, i|
|
51
|
+
pow = BASE ** (str.length - i -1)
|
52
|
+
num += KEYS.index(char) * pow
|
53
|
+
end
|
54
|
+
return num
|
55
|
+
end
|
56
|
+
|
57
|
+
# Similar as above, but use KEYS_HASH to get the index
|
58
|
+
def decode4(str)
|
59
|
+
num = 0
|
60
|
+
str.each_char.with_index do |char, i|
|
61
|
+
pow = BASE ** (str.length - i -1)
|
62
|
+
num += KEYS_HASH[char] * pow
|
63
|
+
end
|
64
|
+
return num
|
65
|
+
end
|
66
|
+
|
67
|
+
# Similar as above, but explicitly define the counter
|
68
|
+
# without using #with_index
|
69
|
+
def decode5(str)
|
70
|
+
num = 0
|
71
|
+
i = 0
|
72
|
+
str.each_char do |char|
|
73
|
+
pow = BASE ** (str.length - i -1)
|
74
|
+
num += KEYS_HASH[char] * pow
|
75
|
+
i += 1
|
76
|
+
end
|
77
|
+
return num
|
78
|
+
end
|
79
|
+
|
80
|
+
# same thing but with less variable
|
81
|
+
def decode6(str)
|
82
|
+
num = 0
|
83
|
+
i = 0
|
84
|
+
while i < str.length
|
85
|
+
num += KEYS_HASH[str[i]] * (BASE ** (str.length - 1 - i))
|
86
|
+
i += 1
|
87
|
+
end
|
88
|
+
return num
|
89
|
+
end
|
90
|
+
|
91
|
+
### ==================
|
92
|
+
|
93
|
+
### Testing =====
|
94
|
+
|
95
|
+
tests = ["A", "Jr", "DFL", "2B5S", "8zTZmv", "1AnE6bpNA", "hjNv8tS3K"]
|
96
|
+
|
97
|
+
Benchmark.ips do |x|
|
98
|
+
x.report("decode") do
|
99
|
+
tests.each do |test|
|
100
|
+
decode(test)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
x.report("decode1") do
|
105
|
+
tests.each do |test|
|
106
|
+
decode1(test)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
x.report("decode2") do
|
111
|
+
tests.each do |test|
|
112
|
+
decode2(test)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
x.report("decode3") do
|
117
|
+
tests.each do |test|
|
118
|
+
decode3(test)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
x.report("decode4") do
|
123
|
+
tests.each do |test|
|
124
|
+
decode4(test)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
x.report("decode5") do
|
129
|
+
tests.each do |test|
|
130
|
+
decode5(test)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
x.report("decode6") do
|
135
|
+
tests.each do |test|
|
136
|
+
decode6(test)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
### Results
|
142
|
+
|
143
|
+
# Results tested on Macbook Air 1.4 GHz Intel Core i5, 8 GB 1600 MHz DDR3
|
144
|
+
# Using Ruby 2.1.3p242
|
145
|
+
|
146
|
+
# Calculating -------------------------------------
|
147
|
+
# decode 6677 i/100ms
|
148
|
+
# decode1 5962 i/100ms
|
149
|
+
# decode2 5535 i/100ms
|
150
|
+
# decode3 4378 i/100ms
|
151
|
+
# decode4 4502 i/100ms
|
152
|
+
# decode5 5915 i/100ms
|
153
|
+
# decode6 6443 i/100ms
|
154
|
+
# -------------------------------------------------
|
155
|
+
# decode 67891.4 (±9.7%) i/s - 340527 in 5.069726s
|
156
|
+
# decode1 62385.4 (±7.8%) i/s - 310024 in 5.000584s
|
157
|
+
# decode2 57033.8 (±8.5%) i/s - 287820 in 5.082882s
|
158
|
+
# decode3 45356.5 (±6.5%) i/s - 227656 in 5.041103s
|
159
|
+
# decode4 47708.3 (±6.5%) i/s - 238606 in 5.023164s
|
160
|
+
# decode5 64856.4 (±6.6%) i/s - 325325 in 5.038748s
|
161
|
+
# decode6 67880.8 (±7.4%) i/s - 341479 in 5.059089s
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
KEYS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
4
|
+
KEYS_HASH = KEYS.each_char.with_index.inject({}){|h,(k,v)| h[k]=v;h}
|
5
|
+
BASE = KEYS.length
|
6
|
+
|
7
|
+
### Encode Method Candidates =====
|
8
|
+
|
9
|
+
# The one that is used in base62-rb library
|
10
|
+
# Use '+' to prepend string
|
11
|
+
def encode(num)
|
12
|
+
return "0" if num == 0
|
13
|
+
return nil if num < 0
|
14
|
+
|
15
|
+
str = ""
|
16
|
+
while num > 0
|
17
|
+
str = KEYS[num % BASE] + str
|
18
|
+
num = num / BASE
|
19
|
+
end
|
20
|
+
return str
|
21
|
+
end
|
22
|
+
|
23
|
+
# Use String#reverse method at the end
|
24
|
+
def encode1(num)
|
25
|
+
return "0" if num == 0
|
26
|
+
return nil if num < 0
|
27
|
+
|
28
|
+
str = ""
|
29
|
+
while num > 0
|
30
|
+
str = str + KEYS[num % BASE]
|
31
|
+
num = num / (BASE)
|
32
|
+
end
|
33
|
+
return str.reverse
|
34
|
+
end
|
35
|
+
|
36
|
+
# Use String#prepend method
|
37
|
+
def encode2(num)
|
38
|
+
return "0" if num == 0
|
39
|
+
return nil if num < 0
|
40
|
+
|
41
|
+
str = ""
|
42
|
+
while num > 0
|
43
|
+
str.prepend KEYS[num % BASE]
|
44
|
+
num = num / (BASE)
|
45
|
+
end
|
46
|
+
return str
|
47
|
+
end
|
48
|
+
|
49
|
+
# Use the '/=' way
|
50
|
+
def encode3(num)
|
51
|
+
return "0" if num == 0
|
52
|
+
return nil if num < 0
|
53
|
+
|
54
|
+
str = ""
|
55
|
+
while num > 0
|
56
|
+
str = KEYS[num % BASE] + str
|
57
|
+
num /= BASE
|
58
|
+
end
|
59
|
+
return str
|
60
|
+
end
|
61
|
+
|
62
|
+
# Use #<< to append string
|
63
|
+
def encode4(num)
|
64
|
+
return "0" if num == 0
|
65
|
+
return nil if num < 0
|
66
|
+
|
67
|
+
str = ""
|
68
|
+
while num > 0
|
69
|
+
str << KEYS[num % BASE]
|
70
|
+
num /= BASE
|
71
|
+
end
|
72
|
+
return str.reverse
|
73
|
+
end
|
74
|
+
|
75
|
+
### ==================
|
76
|
+
|
77
|
+
### Testing =====
|
78
|
+
|
79
|
+
tests_encode = [630, 1231, 902323, 3781504209452600, 18446744073709551615]
|
80
|
+
|
81
|
+
Benchmark.ips do |x|
|
82
|
+
x.report("encode") do
|
83
|
+
tests_encode.each do |test|
|
84
|
+
encode(test)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
x.report("encode1") do
|
89
|
+
tests_encode.each do |test|
|
90
|
+
encode1(test)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
x.report("encode2") do
|
95
|
+
tests_encode.each do |test|
|
96
|
+
encode2(test)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
x.report("encode3") do
|
101
|
+
tests_encode.each do |test|
|
102
|
+
encode3(test)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
x.report("encode4") do
|
107
|
+
tests_encode.each do |test|
|
108
|
+
encode4(test)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
### Results
|
114
|
+
|
115
|
+
# Results tested on Macbook Air 1.4 GHz Intel Core i5, 8 GB 1600 MHz DDR3
|
116
|
+
# Using Ruby 2.1.3p242
|
117
|
+
|
118
|
+
# Calculating -------------------------------------
|
119
|
+
# encode 9141 i/100ms
|
120
|
+
# encode1 8231 i/100ms
|
121
|
+
# encode2 7222 i/100ms
|
122
|
+
# encode3 9040 i/100ms
|
123
|
+
# encode4 8660 i/100ms
|
124
|
+
# -------------------------------------------------
|
125
|
+
# encode 97414.2 (±8.1%) i/s - 484473 in 5.008219s
|
126
|
+
# encode1 89976.1 (±6.5%) i/s - 452705 in 5.053138s
|
127
|
+
# encode2 76680.4 (±6.8%) i/s - 382766 in 5.015263s
|
128
|
+
# encode3 94348.1 (±8.2%) i/s - 470080 in 5.016213s
|
129
|
+
# encode4 86996.4 (±8.7%) i/s - 433000 in 5.016085s
|
data/lib/base62-rb.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
require "base62/version"
|
2
2
|
|
3
3
|
module Base62
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
KEYS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".freeze
|
5
|
+
KEYS_HASH = KEYS.each_char.with_index.inject({}) { |h, (k, v)| h[k] = v; h }
|
6
|
+
BASE = KEYS.length
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# Encodes base10 (decimal) number to base62 string.
|
9
|
+
def self.encode(num)
|
10
|
+
return "0" if num == 0
|
11
|
+
return nil if num < 0
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
str = ""
|
14
|
+
while num > 0
|
15
|
+
# prepend base62 charaters
|
16
|
+
str = KEYS[num % BASE] + str
|
17
|
+
num = num / BASE
|
18
|
+
end
|
19
|
+
str
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
22
|
+
# Decodes base62 string to a base10 (decimal) number.
|
23
|
+
def self.decode(str)
|
24
|
+
num = 0
|
25
|
+
i = 0
|
26
|
+
len = str.length - 1
|
27
|
+
# while loop is faster than each_char or other 'idiomatic' way
|
28
|
+
while i < str.length
|
29
|
+
pow = BASE**(len - i)
|
30
|
+
num += KEYS_HASH[str[i]] * pow
|
31
|
+
i += 1
|
32
|
+
end
|
33
|
+
num
|
34
|
+
end
|
35
|
+
end
|
data/lib/base62/version.rb
CHANGED
data/test/base62_test.rb
CHANGED
@@ -2,27 +2,27 @@ require "base62-rb"
|
|
2
2
|
require "test/unit"
|
3
3
|
|
4
4
|
class Base62Test < Test::Unit::TestCase
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
5
|
+
def test_encode
|
6
|
+
{
|
7
|
+
0 => "0",
|
8
|
+
3781504209452600 => "hjNv8tS3K",
|
9
|
+
18446744073709551615 => "lYGhA16ahyf"
|
10
|
+
}.each do |int, base62|
|
11
|
+
assert_equal base62, Base62.encode(int)
|
12
|
+
end
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
def test_decode
|
16
|
+
{
|
17
|
+
"0" => 0,
|
18
|
+
"hjNv8tS3K" => 3781504209452600,
|
19
|
+
"lYGhA16ahyf" => 18446744073709551615
|
20
|
+
}.each do |base62, int|
|
21
|
+
assert_equal int, Base62.decode(base62)
|
22
|
+
end
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def test_negative_numbers
|
26
|
+
assert_nil Base62.encode(-123)
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: base62-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Yue
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,8 @@ files:
|
|
52
52
|
- Rakefile
|
53
53
|
- base62-rb.gemspec
|
54
54
|
- benchmarks/comparison.rb
|
55
|
+
- benchmarks/decode_bench.rb
|
56
|
+
- benchmarks/encode_bench.rb
|
55
57
|
- lib/base62-rb.rb
|
56
58
|
- lib/base62/version.rb
|
57
59
|
- test/base62_test.rb
|
@@ -75,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
77
|
version: '0'
|
76
78
|
requirements: []
|
77
79
|
rubyforge_project:
|
78
|
-
rubygems_version: 2.
|
80
|
+
rubygems_version: 2.5.1
|
79
81
|
signing_key:
|
80
82
|
specification_version: 4
|
81
83
|
summary: Fast Base62 encoding and decoding in Ruby
|