ruby_xid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ruby_xid.rb +145 -0
  3. data/lib/xid/base32.rb +141 -0
  4. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '08ba94c6fc740af7f91b899eb0459764ff1dcd7512eac6a575f33bdc6ee8c255'
4
+ data.tar.gz: 7b5eac75b7a82f8b2e67b4df57f5d85d3b451ad62ee4235e97df5f1c1a87675c
5
+ SHA512:
6
+ metadata.gz: '072618ecba2040867b4a04c95d844ddf8e0e50bdbc744455aa096de2e16ae931b0dbdeb6f35ebce103d061d3e52dc49fed82a109300ca342cf17182c5aa58c25'
7
+ data.tar.gz: 3f7061a4a6893c6f77cfb738b39f135ba0b1c66c7ce97014d30e7be9fc1e7e0c95ffe5bc02b10bbc6938ffaa7719b9e623059682bffb91bbf6a7c792e629124e
data/lib/ruby_xid.rb ADDED
@@ -0,0 +1,145 @@
1
+ # Xid implementatin in Ruby
2
+ class Xid
3
+ require 'socket'
4
+ require 'securerandom'
5
+ require 'date'
6
+
7
+ RAW_LEN = 12
8
+ TRIM_LEN = 20
9
+
10
+ def initialize(id = nil)
11
+ @mutex = Mutex.new
12
+ init_rand_int
13
+ @pid = Process.pid
14
+ unless id.nil?
15
+ # Decoded array
16
+ @value = id
17
+ else
18
+ @value = generate_xid
19
+ end
20
+ end
21
+
22
+ def next_xid
23
+ @value = generate_xid
24
+ string
25
+ end
26
+
27
+ def pid
28
+ # type: () -> int
29
+ (@value[7] << 8 | @value[8])
30
+ end
31
+
32
+ def counter
33
+ # type: () -> int
34
+ @value[9] << 16 | @value[10] << 8 | @value[11]
35
+ end
36
+
37
+ def machine
38
+ # type: () -> str
39
+ @value[4..7].map(&:chr)
40
+ end
41
+
42
+ def datetime
43
+ Time.at(time).to_datetime
44
+ end
45
+
46
+ def time
47
+ # type: () -> int
48
+ @value[0] << 24 | @value[1] << 16 | @value[2] << 8 | @value[3]
49
+ end
50
+
51
+ def machine_id
52
+ @machine_id ||= real_machine_id
53
+ end
54
+
55
+ def string
56
+ # type: () -> str
57
+ byte_value = bytes
58
+ Base32.b32encode(byte_value).downcase[0..TRIM_LEN - 1]
59
+ end
60
+
61
+ def bytes
62
+ # type: () -> str
63
+ @value.map(&:chr).join('')
64
+ end
65
+
66
+ def init_rand_int
67
+ # type: () -> int
68
+ @rand_int = begin
69
+ buford = SecureRandom.hex(3).scan(/.{2}/m).map(&:hex)
70
+ buford[0] << 16 | buford[1] << 8 | buford[2]
71
+ end
72
+ end
73
+
74
+ def ==(other_xid)
75
+ # type: (Xid) -> bool
76
+ string < other_xid.string
77
+ end
78
+
79
+ def <(other_xid)
80
+ # type: (Xid) -> bool
81
+ string < other_xid.string
82
+ end
83
+
84
+ def >(other_xid)
85
+ # type: (Xid) -> bool
86
+ string > other_xid.string
87
+ end
88
+
89
+ def self.from_string(str)
90
+ val = Base32.b32decode(str)
91
+ value_check = val.select { |x| x >= 0 && x < 255 }
92
+
93
+ (value_check.length..RAW_LEN - 1).each do |i|
94
+ value_check[i] = false
95
+ end
96
+
97
+ raise 'Invalid Xid' unless value_check.all?
98
+
99
+ Object.const_get(name).new(val)
100
+ end
101
+
102
+ private
103
+
104
+ def real_machine_id
105
+ # type: () -> List[int]
106
+ hostname = Socket.gethostname.encode('utf-8')
107
+ md5 = Digest::MD5.new
108
+ md5 << hostname
109
+ val = md5.digest[0..3]
110
+ val.scan(/.{1}/m).map(&:ord)
111
+ rescue
112
+ SecureRandom.hex(3).scan(/.{2}/m).map(&:hex)
113
+ end
114
+
115
+ def generate_xid
116
+ # type: () -> List[int]
117
+ now = Time.now.to_i
118
+ id = [0] * RAW_LEN
119
+
120
+ id[0] = (now >> 24) & 0xff
121
+ id[1] = (now >> 16) & 0xff
122
+ id[2] = (now >> 8) & 0xff
123
+ id[3] = now & 0xff
124
+
125
+ id[4] = machine_id[0]
126
+ id[5] = machine_id[1]
127
+ id[6] = machine_id[2]
128
+
129
+ id[7] = (@pid >> 8) & 0xff
130
+ id[8] = @pid & 0xff
131
+
132
+ @mutex.synchronize do
133
+ @rand_int += 1
134
+ end
135
+ i = @rand_int
136
+
137
+ id[9] = (i >> 16) & 0xff
138
+ id[10] = (i >> 8) & 0xff
139
+ id[11] = i & 0xff
140
+
141
+ id
142
+ end
143
+ end
144
+
145
+ require_relative 'xid/base32'
data/lib/xid/base32.rb ADDED
@@ -0,0 +1,141 @@
1
+
2
+ class Xid::Base32
3
+
4
+ ENCODE_HEX = '0123456789abcdefghijklmnopqrstuv'.freeze
5
+ PAD_CHAR = '='.freeze
6
+
7
+ # Start class methods
8
+ class << self
9
+
10
+ def decode_hex_map
11
+ Hash[ENCODE_HEX.chars.each_with_index.map { |x, i| [x, i] }]
12
+ end
13
+
14
+ def b32encode(src)
15
+ src = src.scan(/.{1}/m).map(&:ord)
16
+ encode(src, ENCODE_HEX)
17
+ end
18
+
19
+ def b32decode(src)
20
+ decode(src, ENCODE_HEX)
21
+ end
22
+
23
+ def encode(src_str, str_map)
24
+ return '' if src_str.empty?
25
+
26
+ dst = []
27
+ src_len = 0
28
+ while src_str && !src_str.empty?
29
+ src_len = src_str.length
30
+ next_byte = [0] * 8
31
+
32
+ if src_len > 4
33
+ next_byte[7] = src_str[4] & 0x1f
34
+ next_byte[6] = src_str[4] >> 5
35
+ end
36
+ if src_len > 3
37
+ next_byte[6] = next_byte[6] | (src_str[3] << 3) & 0x1f
38
+ next_byte[5] = (src_str[3] >> 2) & 0x1f
39
+ next_byte[4] = src_str[3] >> 7
40
+ end
41
+ if src_len > 2
42
+ next_byte[4] = next_byte[4] | (src_str[2] << 1) & 0x1f
43
+ next_byte[3] = (src_str[2] >> 4) & 0x1f
44
+ end
45
+ if src_len > 1
46
+ next_byte[3] = next_byte[3] | (src_str[1] << 4) & 0x1f
47
+ next_byte[2] = (src_str[1] >> 1) & 0x1f
48
+ next_byte[1] = (src_str[1] >> 6) & 0x1f
49
+ end
50
+ if src_len > 0
51
+ next_byte[1] = next_byte[1] | (src_str[0] << 2) & 0x1f
52
+ next_byte[0] = src_str[0] >> 3
53
+ end
54
+
55
+ next_byte.each do |nb|
56
+ dst << str_map[nb]
57
+ end
58
+ src_str = src_str[5..src_str.length]
59
+ end
60
+
61
+ dst[-1] = '=' if src_len < 5
62
+ if src_len < 4
63
+ dst[-2] = '='
64
+ dst[-3] = '='
65
+ end
66
+ if src_len < 3
67
+ dst[-4] = '='
68
+ end
69
+ if src_len < 2
70
+ dst[-5] = '='
71
+ dst[-6] = '='
72
+ end
73
+
74
+ dst.join('')
75
+ end
76
+
77
+ def decode(src, str_map)
78
+ src.downcase!
79
+
80
+ end_loop = false
81
+ result = []
82
+ while src && !src.empty? && !end_loop
83
+ dst = [0] * 5
84
+ dbuf = [0] * 8
85
+ src_len = 8
86
+
87
+ (0..8).each do |i|
88
+ if i >= src.length
89
+ src_len = i
90
+ end_loop = true
91
+ break
92
+ end
93
+ char = src[i]
94
+ if char == PAD_CHAR
95
+ end_loop = true
96
+ src_len = i
97
+ break
98
+ else
99
+ dbuf[i] = decode_hex_map[char]
100
+ end
101
+ end
102
+
103
+ if src_len >= 8
104
+ dst[4] = (dbuf[6] << 5) | (dbuf[7])
105
+ end
106
+ if src_len >= 7
107
+ dst[3] = (dbuf[4] << 7) | (dbuf[5] << 2) | (dbuf[6] >> 3)
108
+ end
109
+ if src_len >= 5
110
+ dst[2] = (dbuf[3] << 4) | (dbuf[4] >> 1)
111
+ end
112
+ if src_len >= 4
113
+ dst[1] = (dbuf[1] << 6) | (dbuf[2] << 1) | (dbuf[3] >> 4)
114
+ end
115
+ if src_len >= 2
116
+ dst[0] = (dbuf[0] << 3) | (dbuf[1] >> 2)
117
+ end
118
+
119
+ dst = dst.map { |x| x & 0xff }
120
+
121
+ if src_len == 2
122
+ dst = dst[0]
123
+ elsif src_len == 4
124
+ dst = dst[0..1]
125
+ elsif src_len == 5
126
+ dst = dst[0..2]
127
+ elsif src_len == 7
128
+ dst = dst[0..3]
129
+ elsif src_len == 8
130
+ dst = dst[0..4]
131
+ end
132
+
133
+ result += dst
134
+ src = src[8..src.length]
135
+ end
136
+
137
+ result
138
+ end
139
+ end
140
+ # END - Class methods
141
+ end
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_xid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Valarpiraichandran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: a.valarpirai@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/ruby_xid.rb
20
+ - lib/xid/base32.rb
21
+ homepage: http://github.com/valarpirai/ruby_xid
22
+ licenses:
23
+ - MIT
24
+ metadata: {}
25
+ post_install_message:
26
+ rdoc_options: []
27
+ require_paths:
28
+ - lib
29
+ required_ruby_version: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubygems_version: 3.0.6
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Ruby xid implementation
44
+ test_files: []