tarantool 0.2.5 → 0.3.0.7
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/Gemfile +14 -12
- data/LICENSE +4 -4
- data/README.md +15 -9
- data/Rakefile +5 -129
- data/lib/tarantool/base_record.rb +288 -0
- data/lib/tarantool/block_db.rb +135 -0
- data/lib/tarantool/callback_db.rb +47 -0
- data/lib/tarantool/core-ext.rb +12 -0
- data/lib/tarantool/em_db.rb +37 -0
- data/lib/tarantool/exceptions.rb +52 -7
- data/lib/tarantool/fiber_db.rb +152 -0
- data/lib/tarantool/light_record.rb +68 -0
- data/lib/tarantool/query.rb +127 -0
- data/lib/tarantool/record/select.rb +59 -0
- data/lib/tarantool/record.rb +93 -282
- data/lib/tarantool/request.rb +351 -52
- data/lib/tarantool/response.rb +108 -45
- data/lib/tarantool/serializers/bson.rb +2 -2
- data/lib/tarantool/serializers.rb +32 -4
- data/lib/tarantool/space_array.rb +153 -0
- data/lib/tarantool/space_hash.rb +262 -0
- data/lib/tarantool/util.rb +182 -0
- data/lib/tarantool/version.rb +3 -0
- data/lib/tarantool.rb +79 -29
- data/test/box.pid +1 -0
- data/test/helper.rb +164 -0
- data/test/run_all.rb +3 -0
- data/test/shared_query.rb +73 -0
- data/test/shared_record.rb +474 -0
- data/test/shared_space_array.rb +284 -0
- data/test/shared_space_hash.rb +239 -0
- data/test/tarant/init.lua +22 -0
- data/test/tarantool.cfg +62 -0
- data/test/tarantool.log +6 -0
- data/{spec/tarantool.cfg → test/tarantool_repl.cfg} +11 -5
- data/test/test_light_record.rb +48 -0
- data/test/test_query_block.rb +6 -0
- data/test/test_query_fiber.rb +7 -0
- data/test/test_record.rb +88 -0
- data/{spec/tarantool/composite_primary_key_spec.rb → test/test_record_composite_pk.rb} +5 -7
- data/test/test_space_array_block.rb +6 -0
- data/test/test_space_array_callback.rb +255 -0
- data/test/test_space_array_callback_nodef.rb +190 -0
- data/test/test_space_array_fiber.rb +7 -0
- data/test/test_space_hash_block.rb +6 -0
- data/test/test_space_hash_fiber.rb +7 -0
- metadata +78 -55
- data/Gemfile.lock +0 -54
- data/examples/em_simple.rb +0 -16
- data/examples/record.rb +0 -68
- data/examples/simple.rb +0 -13
- data/lib/tarantool/requests/call.rb +0 -20
- data/lib/tarantool/requests/delete.rb +0 -18
- data/lib/tarantool/requests/insert.rb +0 -19
- data/lib/tarantool/requests/ping.rb +0 -16
- data/lib/tarantool/requests/select.rb +0 -22
- data/lib/tarantool/requests/update.rb +0 -35
- data/lib/tarantool/requests.rb +0 -19
- data/lib/tarantool/serializers/integer.rb +0 -14
- data/lib/tarantool/serializers/string.rb +0 -14
- data/lib/tarantool/space.rb +0 -39
- data/spec/helpers/let.rb +0 -11
- data/spec/helpers/truncate.rb +0 -12
- data/spec/spec_helper.rb +0 -21
- data/spec/tarantool/em_spec.rb +0 -22
- data/spec/tarantool/record_spec.rb +0 -316
- data/spec/tarantool/request_spec.rb +0 -103
- data/tarantool.gemspec +0 -69
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'tarantool/util'
|
2
|
+
require 'tarantool/request'
|
3
|
+
require 'tarantool/response'
|
4
|
+
require 'tarantool/serializers'
|
5
|
+
require 'tarantool/core-ext'
|
6
|
+
|
7
|
+
module Tarantool
|
8
|
+
class SpaceArray
|
9
|
+
include Request
|
10
|
+
include Util::Array
|
11
|
+
|
12
|
+
def initialize(tarantool, space_no, fields, indexes)
|
13
|
+
@tarantool = tarantool
|
14
|
+
@space_no = space_no
|
15
|
+
|
16
|
+
fields = ([*fields].empty? ? TYPES_AUTO : fields).dup
|
17
|
+
tail_size = Integer === fields.last ? fields.pop : 1
|
18
|
+
fields.map!{|type| check_type(type)}
|
19
|
+
fields << tail_size
|
20
|
+
@fields = fields
|
21
|
+
|
22
|
+
indexes = [*indexes].map{|ind| frozen_array(ind) }.freeze
|
23
|
+
if !indexes.empty?
|
24
|
+
@index_fields = indexes
|
25
|
+
@indexes = _map_indexes(indexes)
|
26
|
+
else
|
27
|
+
@index_fields = nil
|
28
|
+
@indexes = [TYPES_FALLBACK]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def _map_indexes(indexes)
|
33
|
+
indexes.map do |index|
|
34
|
+
(index.map do |i|
|
35
|
+
unless (field = @fields[i]) && !field.is_a?(Integer)
|
36
|
+
raise ArgumentError, "Wrong index field number: #{index} #{i}"
|
37
|
+
end
|
38
|
+
field
|
39
|
+
end << :error).freeze
|
40
|
+
end.freeze
|
41
|
+
end
|
42
|
+
|
43
|
+
def _send_request(type, body, cb)
|
44
|
+
@tarantool._send_request(type, body, cb)
|
45
|
+
end
|
46
|
+
|
47
|
+
def all_by_pks_cb(keys, cb, opts={})
|
48
|
+
all_by_keys_cb(0, keys, cb, opts)
|
49
|
+
end
|
50
|
+
|
51
|
+
def by_pk_cb(pk, cb)
|
52
|
+
first_by_key_cb(0, pk, cb)
|
53
|
+
end
|
54
|
+
|
55
|
+
def all_by_key_cb(index_no, key, cb, opts={})
|
56
|
+
select_cb(index_no, [key], opts[:offset] || 0, opts[:limit] || -1, cb)
|
57
|
+
end
|
58
|
+
|
59
|
+
def first_by_key_cb(index_no, key, cb)
|
60
|
+
select_cb(index_no, [key], 0, :first, cb)
|
61
|
+
end
|
62
|
+
|
63
|
+
def all_by_keys_cb(index_no, keys, cb, opts = {})
|
64
|
+
select_cb(index_no, keys, opts[:offset] || 0, opts[:limit] || -1, cb)
|
65
|
+
end
|
66
|
+
|
67
|
+
def _fix_index_fields(index_fields, keys)
|
68
|
+
sorted = index_fields.sort
|
69
|
+
if index_no = @index_fields.index{|fields| fields.take(index_fields.size).sort == sorted}
|
70
|
+
real_fields = @index_fields[index_no]
|
71
|
+
permutation = index_fields.map{|i| real_fields.index(i)}
|
72
|
+
keys = [*keys].map{|v| [*v].values_at(*permutation)}
|
73
|
+
[index_no, keys]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def select_cb(index_no, keys, offset, limit, cb)
|
78
|
+
if Array === index_no
|
79
|
+
raise ArgumentError, "Has no defined indexes to search index #{index_no}" unless @index_fields
|
80
|
+
index_fields = index_no
|
81
|
+
index_no = @index_fields.index{|fields| fields.take(index_fields.size) == index_fields}
|
82
|
+
unless index_no || index_fields.size == 1
|
83
|
+
index_no, keys = _fix_index_fields(index_fields, keys)
|
84
|
+
unless index_no
|
85
|
+
raise(ArgumentError, "Not found index with field numbers #{index_no}, " +
|
86
|
+
"(defined indexes #{@index_fields})")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
if @index_fields
|
91
|
+
unless index_types = @indexes[index_no]
|
92
|
+
raise ArgumentError, "No index ##{index_no}"
|
93
|
+
end
|
94
|
+
else
|
95
|
+
index_types = _detect_types([*[*keys][0]])
|
96
|
+
end
|
97
|
+
|
98
|
+
_select(@space_no, index_no, offset, limit, keys, cb, @fields, index_types)
|
99
|
+
end
|
100
|
+
|
101
|
+
def insert_cb(tuple, cb, opts = {})
|
102
|
+
_insert(@space_no, BOX_ADD, tuple, @fields, cb, opts[:return_tuple])
|
103
|
+
end
|
104
|
+
|
105
|
+
def replace_cb(tuple, cb, opts = {})
|
106
|
+
_insert(@space_no, BOX_REPLACE, tuple, @fields, cb, opts[:return_tuple])
|
107
|
+
end
|
108
|
+
|
109
|
+
def update_cb(pk, operations, cb, opts = {})
|
110
|
+
_update(@space_no, pk, operations, @fields,
|
111
|
+
@indexes[0] || _detect_types([*pk]),
|
112
|
+
cb, opts[:return_tuple])
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete_cb(pk, cb, opts = {})
|
116
|
+
_delete(@space_no, pk, @fields,
|
117
|
+
@indexes[0] || _detect_types([*pk]),
|
118
|
+
cb, opts[:return_tuple])
|
119
|
+
end
|
120
|
+
|
121
|
+
def invoke_cb(func_name, values, cb, opts = {})
|
122
|
+
values, opts = _space_call_fix_values(values, @space_no, opts)
|
123
|
+
_call(func_name, values, cb, opts)
|
124
|
+
end
|
125
|
+
|
126
|
+
def call_cb(func_name, values, cb, opts = {})
|
127
|
+
values, opts = _space_call_fix_values(values, @space_no, opts)
|
128
|
+
|
129
|
+
opts[:return_tuple] = true if opts[:return_tuple].nil?
|
130
|
+
opts[:returns] ||= @fields if opts[:return_tuple]
|
131
|
+
|
132
|
+
_call(func_name, values, cb, opts)
|
133
|
+
end
|
134
|
+
|
135
|
+
include CommonSpaceBlockMethods
|
136
|
+
# callback with block api
|
137
|
+
def all_by_key_blk(index_no, key, opts={}, &block)
|
138
|
+
all_by_key_cb(index_no, key, block, opts)
|
139
|
+
end
|
140
|
+
|
141
|
+
def first_by_key_blk(index_no, key, &block)
|
142
|
+
first_by_key_cb(index_no, key, block)
|
143
|
+
end
|
144
|
+
|
145
|
+
def all_by_keys_blk(index_no, keys, opts={}, &block)
|
146
|
+
all_by_keys_cb(index_no, keys, block, opts)
|
147
|
+
end
|
148
|
+
|
149
|
+
def select_blk(index_no, keys, offset=0, limit=-1, &block)
|
150
|
+
select_cb(index_no, keys, offset, limit, block)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,262 @@
|
|
1
|
+
require 'tarantool/util'
|
2
|
+
require 'tarantool/request'
|
3
|
+
require 'tarantool/response'
|
4
|
+
require 'tarantool/core-ext'
|
5
|
+
|
6
|
+
module Tarantool
|
7
|
+
class SpaceHash
|
8
|
+
include Request
|
9
|
+
|
10
|
+
def initialize(tarantool, space_no, fields_def, indexes)
|
11
|
+
@tarantool = tarantool
|
12
|
+
@space_no = space_no
|
13
|
+
|
14
|
+
field_to_pos = {}
|
15
|
+
field_to_type = {}
|
16
|
+
field_types = []
|
17
|
+
i = 0
|
18
|
+
last_type = nil
|
19
|
+
for k, t in fields_def
|
20
|
+
k = k.to_sym
|
21
|
+
unless k == :_tail
|
22
|
+
raise ArgumentError, ":_tail field should be defined last" if field_to_pos[:_tail]
|
23
|
+
t = check_type(t)
|
24
|
+
last_type = t
|
25
|
+
field_to_pos[k] = i
|
26
|
+
field_to_type[k] = t
|
27
|
+
field_types << t
|
28
|
+
else
|
29
|
+
t = [*t].map{|tt| check_type(tt)}
|
30
|
+
field_to_pos[:_tail] = i
|
31
|
+
field_to_type[:_tail] = t
|
32
|
+
field_types.concat t
|
33
|
+
field_types << t.size
|
34
|
+
end
|
35
|
+
i += 1
|
36
|
+
end
|
37
|
+
field_to_pos[:_tail] ||= i
|
38
|
+
field_to_type[:_tail] ||= [last_type]
|
39
|
+
@field_to_pos = field_to_pos
|
40
|
+
@field_to_type = field_to_type
|
41
|
+
@field_names = field_to_pos.keys
|
42
|
+
@field_types = field_types
|
43
|
+
@tail_pos = field_to_pos[:_tail]
|
44
|
+
@tail_size = field_to_type[:_tail].size
|
45
|
+
|
46
|
+
@index_fields = [*indexes].map{|ind| [*ind].map{|fld| fld.to_sym}.freeze}.freeze
|
47
|
+
@indexes = _map_indexes(@index_fields)
|
48
|
+
@translators = [TranslateToHash.new(@field_names - [:_tail], @tail_size)].freeze
|
49
|
+
end
|
50
|
+
|
51
|
+
def _add_translator(v)
|
52
|
+
@translators += [v]
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_translator(cb = nil, &block)
|
56
|
+
copy = dup
|
57
|
+
copy._add_translator(cb || block)
|
58
|
+
copy
|
59
|
+
end
|
60
|
+
|
61
|
+
def _map_indexes(indexes)
|
62
|
+
indexes.map do |index|
|
63
|
+
(index.map do |name|
|
64
|
+
@field_to_type[name.to_sym] or raise "Wrong index field name: #{index} #{name}"
|
65
|
+
end << :error).freeze
|
66
|
+
end.freeze
|
67
|
+
end
|
68
|
+
|
69
|
+
def _send_request(type, body, cb)
|
70
|
+
@tarantool._send_request(type, body, cb)
|
71
|
+
end
|
72
|
+
|
73
|
+
def select_cb(keys, offset, limit, cb)
|
74
|
+
index_names = Hash === keys ? keys.keys : keys.first.keys
|
75
|
+
index_no = @index_fields.index{|fields|
|
76
|
+
fields.take(index_names.size) == index_names
|
77
|
+
}
|
78
|
+
unless index_no
|
79
|
+
index_names.sort!
|
80
|
+
index_no = @index_fields.index{|fields|
|
81
|
+
fields.take(index_names.size).sort == index_names
|
82
|
+
}
|
83
|
+
raise ArgumentError, "Could not find index for keys #{index_names}" unless index_no
|
84
|
+
index_names = @index_fields[index_no].take(index_names.size)
|
85
|
+
end
|
86
|
+
index_types = @indexes[index_no]
|
87
|
+
|
88
|
+
unless Hash === keys
|
89
|
+
keys = keys.map{|key| key.values_at(*index_names)}
|
90
|
+
else
|
91
|
+
keys = keys.values_at(*index_names)
|
92
|
+
if keys.all?{|v| Array === v}
|
93
|
+
if (max_size = keys.map{|v| v.size}.max) > 1
|
94
|
+
keys.map!{|v| v.size == max_size ? v :
|
95
|
+
v.size == 1 ? v*max_size :
|
96
|
+
raise(ArgumentError, "size of array keys ought to be 1 or equal to others")
|
97
|
+
}
|
98
|
+
end
|
99
|
+
keys = keys.transpose
|
100
|
+
else
|
101
|
+
keys = [keys]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
_select(@space_no, index_no, offset, limit, keys, cb, @field_types, index_types, @translators)
|
106
|
+
end
|
107
|
+
|
108
|
+
def all_cb(keys, cb, opts = {})
|
109
|
+
select_cb(keys, opts[:offset] || 0, opts[:limit] || -1, cb)
|
110
|
+
end
|
111
|
+
|
112
|
+
def first_cb(key, cb)
|
113
|
+
select_cb([key], 0, :first, cb)
|
114
|
+
end
|
115
|
+
|
116
|
+
def all_by_pks_cb(keys, cb, opts={})
|
117
|
+
keys = [*keys].map{|key| _prepare_pk(key)}
|
118
|
+
_select(@space_no, 0,
|
119
|
+
opts[:offset] || 0, opts[:limit] || -1,
|
120
|
+
keys, cb, @field_types, @indexes[0], @translators)
|
121
|
+
end
|
122
|
+
|
123
|
+
def by_pk_cb(key_array, cb)
|
124
|
+
key_array = _prepare_pk(key_array)
|
125
|
+
_select(@space_no, 0, 0, :first, [key_array], cb, @field_types, @indexes[0], @translators)
|
126
|
+
end
|
127
|
+
|
128
|
+
def _prepare_tuple(tuple)
|
129
|
+
unless (exc = (tuple.keys - @field_names)).empty?
|
130
|
+
raise ArgumentError, "wrong keys #{exc} for tuple"
|
131
|
+
end
|
132
|
+
tuple_ar = tuple.values_at(*@field_names)
|
133
|
+
case tail = tuple_ar.pop
|
134
|
+
when Array
|
135
|
+
tail = tail.flatten(1) if @tail_size > 1
|
136
|
+
tuple_ar.concat tail
|
137
|
+
when nil
|
138
|
+
else
|
139
|
+
raise ArgumentError, "_tail ought to be an array, but it == #{tail.inspect}"
|
140
|
+
end
|
141
|
+
tuple_ar
|
142
|
+
end
|
143
|
+
|
144
|
+
def insert_cb(tuple, cb, opts = {})
|
145
|
+
_insert(@space_no, BOX_ADD, _prepare_tuple(tuple),
|
146
|
+
@field_types, cb, opts[:return_tuple], @translators)
|
147
|
+
end
|
148
|
+
|
149
|
+
def replace_cb(tuple, cb, opts = {})
|
150
|
+
_insert(@space_no, BOX_REPLACE, _prepare_tuple(tuple),
|
151
|
+
@field_types, cb, opts[:return_tuple], @translators)
|
152
|
+
end
|
153
|
+
|
154
|
+
def _prepare_pk(pk)
|
155
|
+
if Hash === pk
|
156
|
+
pk_fields = pk.keys
|
157
|
+
unless (pindex = @index_fields[0]) == pk_fields
|
158
|
+
if !(exc = (pk_fields - pindex)).empty?
|
159
|
+
raise ArgumentError, "Wrong keys #{exc} for primary index"
|
160
|
+
elsif !(exc = (pindex - pk_fields)).empty?
|
161
|
+
raise ArgumentError, "you should provide values for all keys of primary index (missed #{exc})"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
pk.values_at *pindex
|
165
|
+
else
|
166
|
+
[*pk]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def update_cb(pk, operations, cb, opts = {})
|
171
|
+
pk = _prepare_pk(pk)
|
172
|
+
opers = []
|
173
|
+
operations.each{|oper|
|
174
|
+
if Array === oper[0]
|
175
|
+
oper = oper[0] + oper.drop(1)
|
176
|
+
elsif Array === oper[1] && oper.size == 2 && UPDATE_OPS[oper[1][0]]
|
177
|
+
oper = [oper[0]] + oper[1]
|
178
|
+
end
|
179
|
+
case oper[0]
|
180
|
+
when Integer
|
181
|
+
opers << oper[1..-1].unshift(oper[0] + @tail_pos)
|
182
|
+
when :_tail
|
183
|
+
if UPDATE_OPS[oper[1]] == 0
|
184
|
+
tail = oper[2]
|
185
|
+
unless Array === tail[0] && @tail_size > 1
|
186
|
+
tail.each_with_index{|val, i| opers << [i + @tail_pos, :set, val]}
|
187
|
+
else
|
188
|
+
tail.each_with_index{|vals, i|
|
189
|
+
vals.each_with_index{|val, j|
|
190
|
+
opers << [i*@tail_size + j + @tail_pos, :set, val]
|
191
|
+
}
|
192
|
+
}
|
193
|
+
end
|
194
|
+
else
|
195
|
+
raise ArgumentError, "_tail update should be array with operations" unless Array === oper[1] && Array === oper[1][0]
|
196
|
+
if @tail_size == 1 || !(Array === oper[1][0][0])
|
197
|
+
oper[1].each_with_index{|op, i| opers << [i + @tail_pos, op]}
|
198
|
+
else
|
199
|
+
oper[1].each_with_index{|ops, i|
|
200
|
+
ops.each_with_index{|op, j|
|
201
|
+
opers << [i*@tail_size + j + @tail_pos, op]
|
202
|
+
}
|
203
|
+
}
|
204
|
+
end
|
205
|
+
end
|
206
|
+
else
|
207
|
+
opers << oper[1..-1].unshift(
|
208
|
+
@field_to_pos[oper[0]] || raise(ArgumentError, "Not defined field name #{oper[0]}")
|
209
|
+
)
|
210
|
+
end
|
211
|
+
}
|
212
|
+
_update(@space_no, pk, opers, @field_types,
|
213
|
+
@indexes[0], cb, opts[:return_tuple], @translators)
|
214
|
+
end
|
215
|
+
|
216
|
+
def delete_cb(pk, cb, opts = {})
|
217
|
+
_delete(@space_no, _prepare_pk(pk), @field_types,
|
218
|
+
@indexes[0], cb, opts[:return_tuple], @translators)
|
219
|
+
end
|
220
|
+
|
221
|
+
def invoke_cb(func_name, values, cb, opts = {})
|
222
|
+
values, opts = _space_call_fix_values(values, @space_no, opts)
|
223
|
+
_call(func_name, values, cb, opts)
|
224
|
+
end
|
225
|
+
|
226
|
+
def call_cb(func_name, values, cb, opts = {})
|
227
|
+
values, opts = _space_call_fix_values(values, @space_no, opts)
|
228
|
+
|
229
|
+
opts[:return_tuple] = true if opts[:return_tuple].nil?
|
230
|
+
if opts[:return_tuple]
|
231
|
+
if opts[:returns]
|
232
|
+
if Hash === opts[:returns]
|
233
|
+
opts[:returns], *opts[:translators] =
|
234
|
+
_parse_hash_definition(opts[:returns])
|
235
|
+
end
|
236
|
+
else
|
237
|
+
types = @field_to_type.values
|
238
|
+
types << [*types.last].size
|
239
|
+
types.flatten!
|
240
|
+
opts[:returns] = types.flatten
|
241
|
+
opts[:translators] = @translators
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
_call(func_name, values, cb, opts)
|
246
|
+
end
|
247
|
+
|
248
|
+
include CommonSpaceBlockMethods
|
249
|
+
# callback with block api
|
250
|
+
def all_blk(keys, opts = {}, &block)
|
251
|
+
all_cb(keys, block, opts)
|
252
|
+
end
|
253
|
+
|
254
|
+
def first_blk(key, &block)
|
255
|
+
first_blk(key, block)
|
256
|
+
end
|
257
|
+
|
258
|
+
def select_blk(keys, offset=0, limit=1, &block)
|
259
|
+
select_cb(keys, offset, limit, block)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Tarantool
|
2
|
+
module Util
|
3
|
+
module Packer
|
4
|
+
private
|
5
|
+
EMPTY = ''.freeze
|
6
|
+
def unpack_int16(data)
|
7
|
+
data.getbyte(0) + data.getbyte(1) * 256
|
8
|
+
end
|
9
|
+
|
10
|
+
def unpack_int16!(data)
|
11
|
+
int = data.getbyte(0) + data.getbyte(1) * 256
|
12
|
+
data[0, 2] = EMPTY
|
13
|
+
data
|
14
|
+
end
|
15
|
+
|
16
|
+
def unpack_int32(int)
|
17
|
+
(int.getbyte(0) + int.getbyte(1) * 256 +
|
18
|
+
int.getbyte(2) * 65536 + int.getbyte(3) * 16777216)
|
19
|
+
end
|
20
|
+
|
21
|
+
def unpack_int32!(data)
|
22
|
+
int = (data.getbyte(0) + data.getbyte(1) * 256 +
|
23
|
+
data.getbyte(2) * 65536 + data.getbyte(3) * 16777216)
|
24
|
+
data[0, 4] = EMPTY
|
25
|
+
int
|
26
|
+
end
|
27
|
+
|
28
|
+
def unpack_int64!(data)
|
29
|
+
int = (data.getbyte(0) + data.getbyte(1) * 256 +
|
30
|
+
data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
|
31
|
+
data.getbyte(4) << 32 + data.getbyte(5) << 40 +
|
32
|
+
data.getbyte(6) << 48 + data.getbyte(7) << 56
|
33
|
+
)
|
34
|
+
data[0, 8] = EMPTY
|
35
|
+
int
|
36
|
+
end
|
37
|
+
|
38
|
+
def unpack_int64(data)
|
39
|
+
data.getbyte(0) + data.getbyte(1) * 256 +
|
40
|
+
data.getbyte(2) * 65536 + data.getbyte(3) * 16777216 +
|
41
|
+
data.getbyte(4) << 32 + data.getbyte(5) << 40 +
|
42
|
+
data.getbyte(6) << 48 + data.getbyte(7) << 56
|
43
|
+
end
|
44
|
+
|
45
|
+
def ber_size(int)
|
46
|
+
int < 128 ? 1 :
|
47
|
+
int < 16384 ? 2 :
|
48
|
+
int < 2097153 ? 3 :
|
49
|
+
int < 268435456 ? 4 : 5
|
50
|
+
end
|
51
|
+
|
52
|
+
def unpack_ber!(data)
|
53
|
+
res = 0
|
54
|
+
pos = 0
|
55
|
+
while true
|
56
|
+
if (byte = data.getbyte(pos)) <= 127
|
57
|
+
res += byte
|
58
|
+
break
|
59
|
+
else
|
60
|
+
res = (res + (byte - 128)) * 128
|
61
|
+
pos += 1
|
62
|
+
end
|
63
|
+
end
|
64
|
+
data[0, pos+1] = EMPTY
|
65
|
+
res
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module TailGetter
|
70
|
+
private
|
71
|
+
def get_tail_item(array, index, tail)
|
72
|
+
tail == 1 ?
|
73
|
+
array.last :
|
74
|
+
array[array.size - tail + (index - array.size) % tail]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Array
|
79
|
+
def frozen_array(obj)
|
80
|
+
(Array === obj ? obj.dup : [*obj]).freeze
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class AutoType
|
85
|
+
include Packer
|
86
|
+
include Comparable
|
87
|
+
|
88
|
+
attr_reader :data
|
89
|
+
def initialize(data)
|
90
|
+
@data = data
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_int
|
94
|
+
case @data.bytesize
|
95
|
+
when 8
|
96
|
+
unpack_int64(@data)
|
97
|
+
when 4
|
98
|
+
unpack_int32(@data)
|
99
|
+
when 2
|
100
|
+
unpack_int16(@data)
|
101
|
+
else
|
102
|
+
raise ValueError, "Bad field size #{field_size} for integer field ##{i}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
alias to_i to_int
|
106
|
+
|
107
|
+
def coerce(oth)
|
108
|
+
case oth
|
109
|
+
when Numeric
|
110
|
+
[oth, to_i]
|
111
|
+
when String
|
112
|
+
[oth, @data]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
alias to_str data
|
117
|
+
alias to_s data
|
118
|
+
def inspect
|
119
|
+
"<#{self.class.name} data=#{@data.inspect}>"
|
120
|
+
end
|
121
|
+
|
122
|
+
def ==(oth)
|
123
|
+
case oth
|
124
|
+
when Numeric
|
125
|
+
to_i == oth
|
126
|
+
when String
|
127
|
+
@data == oth
|
128
|
+
when AutoType
|
129
|
+
@data == oth.data
|
130
|
+
end
|
131
|
+
end
|
132
|
+
alias eql? ==
|
133
|
+
|
134
|
+
def <=>(oth)
|
135
|
+
case oth
|
136
|
+
when Numeric
|
137
|
+
to_i <=> oth
|
138
|
+
when String
|
139
|
+
@data <=> oth
|
140
|
+
when AutoType
|
141
|
+
@data <=> oth.data
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def +(oth)
|
146
|
+
case oth
|
147
|
+
when Numeric
|
148
|
+
to_i + oth
|
149
|
+
when String
|
150
|
+
@data + oth
|
151
|
+
when AutoType
|
152
|
+
@data + oth.data
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def -(oth) to_i - oth end
|
157
|
+
def *(oth) to_i * oth end
|
158
|
+
def /(oth) to_i / oth end
|
159
|
+
def %(oth) to_i % oth end
|
160
|
+
def **(oth) to_i ** oth end
|
161
|
+
|
162
|
+
def empty?; @data.empty? end
|
163
|
+
def bytesize; @data.bytesize end
|
164
|
+
def size; @data.size end
|
165
|
+
def length; @data.length end
|
166
|
+
def hash; @data.hash end
|
167
|
+
%w{[] sub gsub slice =~ match scan split upcase downcase bytes each_byte
|
168
|
+
byteslice capitalize casecmp center chars each_char chomp chop chr
|
169
|
+
codepoints each_codepoint count crypt delete dump each_line lines
|
170
|
+
empty? encode end_with? getbyte hex include? index intern to_sym
|
171
|
+
ljust lstrip succ next oct ord partition reverse rindex
|
172
|
+
rjust rpartition rstrip squeeze start_with? strip swapcase to_c to_f
|
173
|
+
to_r intern to_sym tr unpack upto}.each do |meth|
|
174
|
+
class_eval <<-EOF, __FILE__, __LINE__
|
175
|
+
def #{meth}(*args, &block)
|
176
|
+
@data.#{meth}(*args, &block)
|
177
|
+
end
|
178
|
+
EOF
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|