redis-protocol 0.1.0 → 0.1.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.
- data/VERSION +1 -1
- data/lib/redis-protocol.rb +3 -136
- data/lib/redis-protocol/request.rb +134 -0
- data/lib/redis-protocol/static_request.rb +74 -0
- metadata +4 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/redis-protocol.rb
CHANGED
@@ -1,142 +1,9 @@
|
|
1
1
|
# start with *, $, alpha-numeric, \r
|
2
2
|
|
3
3
|
module RedisProtocol
|
4
|
-
class Request
|
5
|
-
attr_reader :raw_data, :type, :component_count, :components
|
6
|
-
|
7
|
-
def initialize(data)
|
8
|
-
@raw_data = data
|
9
|
-
@type, @components, @component_count, @current = nil, [], 0, ''
|
10
|
-
@next_length = 0
|
11
|
-
parse
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def parse
|
17
|
-
@raw_data.chars.each do |c|
|
18
|
-
@type ||= c.eql?('*') ? :standard : :inline
|
19
|
-
|
20
|
-
send(@type, c)
|
21
|
-
end
|
22
|
-
|
23
|
-
complete_token
|
24
|
-
@component_count += @components.count if @type == :inline
|
25
|
-
end
|
26
|
-
=begin
|
27
|
-
Inline Parser
|
28
|
-
=end
|
29
|
-
def inline(char)
|
30
|
-
c = char.strip
|
31
|
-
|
32
|
-
if c.empty? and not @current.empty?
|
33
|
-
complete_token
|
34
|
-
else
|
35
|
-
@current += c
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
=begin
|
40
|
-
Standard Parser
|
41
|
-
=end
|
42
|
-
|
43
|
-
def standard(char)
|
44
|
-
@token_type ||= detect_token_type char
|
45
|
-
@doubt_str = ''
|
46
|
-
|
47
|
-
send(@token_type, char)
|
48
|
-
end
|
49
|
-
|
50
|
-
def args_counts(char)
|
51
|
-
return if char.eql? '*'
|
52
|
-
|
53
|
-
tokenize char do
|
54
|
-
@component_count = @current.to_i
|
55
|
-
clean_context
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def length(char)
|
60
|
-
return if char.eql? '$'
|
61
|
-
|
62
|
-
tokenize char do
|
63
|
-
@next_length = @current.to_i
|
64
|
-
clean_context
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def payload(char)
|
69
|
-
return if char.eql? '$'
|
70
|
-
|
71
|
-
@next_length = 0 if @current.length == @next_length
|
72
|
-
|
73
|
-
tokenize char do
|
74
|
-
@next_length = 0
|
75
|
-
complete_token
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def tokenize(char, &block)
|
80
|
-
reach_delimiter = consume_delimiter(char)
|
81
|
-
|
82
|
-
if reach_delimiter
|
83
|
-
if @doubt_str.empty?
|
84
|
-
block.call
|
85
|
-
end
|
86
|
-
return
|
87
|
-
end
|
88
|
-
|
89
|
-
@current += char
|
90
|
-
end
|
91
|
-
|
92
|
-
=begin
|
93
|
-
Common Utilities
|
94
|
-
=end
|
95
|
-
|
96
|
-
def complete_token
|
97
|
-
@components << @current
|
98
|
-
clean_context
|
99
|
-
end
|
100
|
-
|
101
|
-
def clean_context
|
102
|
-
@current = ''
|
103
|
-
@token_type = nil
|
104
|
-
end
|
105
|
-
|
106
|
-
def consume_delimiter(char)
|
107
|
-
return false if @next_length != 0
|
108
|
-
|
109
|
-
if @doubt and char.eql? "\n"
|
110
|
-
@doubt_str = ''
|
111
|
-
@doubt = false
|
112
|
-
return true
|
113
|
-
end
|
114
|
-
|
115
|
-
if not @doubt and char.eql? "\r"
|
116
|
-
@doubt_str = "\r"
|
117
|
-
@doubt = true
|
118
|
-
return true
|
119
|
-
end
|
120
|
-
|
121
|
-
@doubt = false
|
122
|
-
@current += @doubt_str unless @doubt_str.empty?
|
123
|
-
@doubt_str = ''
|
124
|
-
return false
|
125
|
-
end
|
126
|
-
|
127
|
-
def detect_token_type(char)
|
128
|
-
case char
|
129
|
-
when '*'
|
130
|
-
:args_counts
|
131
|
-
when '$'
|
132
|
-
:length
|
133
|
-
else
|
134
|
-
:payload
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
4
|
class Response
|
140
5
|
|
141
6
|
end
|
142
|
-
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'redis-protocol/request'
|
@@ -0,0 +1,134 @@
|
|
1
|
+
class RedisProtocol::Request
|
2
|
+
attr_reader :raw_data, :type, :component_count, :components
|
3
|
+
|
4
|
+
def initialize(data)
|
5
|
+
@raw_data = data
|
6
|
+
@type, @components, @component_count, @current = nil, [], 0, ''
|
7
|
+
@next_length = 0
|
8
|
+
parse
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def parse
|
14
|
+
@raw_data.chars.each do |c|
|
15
|
+
@type ||= c.eql?('*') ? :standard : :inline
|
16
|
+
|
17
|
+
send(@type, c)
|
18
|
+
end
|
19
|
+
|
20
|
+
complete_token
|
21
|
+
@component_count += @components.count if @type == :inline
|
22
|
+
end
|
23
|
+
=begin
|
24
|
+
Inline Parser
|
25
|
+
=end
|
26
|
+
def inline(char)
|
27
|
+
c = char.strip
|
28
|
+
|
29
|
+
if c.empty? and not @current.empty?
|
30
|
+
complete_token
|
31
|
+
else
|
32
|
+
@current += c
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
=begin
|
37
|
+
Standard Parser
|
38
|
+
=end
|
39
|
+
|
40
|
+
def standard(char)
|
41
|
+
@token_type ||= detect_token_type char
|
42
|
+
@doubt_str = ''
|
43
|
+
|
44
|
+
send(@token_type, char)
|
45
|
+
end
|
46
|
+
|
47
|
+
def args_counts(char)
|
48
|
+
return if char.eql? '*'
|
49
|
+
|
50
|
+
tokenize char do
|
51
|
+
@component_count = @current.to_i
|
52
|
+
clean_context
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def length(char)
|
57
|
+
return if char.eql? '$'
|
58
|
+
|
59
|
+
tokenize char do
|
60
|
+
@next_length = @current.to_i
|
61
|
+
clean_context
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def payload(char)
|
66
|
+
return if char.eql? '$'
|
67
|
+
|
68
|
+
@next_length = 0 if @current.length == @next_length
|
69
|
+
|
70
|
+
tokenize char do
|
71
|
+
@next_length = 0
|
72
|
+
complete_token
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def tokenize(char, &block)
|
77
|
+
reach_delimiter = consume_delimiter(char)
|
78
|
+
|
79
|
+
if reach_delimiter
|
80
|
+
if @doubt_str.empty?
|
81
|
+
block.call
|
82
|
+
end
|
83
|
+
return
|
84
|
+
end
|
85
|
+
|
86
|
+
@current += char
|
87
|
+
end
|
88
|
+
|
89
|
+
=begin
|
90
|
+
Common Utilities
|
91
|
+
=end
|
92
|
+
|
93
|
+
def complete_token
|
94
|
+
@components << @current
|
95
|
+
clean_context
|
96
|
+
end
|
97
|
+
|
98
|
+
def clean_context
|
99
|
+
@current = ''
|
100
|
+
@token_type = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
def consume_delimiter(char)
|
104
|
+
return false if @next_length != 0
|
105
|
+
|
106
|
+
if @doubt and char.eql? "\n"
|
107
|
+
@doubt_str = ''
|
108
|
+
@doubt = false
|
109
|
+
return true
|
110
|
+
end
|
111
|
+
|
112
|
+
if not @doubt and char.eql? "\r"
|
113
|
+
@doubt_str = "\r"
|
114
|
+
@doubt = true
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
@doubt = false
|
119
|
+
@current += @doubt_str unless @doubt_str.empty?
|
120
|
+
@doubt_str = ''
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
|
124
|
+
def detect_token_type(char)
|
125
|
+
case char
|
126
|
+
when '*'
|
127
|
+
:args_counts
|
128
|
+
when '$'
|
129
|
+
:length
|
130
|
+
else
|
131
|
+
:payload
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
class RedisProtocol::StaticRequest
|
2
|
+
attr_reader :raw_data, :type
|
3
|
+
OP_DELIMITER = "\r\n"
|
4
|
+
|
5
|
+
def initialize(data)
|
6
|
+
@raw_data = data
|
7
|
+
parse_request
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse_request
|
11
|
+
@type = check_request_type
|
12
|
+
|
13
|
+
send @type, @raw_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.recognize(data)
|
17
|
+
(check_request_type == :inline) ? data.split[0] : data.split(OP_DELIMITER)[2]
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_request_type
|
21
|
+
(@raw_data.start_with? '*') ? :standard : :inline
|
22
|
+
end
|
23
|
+
|
24
|
+
def standard
|
25
|
+
components = []
|
26
|
+
data = @raw_data.dup
|
27
|
+
operand_length, payload = operand_length_for data.split(OP_DELIMITER)
|
28
|
+
1.upto(operand_length).each do |_|
|
29
|
+
data, payload = unpack_payload(payload)
|
30
|
+
components << data
|
31
|
+
end
|
32
|
+
@components = components
|
33
|
+
end
|
34
|
+
|
35
|
+
def operand_length_for(data)
|
36
|
+
operand_length, payload = next_token data
|
37
|
+
raise "invalid packet : #{operand_length}" unless operand_length.start_with? '*'
|
38
|
+
operand_length = operand_length[1..-1].to_i
|
39
|
+
|
40
|
+
[operand_length, payload]
|
41
|
+
end
|
42
|
+
|
43
|
+
def unpack_payload(payload)
|
44
|
+
field_length, payload = next_token payload
|
45
|
+
raise "invalid length format : #{field_length}" unless field_length.start_with? '$'
|
46
|
+
field_length = field_length[1..-1].to_i
|
47
|
+
next_token payload, field_length
|
48
|
+
end
|
49
|
+
|
50
|
+
def inline(data)
|
51
|
+
data.split
|
52
|
+
end
|
53
|
+
|
54
|
+
def next_token(data, length = 0)
|
55
|
+
if data[0].length == length || length == 0
|
56
|
+
[data[0], data[1..-1]]
|
57
|
+
else
|
58
|
+
multiline_token(data, length)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def multiline_token(data, length)
|
63
|
+
val, index = '', 0
|
64
|
+
data.each do |token|
|
65
|
+
val.empty? ? val = token : val += "\r\n#{token}"
|
66
|
+
index += 1
|
67
|
+
|
68
|
+
break if val.length == length
|
69
|
+
end
|
70
|
+
[val, data[index..-1]]
|
71
|
+
end
|
72
|
+
|
73
|
+
private :next_token, :standard, :inline, :operand_length_for, :unpack_payload, :multiline_token
|
74
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-protocol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -126,6 +126,8 @@ files:
|
|
126
126
|
- Rakefile
|
127
127
|
- VERSION
|
128
128
|
- lib/redis-protocol.rb
|
129
|
+
- lib/redis-protocol/request.rb
|
130
|
+
- lib/redis-protocol/static_request.rb
|
129
131
|
- spec/redis-protocol/request_spec.rb
|
130
132
|
- spec/redis-protocol_spec.rb
|
131
133
|
- spec/spec_helper.rb
|
@@ -144,7 +146,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
146
|
version: '0'
|
145
147
|
segments:
|
146
148
|
- 0
|
147
|
-
hash:
|
149
|
+
hash: 1791670625487340402
|
148
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
151
|
none: false
|
150
152
|
requirements:
|