PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/src/scripts/simple_thrift.rb

http://github.com/nkallen/Rowz
Ruby | 257 lines | 207 code | 40 blank | 10 comment | 12 complexity | d6f130e1fd7ab4df82b6e911e3b21a0f MD5 | raw file
Possible License(s): Apache-2.0
  1. #!/usr/bin/env ruby
  2. #
  3. # This is a simplified form of thrift, useful for clients only, and not
  4. # making any attempt to have good performance. It's intended to be used by
  5. # small command-line tools that don't want to install a dozen ruby files.
  6. #
  7. require 'socket'
  8. require 'getoptlong'
  9. module SimpleThrift
  10. VERSION_1 = 0x8001
  11. # message types
  12. CALL, REPLY, EXCEPTION = (1..3).to_a
  13. # value types
  14. STOP, VOID, BOOL, BYTE, DOUBLE, _, I16, _, I32, _, I64, STRING, STRUCT, MAP, SET, LIST = (0..15).to_a
  15. FORMATS = {
  16. BYTE => "c",
  17. DOUBLE => "G",
  18. I16 => "n",
  19. I32 => "N",
  20. }
  21. SIZES = {
  22. BYTE => 1,
  23. DOUBLE => 8,
  24. I16 => 2,
  25. I32 => 4,
  26. }
  27. module ComplexType
  28. module Extends
  29. def type_id=(n)
  30. @type_id = n
  31. end
  32. def type_id
  33. @type_id
  34. end
  35. end
  36. module Includes
  37. def to_i
  38. self.class.type_id
  39. end
  40. def to_s
  41. args = self.values.map { |v| self.class.type_id == STRUCT ? v.name : v.to_s }.join(", ")
  42. "#{self.class.name}.new(#{args})"
  43. end
  44. end
  45. end
  46. def self.make_type(type_id, name, *args)
  47. klass = Struct.new("STT_#{name}", *args)
  48. klass.send(:extend, ComplexType::Extends)
  49. klass.send(:include, ComplexType::Includes)
  50. klass.type_id = type_id
  51. klass
  52. end
  53. ListType = make_type(LIST, "ListType", :element_type)
  54. MapType = make_type(MAP, "MapType", :key_type, :value_type)
  55. StructType = make_type(STRUCT, "StructType", :struct_class)
  56. class << self
  57. def pack_value(type, value)
  58. case type
  59. when BOOL
  60. [ value ? 1 : 0 ].pack("c")
  61. when STRING
  62. [ value.size, value ].pack("Na*")
  63. when I64
  64. [ value >> 32, value & 0xffffffff ].pack("NN")
  65. when ListType
  66. [ type.element_type.to_i, value.size ].pack("cN") + value.map { |item| pack_value(type.element_type, item) }.join("")
  67. when MapType
  68. [ type.key_type.to_i, type.value_type.to_i, map.size ].pack("ccN") + map.map { |k, v| pack_value(type.key_type, k) + pack_value(type.value_type, v) }.join("")
  69. when StructType
  70. value._pack
  71. else
  72. [ value ].pack(FORMATS[type])
  73. end
  74. end
  75. def read_value(s, type)
  76. case type
  77. when BOOL
  78. s.read(1).unpack("c").first != 0
  79. when STRING
  80. len = s.read(4).unpack("N").first
  81. s.read(len)
  82. when I64
  83. hi, lo = s.read(8).unpack("NN")
  84. (hi << 32) | lo
  85. when LIST
  86. read_list(s)
  87. when MAP
  88. read_map(s)
  89. when STRUCT
  90. read_struct(s)
  91. when ListType
  92. read_list(s, type.element_type)
  93. when MapType
  94. read_map(s, type.key_type, type.value_type)
  95. when StructType
  96. read_struct(s, type.struct_class)
  97. else
  98. s.read(SIZES[type]).unpack(FORMATS[type]).first
  99. end
  100. end
  101. def read_list(s, element_type=nil)
  102. etype, len = s.read(5).unpack("cN")
  103. expected_type = (element_type and element_type.to_i == etype.to_i) ? element_type : etype
  104. rv = []
  105. len.times do
  106. rv << read_value(s, expected_type)
  107. end
  108. rv
  109. end
  110. def read_map(s, key_type=nil, value_type=nil)
  111. ktype, vtype, len = s.read(6).unpack("ccN")
  112. rv = {}
  113. expected_key_type, expected_value_type = if key_type and value_type and key_type.to_i == ktype and value_type.to_i = vtype
  114. [ key_type, value_type ]
  115. else
  116. [ ktype, vtype ]
  117. end
  118. len.times do
  119. key = read_value(s, expected_key_type)
  120. value = read_value(s, expected_value_type)
  121. rv[key] = value
  122. end
  123. rv
  124. end
  125. def read_struct(s, struct_class=nil)
  126. rv = struct_class ? struct_class.new() : nil
  127. while true
  128. type = s.read(1).unpack("c").first
  129. return rv if type == STOP
  130. fid = s.read(2).unpack("n").first
  131. field = struct_class ? struct_class._fields.find { |f| (f.fid == fid) and (f.type.to_i == type) } : nil
  132. value = read_value(s, field ? field.type : type)
  133. rv[field.name] = value if field
  134. end
  135. end
  136. def read_response(s, rv_class)
  137. version, message_type, method_name_len = s.read(8).unpack("nnN")
  138. method_name = s.read(method_name_len)
  139. seq_id = s.read(4).unpack("N").first
  140. [ method_name, seq_id, read_struct(s, rv_class).rv ]
  141. end
  142. end
  143. ## ----------------------------------------
  144. class Field
  145. attr_accessor :name, :type, :fid
  146. def initialize(name, type, fid)
  147. @name = name
  148. @type = type
  149. @fid = fid
  150. end
  151. def pack(value)
  152. value.nil? ? "" : [ type.to_i, fid, SimpleThrift.pack_value(type, value) ].pack("cna*")
  153. end
  154. end
  155. class ThriftException < RuntimeError
  156. def initialize(reason)
  157. @reason = reason
  158. end
  159. def to_s
  160. "ThriftException(#{@reason.inspect})"
  161. end
  162. end
  163. module ThriftStruct
  164. module Include
  165. def _pack
  166. self.class._fields.map { |f| f.pack(self[f.name]) }.join + [ STOP ].pack("c")
  167. end
  168. end
  169. module Extend
  170. def _fields
  171. @fields
  172. end
  173. def _fields=(f)
  174. @fields = f
  175. end
  176. end
  177. end
  178. def self.make_struct(name, *fields)
  179. names = fields.map { |f| f.name.to_sym }
  180. klass = Struct.new("ST_#{name}", *names)
  181. klass.send(:include, ThriftStruct::Include)
  182. klass.send(:extend, ThriftStruct::Extend)
  183. klass._fields = fields
  184. klass
  185. end
  186. class ThriftService
  187. def initialize(sock)
  188. @sock = sock
  189. end
  190. def self._arg_structs
  191. @_arg_structs = {} if @_arg_structs.nil?
  192. @_arg_structs
  193. end
  194. def self.thrift_method(name, rtype, *args)
  195. arg_struct = SimpleThrift.make_struct("Args__#{self.name}__#{name}", *args)
  196. rv_struct = SimpleThrift.make_struct("Retval__#{self.name}__#{name}", SimpleThrift::Field.new(:rv, rtype, 0))
  197. _arg_structs[name.to_sym] = [ arg_struct, rv_struct ]
  198. arg_names = args.map { |a| a.name.to_s }.join(", ")
  199. class_eval "def #{name}(#{arg_names}); _proxy(:#{name}#{args.size > 0 ? ', ' : ''}#{arg_names}); end"
  200. end
  201. def _proxy(method_name, *args)
  202. cls = self.class.ancestors.find { |cls| cls.respond_to?(:_arg_structs) and cls._arg_structs[method_name.to_sym] }
  203. arg_class, rv_class = cls._arg_structs[method_name.to_sym]
  204. arg_struct = arg_class.new(*args)
  205. data = [ VERSION_1, CALL, method_name.to_s.size, method_name.to_s, 0, arg_struct._pack ].pack("nnNa*Na*")
  206. @sock.write(data)
  207. rv = SimpleThrift.read_response(@sock, rv_class)
  208. rv[2]
  209. end
  210. # convenience. robey is lazy.
  211. [[ :field, "Field.new" ], [ :struct, "StructType.new" ],
  212. [ :list, "ListType.new" ], [ :map, "MapType.new" ]].each do |new_name, old_name|
  213. class_eval "def self.#{new_name}(*args); SimpleThrift::#{old_name}(*args); end"
  214. end
  215. [ :void, :bool, :byte, :double, :i16, :i32, :i64, :string ].each { |sym| class_eval "def self.#{sym}; SimpleThrift::#{sym.to_s.upcase}; end" }
  216. end
  217. end