uniflake 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/uniflake/generator_ids/number.rb +18 -0
- data/lib/uniflake/generators/discord.rb +8 -0
- data/lib/uniflake/generators/snowflake.rb +8 -0
- data/lib/uniflake/generators/uniflake.rb +65 -0
- data/lib/uniflake/sequence_resolvers/memory.rb +18 -0
- data/lib/uniflake/sequence_resolvers/random.rb +15 -0
- data/lib/uniflake/sequence_resolvers/redis.rb +37 -0
- data/lib/uniflake.rb +11 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f80b6d4ffd25a72a9a88dcec412a922514d9c6e631ac4244c9dfb3d2e590018f
|
4
|
+
data.tar.gz: 5405b316f49e42ce17876f062302d44d447d165152138cc2286ce04d7e176070
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 39ae675c29e7de6674043d050d0ce7b133316b481a5be6b4026dd013312afd2b49cc75717760db94b14095ea680db4d960a0576270408b8588e7306861d80c59
|
7
|
+
data.tar.gz: 7c9270a843f01f0600fd0212dfbdfad0d4d1a1e26b7c60c6b3f143194a9d3a34248001ce5bdf9aae22a403fe061ad1089a48756d12c9699ce178e2537ebb7023
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Uniflake
|
2
|
+
|
3
|
+
class Number
|
4
|
+
BIT_LENGTH = 10
|
5
|
+
MAX_ID = (1 << BIT_LENGTH) - 1
|
6
|
+
|
7
|
+
attr_reader :id
|
8
|
+
|
9
|
+
def initialize(id = 0)
|
10
|
+
raise OverflowError, "invalid generator id (#{id} > #{MAX_ID})" if id > MAX_ID
|
11
|
+
@id = id
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_i
|
15
|
+
@id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Uniflake::Generators
|
2
|
+
|
3
|
+
class Uniflake
|
4
|
+
STRUCTURE = {
|
5
|
+
timestamp: 41,
|
6
|
+
generator_id: 10,
|
7
|
+
sequence: 12
|
8
|
+
}
|
9
|
+
|
10
|
+
DEFAULT_EPOCH_START_AT = Time.new(2022, 01, 01)
|
11
|
+
|
12
|
+
MAX_TIMESTAMP = (1 << STRUCTURE[:timestamp]) - 1
|
13
|
+
MAX_GENERATOR_ID = (1 << STRUCTURE[:generator_id]) - 1
|
14
|
+
MAX_SEQUENCE = (1 << STRUCTURE[:sequence]) - 1
|
15
|
+
|
16
|
+
def initialize(sequence_resolver, epoch_start_at = self::class::DEFAULT_EPOCH_START_AT, generator_id = 0)
|
17
|
+
@generator_id = generator_id.to_i
|
18
|
+
raise Error, "invalid generator_id (#{@generator_id} > #{self::class::MAX_GENERATOR_ID})" if @generator_id > self::class::MAX_GENERATOR_ID
|
19
|
+
|
20
|
+
@epoch_start_at = epoch_start_at.strftime('%s%L').to_i
|
21
|
+
now = Time.now.strftime('%s%L').to_i
|
22
|
+
raise Error, "invalid epoch (#{@epoch_start_at} > #{now})" if @epoch_start_at > now
|
23
|
+
|
24
|
+
@sequence_resolver = sequence_resolver
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate
|
28
|
+
now = Time.now.strftime('%s%L').to_i
|
29
|
+
|
30
|
+
sequence = @sequence_resolver.resolve(@generator_id, now, self::class::MAX_SEQUENCE)
|
31
|
+
raise Error, "invalid sequence (#{sequence} > #{self::class::MAX_SEQUENCE})" if sequence > self::class::MAX_SEQUENCE
|
32
|
+
|
33
|
+
timestamp = now - @epoch_start_at
|
34
|
+
raise Error, "invalid timestamp (#{timestamp} > #{self::class::MAX_TIMESTAMP})" if timestamp > self::class::MAX_TIMESTAMP
|
35
|
+
|
36
|
+
self.class.compose(timestamp, @generator_id, sequence)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.compose(timestamp, generator_id, sequence)
|
40
|
+
shift = 0
|
41
|
+
id = 0
|
42
|
+
self::STRUCTURE.keys.reverse.each do |field|
|
43
|
+
id += binding.local_variable_get(field) << shift
|
44
|
+
shift += self::STRUCTURE[field]
|
45
|
+
end
|
46
|
+
id
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse(id)
|
50
|
+
fields = self.class.parse(id)
|
51
|
+
fields[:timestamp] += @epoch_start_at if fields.has_key? :timestamp
|
52
|
+
fields
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.parse(id)
|
56
|
+
id = id.to_s(2).rjust(self::STRUCTURE.values.sum, "0")
|
57
|
+
fields = {}
|
58
|
+
self::STRUCTURE.each do |field, length|
|
59
|
+
fields[field] = id[0..length - 1].to_i(2)
|
60
|
+
id[0..length - 1] = id[length..-1]
|
61
|
+
end
|
62
|
+
fields
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Uniflake::SequenceResolvers
|
2
|
+
|
3
|
+
class Memory
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@sequences = {}
|
7
|
+
@last_time = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def resolve(int_generator_id, time, max_value = 0)
|
11
|
+
raise "invalid time" if time < @last_time[int_generator_id].to_i
|
12
|
+
|
13
|
+
@sequences[int_generator_id] = time == @last_time[int_generator_id].to_i ? @sequences[int_generator_id].to_i + 1 : 0
|
14
|
+
@last_time[int_generator_id] = time
|
15
|
+
@sequences[int_generator_id]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Uniflake::SequenceResolvers
|
2
|
+
|
3
|
+
class Random
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@prng = Random.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def resolve(int_generator_id, time, max_value = 0)
|
10
|
+
raise "invalid time" if time < @last_time.to_i
|
11
|
+
@last_time = time
|
12
|
+
@prng.rand max_value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Uniflake::SequenceResolvers
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
|
5
|
+
def initialize(client, key_prefix = '')
|
6
|
+
@client = client
|
7
|
+
@prefix = key_prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
def resolve(int_generator_id, time, max_value = 0)
|
11
|
+
last_time = connection.get(@prefix + int_generator_id.to_s + "_last_time").to_i
|
12
|
+
raise "invalid time" if time < last_time
|
13
|
+
|
14
|
+
connection.set @prefix + int_generator_id.to_s + "_last_time", time
|
15
|
+
|
16
|
+
connection.eval lua, [@prefix + int_generator_id.to_s + time.to_s], ['0', '10']
|
17
|
+
end
|
18
|
+
|
19
|
+
def connection
|
20
|
+
if @client.class.method_defined? 'with'
|
21
|
+
@client.with { |connection| return connection }
|
22
|
+
end
|
23
|
+
|
24
|
+
@client
|
25
|
+
end
|
26
|
+
|
27
|
+
def lua
|
28
|
+
<<~LUA
|
29
|
+
if redis.call('set', KEYS[1], ARGV[1], "EX", ARGV[2], "NX") then
|
30
|
+
return 0
|
31
|
+
else
|
32
|
+
return redis.call('incr', KEYS[1])
|
33
|
+
end
|
34
|
+
LUA
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/uniflake.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Uniflake
|
2
|
+
require_relative 'uniflake/generators/uniflake.rb'
|
3
|
+
require_relative 'uniflake/generators/snowflake.rb'
|
4
|
+
require_relative 'uniflake/generators/discord.rb'
|
5
|
+
|
6
|
+
require_relative 'uniflake/sequence_resolvers/memory.rb'
|
7
|
+
require_relative 'uniflake/sequence_resolvers/random.rb'
|
8
|
+
require_relative 'uniflake/sequence_resolvers/redis.rb'
|
9
|
+
|
10
|
+
require_relative 'uniflake/generator_ids/number.rb'
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: uniflake
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- nebo7
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: ''
|
14
|
+
email: ''
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/uniflake.rb
|
20
|
+
- lib/uniflake/generator_ids/number.rb
|
21
|
+
- lib/uniflake/generators/discord.rb
|
22
|
+
- lib/uniflake/generators/snowflake.rb
|
23
|
+
- lib/uniflake/generators/uniflake.rb
|
24
|
+
- lib/uniflake/sequence_resolvers/memory.rb
|
25
|
+
- lib/uniflake/sequence_resolvers/random.rb
|
26
|
+
- lib/uniflake/sequence_resolvers/redis.rb
|
27
|
+
homepage: https://github.com/nebo7/uniflake
|
28
|
+
licenses:
|
29
|
+
- MIT
|
30
|
+
metadata: {}
|
31
|
+
post_install_message:
|
32
|
+
rdoc_options: []
|
33
|
+
require_paths:
|
34
|
+
- lib
|
35
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
requirements: []
|
46
|
+
rubygems_version: 3.2.22
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: ''
|
50
|
+
test_files: []
|