sessionm-thrift 0.8.0.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/CHANGELOG +1 -0
- data/README +43 -0
- data/benchmark/Benchmark.thrift +24 -0
- data/benchmark/benchmark.rb +271 -0
- data/benchmark/client.rb +74 -0
- data/benchmark/server.rb +82 -0
- data/benchmark/thin_server.rb +44 -0
- data/ext/binary_protocol_accelerated.c +441 -0
- data/ext/binary_protocol_accelerated.h +20 -0
- data/ext/compact_protocol.c +618 -0
- data/ext/compact_protocol.h +20 -0
- data/ext/constants.h +96 -0
- data/ext/extconf.rb +30 -0
- data/ext/macros.h +41 -0
- data/ext/memory_buffer.c +131 -0
- data/ext/memory_buffer.h +20 -0
- data/ext/protocol.c +185 -0
- data/ext/protocol.h +20 -0
- data/ext/strlcpy.c +41 -0
- data/ext/strlcpy.h +30 -0
- data/ext/struct.c +691 -0
- data/ext/struct.h +25 -0
- data/ext/thrift_native.c +196 -0
- data/lib/thrift.rb +64 -0
- data/lib/thrift/client.rb +62 -0
- data/lib/thrift/core_ext.rb +23 -0
- data/lib/thrift/core_ext/fixnum.rb +29 -0
- data/lib/thrift/exceptions.rb +84 -0
- data/lib/thrift/processor.rb +57 -0
- data/lib/thrift/protocol/base_protocol.rb +290 -0
- data/lib/thrift/protocol/binary_protocol.rb +229 -0
- data/lib/thrift/protocol/binary_protocol_accelerated.rb +39 -0
- data/lib/thrift/protocol/compact_protocol.rb +426 -0
- data/lib/thrift/serializer/deserializer.rb +33 -0
- data/lib/thrift/serializer/serializer.rb +34 -0
- data/lib/thrift/server/base_server.rb +31 -0
- data/lib/thrift/server/mongrel_http_server.rb +58 -0
- data/lib/thrift/server/nonblocking_server.rb +305 -0
- data/lib/thrift/server/simple_server.rb +43 -0
- data/lib/thrift/server/thread_pool_server.rb +75 -0
- data/lib/thrift/server/threaded_server.rb +47 -0
- data/lib/thrift/struct.rb +237 -0
- data/lib/thrift/struct_union.rb +192 -0
- data/lib/thrift/thrift_native.rb +24 -0
- data/lib/thrift/transport/base_server_transport.rb +37 -0
- data/lib/thrift/transport/base_transport.rb +107 -0
- data/lib/thrift/transport/buffered_transport.rb +108 -0
- data/lib/thrift/transport/framed_transport.rb +116 -0
- data/lib/thrift/transport/http_client_transport.rb +51 -0
- data/lib/thrift/transport/io_stream_transport.rb +39 -0
- data/lib/thrift/transport/memory_buffer_transport.rb +125 -0
- data/lib/thrift/transport/server_socket.rb +63 -0
- data/lib/thrift/transport/socket.rb +137 -0
- data/lib/thrift/transport/unix_server_socket.rb +60 -0
- data/lib/thrift/transport/unix_socket.rb +40 -0
- data/lib/thrift/types.rb +101 -0
- data/lib/thrift/union.rb +179 -0
- data/spec/ThriftSpec.thrift +132 -0
- data/spec/base_protocol_spec.rb +160 -0
- data/spec/base_transport_spec.rb +351 -0
- data/spec/binary_protocol_accelerated_spec.rb +46 -0
- data/spec/binary_protocol_spec.rb +61 -0
- data/spec/binary_protocol_spec_shared.rb +375 -0
- data/spec/client_spec.rb +100 -0
- data/spec/compact_protocol_spec.rb +144 -0
- data/spec/exception_spec.rb +142 -0
- data/spec/http_client_spec.rb +64 -0
- data/spec/mongrel_http_server_spec.rb +117 -0
- data/spec/nonblocking_server_spec.rb +265 -0
- data/spec/processor_spec.rb +83 -0
- data/spec/serializer_spec.rb +69 -0
- data/spec/server_socket_spec.rb +80 -0
- data/spec/server_spec.rb +159 -0
- data/spec/socket_spec.rb +61 -0
- data/spec/socket_spec_shared.rb +104 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/struct_spec.rb +295 -0
- data/spec/types_spec.rb +116 -0
- data/spec/union_spec.rb +193 -0
- data/spec/unix_socket_spec.rb +108 -0
- metadata +247 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Thrift
|
21
|
+
class SimpleServer < BaseServer
|
22
|
+
def serve
|
23
|
+
begin
|
24
|
+
@server_transport.listen
|
25
|
+
loop do
|
26
|
+
client = @server_transport.accept
|
27
|
+
trans = @transport_factory.get_transport(client)
|
28
|
+
prot = @protocol_factory.get_protocol(trans)
|
29
|
+
begin
|
30
|
+
loop do
|
31
|
+
@processor.process(prot, prot)
|
32
|
+
end
|
33
|
+
rescue Thrift::TransportException, Thrift::ProtocolException
|
34
|
+
ensure
|
35
|
+
trans.close
|
36
|
+
end
|
37
|
+
end
|
38
|
+
ensure
|
39
|
+
@server_transport.close
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'thread'
|
21
|
+
|
22
|
+
module Thrift
|
23
|
+
class ThreadPoolServer < BaseServer
|
24
|
+
def initialize(processor, server_transport, transport_factory=nil, protocol_factory=nil, num=20)
|
25
|
+
super(processor, server_transport, transport_factory, protocol_factory)
|
26
|
+
@thread_q = SizedQueue.new(num)
|
27
|
+
@exception_q = Queue.new
|
28
|
+
@running = false
|
29
|
+
end
|
30
|
+
|
31
|
+
## exceptions that happen in worker threads will be relayed here and
|
32
|
+
## must be caught. 'retry' can be used to continue. (threads will
|
33
|
+
## continue to run while the exception is being handled.)
|
34
|
+
def rescuable_serve
|
35
|
+
Thread.new { serve } unless @running
|
36
|
+
@running = true
|
37
|
+
raise @exception_q.pop
|
38
|
+
end
|
39
|
+
|
40
|
+
## exceptions that happen in worker threads simply cause that thread
|
41
|
+
## to die and another to be spawned in its place.
|
42
|
+
def serve
|
43
|
+
@server_transport.listen
|
44
|
+
|
45
|
+
begin
|
46
|
+
loop do
|
47
|
+
@thread_q.push(:token)
|
48
|
+
Thread.new do
|
49
|
+
begin
|
50
|
+
loop do
|
51
|
+
client = @server_transport.accept
|
52
|
+
trans = @transport_factory.get_transport(client)
|
53
|
+
prot = @protocol_factory.get_protocol(trans)
|
54
|
+
begin
|
55
|
+
loop do
|
56
|
+
@processor.process(prot, prot)
|
57
|
+
end
|
58
|
+
rescue Thrift::TransportException, Thrift::ProtocolException => e
|
59
|
+
ensure
|
60
|
+
trans.close
|
61
|
+
end
|
62
|
+
end
|
63
|
+
rescue => e
|
64
|
+
@exception_q.push(e)
|
65
|
+
ensure
|
66
|
+
@thread_q.pop # thread died!
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
@server_transport.close
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'thread'
|
21
|
+
|
22
|
+
module Thrift
|
23
|
+
class ThreadedServer < BaseServer
|
24
|
+
def serve
|
25
|
+
begin
|
26
|
+
@server_transport.listen
|
27
|
+
loop do
|
28
|
+
client = @server_transport.accept
|
29
|
+
trans = @transport_factory.get_transport(client)
|
30
|
+
prot = @protocol_factory.get_protocol(trans)
|
31
|
+
Thread.new(prot, trans) do |p, t|
|
32
|
+
begin
|
33
|
+
loop do
|
34
|
+
@processor.process(p, p)
|
35
|
+
end
|
36
|
+
rescue Thrift::TransportException, Thrift::ProtocolException
|
37
|
+
ensure
|
38
|
+
t.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
ensure
|
43
|
+
@server_transport.close
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,237 @@
|
|
1
|
+
#
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'set'
|
21
|
+
|
22
|
+
module Thrift
|
23
|
+
module Struct
|
24
|
+
def initialize(d={}, &block)
|
25
|
+
# get a copy of the default values to work on, removing defaults in favor of arguments
|
26
|
+
fields_with_defaults = fields_with_default_values.dup
|
27
|
+
|
28
|
+
# check if the defaults is empty, or if there are no parameters for this
|
29
|
+
# instantiation, and if so, don't bother overriding defaults.
|
30
|
+
unless fields_with_defaults.empty? || d.empty?
|
31
|
+
d.each_key do |name|
|
32
|
+
fields_with_defaults.delete(name.to_s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# assign all the user-specified arguments
|
37
|
+
unless d.empty?
|
38
|
+
d.each do |name, value|
|
39
|
+
unless name_to_id(name.to_s)
|
40
|
+
raise Exception, "Unknown key given to #{self.class}.new: #{name}"
|
41
|
+
end
|
42
|
+
Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
|
43
|
+
instance_variable_set("@#{name}", value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# assign all the default values
|
48
|
+
unless fields_with_defaults.empty?
|
49
|
+
fields_with_defaults.each do |name, default_value|
|
50
|
+
instance_variable_set("@#{name}", (default_value.dup rescue default_value))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
yield self if block_given?
|
55
|
+
end
|
56
|
+
|
57
|
+
def fields_with_default_values
|
58
|
+
fields_with_default_values = self.class.instance_variable_get(:@fields_with_default_values)
|
59
|
+
unless fields_with_default_values
|
60
|
+
fields_with_default_values = {}
|
61
|
+
struct_fields.each do |fid, field_def|
|
62
|
+
unless field_def[:default].nil?
|
63
|
+
fields_with_default_values[field_def[:name]] = field_def[:default]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
self.class.instance_variable_set(:@fields_with_default_values, fields_with_default_values)
|
67
|
+
end
|
68
|
+
fields_with_default_values
|
69
|
+
end
|
70
|
+
|
71
|
+
def inspect(skip_optional_nulls = true)
|
72
|
+
fields = []
|
73
|
+
each_field do |fid, field_info|
|
74
|
+
name = field_info[:name]
|
75
|
+
value = instance_variable_get("@#{name}")
|
76
|
+
unless skip_optional_nulls && field_info[:optional] && value.nil?
|
77
|
+
fields << "#{name}:#{inspect_field(value, field_info)}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
"<#{self.class} #{fields.join(", ")}>"
|
81
|
+
end
|
82
|
+
|
83
|
+
def read(iprot)
|
84
|
+
iprot.read_struct_begin
|
85
|
+
loop do
|
86
|
+
fname, ftype, fid = iprot.read_field_begin
|
87
|
+
break if (ftype == Types::STOP)
|
88
|
+
handle_message(iprot, fid, ftype)
|
89
|
+
iprot.read_field_end
|
90
|
+
end
|
91
|
+
iprot.read_struct_end
|
92
|
+
validate
|
93
|
+
end
|
94
|
+
|
95
|
+
def write(oprot)
|
96
|
+
validate
|
97
|
+
oprot.write_struct_begin(self.class.name)
|
98
|
+
each_field do |fid, field_info|
|
99
|
+
name = field_info[:name]
|
100
|
+
type = field_info[:type]
|
101
|
+
value = instance_variable_get("@#{name}")
|
102
|
+
unless value.nil?
|
103
|
+
if is_container? type
|
104
|
+
oprot.write_field_begin(name, type, fid)
|
105
|
+
write_container(oprot, value, field_info)
|
106
|
+
oprot.write_field_end
|
107
|
+
else
|
108
|
+
oprot.write_field(name, type, fid, value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
oprot.write_field_stop
|
113
|
+
oprot.write_struct_end
|
114
|
+
end
|
115
|
+
|
116
|
+
def ==(other)
|
117
|
+
return false if other.nil?
|
118
|
+
each_field do |fid, field_info|
|
119
|
+
name = field_info[:name]
|
120
|
+
return false unless other.respond_to?(name) && self.send(name) == other.send(name)
|
121
|
+
end
|
122
|
+
true
|
123
|
+
end
|
124
|
+
|
125
|
+
def eql?(other)
|
126
|
+
self.class == other.class && self == other
|
127
|
+
end
|
128
|
+
|
129
|
+
# This implementation of hash() is inspired by Apache's Java HashCodeBuilder class.
|
130
|
+
def hash
|
131
|
+
total = 17
|
132
|
+
each_field do |fid, field_info|
|
133
|
+
name = field_info[:name]
|
134
|
+
value = self.send(name)
|
135
|
+
total = (total * 37 + value.hash) & 0xffffffff
|
136
|
+
end
|
137
|
+
total
|
138
|
+
end
|
139
|
+
|
140
|
+
def differences(other)
|
141
|
+
diffs = []
|
142
|
+
unless other.is_a?(self.class)
|
143
|
+
diffs << "Different class!"
|
144
|
+
else
|
145
|
+
each_field do |fid, field_info|
|
146
|
+
name = field_info[:name]
|
147
|
+
diffs << "#{name} differs!" unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
diffs
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.field_accessor(klass, field_info)
|
154
|
+
field_name_sym = field_info[:name].to_sym
|
155
|
+
klass.send :attr_reader, field_name_sym
|
156
|
+
klass.send :define_method, "#{field_info[:name]}=" do |value|
|
157
|
+
Thrift.check_type(value, field_info, field_info[:name]) if Thrift.type_checking
|
158
|
+
instance_variable_set("@#{field_name_sym}", value)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.generate_accessors(klass)
|
163
|
+
klass::FIELDS.values.each do |field_info|
|
164
|
+
field_accessor(klass, field_info)
|
165
|
+
qmark_isset_method(klass, field_info)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.qmark_isset_method(klass, field_info)
|
170
|
+
klass.send :define_method, "#{field_info[:name]}?" do
|
171
|
+
!self.send(field_info[:name].to_sym).nil?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def <=>(other)
|
176
|
+
if self.class == other.class
|
177
|
+
each_field do |fid, field_info|
|
178
|
+
v1 = self.send(field_info[:name])
|
179
|
+
v1_set = !v1.nil?
|
180
|
+
v2 = other.send(field_info[:name])
|
181
|
+
v2_set = !v2.nil?
|
182
|
+
if v1_set && !v2_set
|
183
|
+
return -1
|
184
|
+
elsif !v1_set && v2_set
|
185
|
+
return 1
|
186
|
+
elsif v1_set && v2_set
|
187
|
+
cmp = v1 <=> v2
|
188
|
+
if cmp != 0
|
189
|
+
return cmp
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
0
|
194
|
+
else
|
195
|
+
self.class <=> other.class
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
protected
|
200
|
+
|
201
|
+
def self.append_features(mod)
|
202
|
+
if mod.ancestors.include? ::Exception
|
203
|
+
mod.send :class_variable_set, :'@@__thrift_struct_real_initialize', mod.instance_method(:initialize)
|
204
|
+
super
|
205
|
+
# set up our custom initializer so `raise Xception, 'message'` works
|
206
|
+
mod.send :define_method, :struct_initialize, mod.instance_method(:initialize)
|
207
|
+
mod.send :define_method, :initialize, mod.instance_method(:exception_initialize)
|
208
|
+
else
|
209
|
+
super
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def exception_initialize(*args, &block)
|
214
|
+
if args.size == 1 and args.first.is_a? Hash
|
215
|
+
# looks like it's a regular Struct initialize
|
216
|
+
method(:struct_initialize).call(args.first)
|
217
|
+
else
|
218
|
+
# call the Struct initializer first with no args
|
219
|
+
# this will set our field default values
|
220
|
+
method(:struct_initialize).call()
|
221
|
+
# now give it to the exception
|
222
|
+
self.class.send(:class_variable_get, :'@@__thrift_struct_real_initialize').bind(self).call(*args, &block) if args.size > 0
|
223
|
+
# self.class.instance_method(:initialize).bind(self).call(*args, &block)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def handle_message(iprot, fid, ftype)
|
228
|
+
field = struct_fields[fid]
|
229
|
+
if field and field[:type] == ftype
|
230
|
+
value = read_field(iprot, field)
|
231
|
+
instance_variable_set("@#{field[:name]}", value)
|
232
|
+
else
|
233
|
+
iprot.skip(ftype)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
#
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
4
|
+
# distributed with this work for additional information
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
7
|
+
# "License"); you may not use this file except in compliance
|
8
|
+
# with the License. You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
13
|
+
# software distributed under the License is distributed on an
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
15
|
+
# KIND, either express or implied. See the License for the
|
16
|
+
# specific language governing permissions and limitations
|
17
|
+
# under the License.
|
18
|
+
#
|
19
|
+
require 'set'
|
20
|
+
|
21
|
+
module Thrift
|
22
|
+
module Struct_Union
|
23
|
+
def name_to_id(name)
|
24
|
+
names_to_ids = self.class.instance_variable_get(:@names_to_ids)
|
25
|
+
unless names_to_ids
|
26
|
+
names_to_ids = {}
|
27
|
+
struct_fields.each do |fid, field_def|
|
28
|
+
names_to_ids[field_def[:name]] = fid
|
29
|
+
end
|
30
|
+
self.class.instance_variable_set(:@names_to_ids, names_to_ids)
|
31
|
+
end
|
32
|
+
names_to_ids[name]
|
33
|
+
end
|
34
|
+
|
35
|
+
def sorted_field_ids
|
36
|
+
sorted_field_ids = self.class.instance_variable_get(:@sorted_field_ids)
|
37
|
+
unless sorted_field_ids
|
38
|
+
sorted_field_ids = struct_fields.keys.sort
|
39
|
+
self.class.instance_variable_set(:@sorted_field_ids, sorted_field_ids)
|
40
|
+
end
|
41
|
+
sorted_field_ids
|
42
|
+
end
|
43
|
+
|
44
|
+
def each_field
|
45
|
+
sorted_field_ids.each do |fid|
|
46
|
+
data = struct_fields[fid]
|
47
|
+
yield fid, data
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def read_field(iprot, field = {})
|
52
|
+
case field[:type]
|
53
|
+
when Types::STRUCT
|
54
|
+
value = field[:class].new
|
55
|
+
value.read(iprot)
|
56
|
+
when Types::MAP
|
57
|
+
key_type, val_type, size = iprot.read_map_begin
|
58
|
+
# Skip the map contents if the declared key or value types don't match the expected ones.
|
59
|
+
if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type]))
|
60
|
+
size.times do
|
61
|
+
iprot.skip(key_type)
|
62
|
+
iprot.skip(val_type)
|
63
|
+
end
|
64
|
+
value = nil
|
65
|
+
else
|
66
|
+
value = {}
|
67
|
+
size.times do
|
68
|
+
k = read_field(iprot, field_info(field[:key]))
|
69
|
+
v = read_field(iprot, field_info(field[:value]))
|
70
|
+
value[k] = v
|
71
|
+
end
|
72
|
+
end
|
73
|
+
iprot.read_map_end
|
74
|
+
when Types::LIST
|
75
|
+
e_type, size = iprot.read_list_begin
|
76
|
+
# Skip the list contents if the declared element type doesn't match the expected one.
|
77
|
+
if (e_type != field[:element][:type])
|
78
|
+
size.times do
|
79
|
+
iprot.skip(e_type)
|
80
|
+
end
|
81
|
+
value = nil
|
82
|
+
else
|
83
|
+
value = Array.new(size) do |n|
|
84
|
+
read_field(iprot, field_info(field[:element]))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
iprot.read_list_end
|
88
|
+
when Types::SET
|
89
|
+
e_type, size = iprot.read_set_begin
|
90
|
+
# Skip the set contents if the declared element type doesn't match the expected one.
|
91
|
+
if (e_type != field[:element][:type])
|
92
|
+
size.times do
|
93
|
+
iprot.skip(e_type)
|
94
|
+
end
|
95
|
+
else
|
96
|
+
value = Set.new
|
97
|
+
size.times do
|
98
|
+
element = read_field(iprot, field_info(field[:element]))
|
99
|
+
value << element
|
100
|
+
end
|
101
|
+
end
|
102
|
+
iprot.read_set_end
|
103
|
+
else
|
104
|
+
value = iprot.read_type(field[:type])
|
105
|
+
end
|
106
|
+
value
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_data(oprot, value, field)
|
110
|
+
if is_container? field[:type]
|
111
|
+
write_container(oprot, value, field)
|
112
|
+
else
|
113
|
+
oprot.write_type(field[:type], value)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def write_container(oprot, value, field = {})
|
118
|
+
case field[:type]
|
119
|
+
when Types::MAP
|
120
|
+
oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size)
|
121
|
+
value.each do |k, v|
|
122
|
+
write_data(oprot, k, field[:key])
|
123
|
+
write_data(oprot, v, field[:value])
|
124
|
+
end
|
125
|
+
oprot.write_map_end
|
126
|
+
when Types::LIST
|
127
|
+
oprot.write_list_begin(field[:element][:type], value.size)
|
128
|
+
value.each do |elem|
|
129
|
+
write_data(oprot, elem, field[:element])
|
130
|
+
end
|
131
|
+
oprot.write_list_end
|
132
|
+
when Types::SET
|
133
|
+
oprot.write_set_begin(field[:element][:type], value.size)
|
134
|
+
value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets
|
135
|
+
write_data(oprot, v, field[:element])
|
136
|
+
end
|
137
|
+
oprot.write_set_end
|
138
|
+
else
|
139
|
+
raise "Not a container type: #{field[:type]}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
CONTAINER_TYPES = []
|
144
|
+
CONTAINER_TYPES[Types::LIST] = true
|
145
|
+
CONTAINER_TYPES[Types::MAP] = true
|
146
|
+
CONTAINER_TYPES[Types::SET] = true
|
147
|
+
def is_container?(type)
|
148
|
+
CONTAINER_TYPES[type]
|
149
|
+
end
|
150
|
+
|
151
|
+
def field_info(field)
|
152
|
+
{ :type => field[:type],
|
153
|
+
:class => field[:class],
|
154
|
+
:key => field[:key],
|
155
|
+
:value => field[:value],
|
156
|
+
:element => field[:element] }
|
157
|
+
end
|
158
|
+
|
159
|
+
def inspect_field(value, field_info)
|
160
|
+
if enum_class = field_info[:enum_class]
|
161
|
+
"#{enum_class.const_get(:VALUE_MAP)[value]} (#{value})"
|
162
|
+
elsif value.is_a? Hash
|
163
|
+
if field_info[:type] == Types::MAP
|
164
|
+
map_buf = []
|
165
|
+
value.each do |k, v|
|
166
|
+
map_buf << inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value])
|
167
|
+
end
|
168
|
+
"{" + map_buf.join(", ") + "}"
|
169
|
+
else
|
170
|
+
# old-style set
|
171
|
+
inspect_collection(value.keys, field_info)
|
172
|
+
end
|
173
|
+
elsif value.is_a? Array
|
174
|
+
inspect_collection(value, field_info)
|
175
|
+
elsif value.is_a? Set
|
176
|
+
inspect_collection(value, field_info)
|
177
|
+
elsif value.is_a?(String) && field_info[:binary]
|
178
|
+
value.unpack("H*").first
|
179
|
+
else
|
180
|
+
value.inspect
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def inspect_collection(collection, field_info)
|
185
|
+
buf = []
|
186
|
+
collection.each do |k|
|
187
|
+
buf << inspect_field(k, field_info[:element])
|
188
|
+
end
|
189
|
+
"[" + buf.join(", ") + "]"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|