Class: QRubyDriver::QIO
- Inherits:
-
StringIO
- Object
- StringIO
- QRubyDriver::QIO
- Defined in:
- lib/q-ruby-driver/q_io.rb
Overview
Single-pass Ruby reader/writer for byte-streams in Q IPC protocol format
Constant Summary collapse
- Q_ATOM_TYPES =
Q type constants
-19..-1
- Q_VECTOR_TYPES =
1..19
- Q_TYPE_EXCEPTION =
-128
- Q_TYPE_BOOLEAN =
-1
- Q_TYPE_BYTE =
-4
- Q_TYPE_SHORT =
-5
- Q_TYPE_INT =
-6
- Q_TYPE_LONG =
-7
- Q_TYPE_REAL =
single-prec float
-8 # single-prec float
- Q_TYPE_FLOAT =
double-prec float
-9 # double-prec float
- Q_TYPE_CHAR =
-10
- Q_TYPE_SYMBOL =
-11
- Q_TYPE_TIMESTAMP =
-12
- Q_TYPE_MONTH =
-13
- Q_TYPE_DATE =
-14
- Q_TYPE_DATETIME =
-15
- Q_TYPE_TIMESPAN =
-16
- Q_TYPE_MINUTE =
-17
- Q_TYPE_SECOND =
-18
- Q_TYPE_TIME =
-19
- Q_TYPE_CHAR_VECTOR =
10
- Q_TYPE_SYMBOL_VECTOR =
11
- Q_TYPE_LIST =
0
- Q_TYPE_FLIP =
98
- Q_TYPE_DICTIONARY =
99
Instance Method Summary collapse
-
#atom_to_ruby(value, type) ⇒ Object
Extracts atom types into Ruby types.
-
#boolean_to_ruby(value) ⇒ Object
Atom type handlers.
-
#get_atom_pack(type) ⇒ Object
Returns pack type and byte-length of q atom type.
-
#get_q_array_type(array) ⇒ Object
Helper method to write a Ruby array into either a list or a vector, depending on whether or not the array contains mixed types.
-
#get_q_type(item) ⇒ Object
Helper method to infer Q type from native Ruby types.
-
#read_atom(type) ⇒ Object
Extracts atom types into Ruby types.
-
#read_boolean ⇒ Object
Short cut methods for reading atoms.
- #read_byte ⇒ Object
- #read_char ⇒ Object
- #read_date ⇒ Object
- #read_datetime ⇒ Object
-
#read_dictionary ⇒ Object
Reads a dictionary into a Ruby Hash.
-
#read_flip ⇒ Object
Decodes a flip table into a Ruby Hash.
- #read_float ⇒ Object
- #read_int ⇒ Object
-
#read_item(type = nil) ⇒ Object
Reads the next item and extracts it into a Ruby type Will extract vectors, dictionaries, lists, etc.
-
#read_list ⇒ Object
Decodes a list into an array.
- #read_long ⇒ Object
-
#read_message ⇒ Object
Decodes a binary Q message into Ruby types.
-
#read_message_header ⇒ Object
Extracts length and message type from the message header.
- #read_minute ⇒ Object
- #read_month ⇒ Object
- #read_real ⇒ Object
- #read_second ⇒ Object
- #read_short ⇒ Object
- #read_symbol ⇒ Object
- #read_time ⇒ Object
- #read_timespan ⇒ Object
- #read_timestamp ⇒ Object
-
#read_vector(type) ⇒ Object
Reads a vector into a Ruby Array.
- #ruby_to_atom(value, type) ⇒ Object
- #ruby_to_boolean(value) ⇒ Object
- #ruby_to_exception(value) ⇒ Object
- #ruby_to_symbol(value) ⇒ Object
-
#write_atom(value, type) ⇒ Object
Encodes atom types.
-
#write_boolean(value) ⇒ Object
Atom type write shortcut methods.
- #write_byte(value) ⇒ Object
- #write_char(value) ⇒ Object
- #write_date(value) ⇒ Object
- #write_datetime(value) ⇒ Object
-
#write_dictionary(hash) ⇒ Object
Encodes a dictionary.
- #write_exception(value) ⇒ Object
-
#write_flip(hash) ⇒ Object
Encodes a flip table.
- #write_float(value) ⇒ Object
- #write_int(value) ⇒ Object
-
#write_item(item, type = nil) ⇒ Object
Encodes a type into the IPC representation no native support for the following atom types: byte, short, real, char.
-
#write_list(array) ⇒ Object
Encodes a list.
- #write_long(value) ⇒ Object
-
#write_message(message, msg_type = :async) ⇒ Object
Writes a Ruby object to a Q message.
- #write_minute(value) ⇒ Object
- #write_month(value) ⇒ Object
- #write_real(value) ⇒ Object
- #write_second(value) ⇒ Object
- #write_short(value) ⇒ Object
-
#write_string(item) ⇒ Object
Encodes a string as a char vector.
- #write_symbol(value) ⇒ Object
- #write_time(value) ⇒ Object
- #write_timespan(value) ⇒ Object
- #write_timestamp(value) ⇒ Object
-
#write_type(type) ⇒ Object
Writes the type byte.
-
#write_vector(array, type = nil) ⇒ Object
Encodes an array as a vector.
Instance Method Details
#atom_to_ruby(value, type) ⇒ Object
Extracts atom types into Ruby types
148 149 150 151 152 153 154 155 156 |
# File 'lib/q-ruby-driver/q_io.rb', line 148 def atom_to_ruby(value, type) case type when Q_TYPE_BOOLEAN then boolean_to_ruby value # TODO: add support for date/time types else value end end |
#boolean_to_ruby(value) ⇒ Object
Atom type handlers
159 160 161 |
# File 'lib/q-ruby-driver/q_io.rb', line 159 def boolean_to_ruby value value==1 end |
#get_atom_pack(type) ⇒ Object
Returns pack type and byte-length of q atom type
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/q-ruby-driver/q_io.rb', line 369 def get_atom_pack(type) case type when Q_TYPE_BOOLEAN, Q_TYPE_BYTE then ['c',1] when Q_TYPE_SHORT then ['s',2] when Q_TYPE_INT, Q_TYPE_MONTH, Q_TYPE_DATE, Q_TYPE_MINUTE, Q_TYPE_SECOND, Q_TYPE_TIME then ['I',4] when Q_TYPE_LONG, Q_TYPE_TIMESTAMP, Q_TYPE_TIMESPAN then ['q',8] when Q_TYPE_REAL then ['F',4] when Q_TYPE_FLOAT, Q_TYPE_DATETIME then ['D',8] when Q_TYPE_CHAR then ['Z',1] when Q_TYPE_SYMBOL, Q_TYPE_EXCEPTION then ['Z*',0] else raise QIOException.new "Unknown atom type #{type}" end end |
#get_q_array_type(array) ⇒ Object
Helper method to write a Ruby array into either a list or a vector, depending on whether or not the array contains mixed types
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/q-ruby-driver/q_io.rb', line 273 def get_q_array_type(array) raise QIOException.new("Cannot write empty array") if array.empty? klass = array[0].class return 0 if klass==String # String is a vector type; cannot make a vector of vectors if klass==TrueClass || klass==FalseClass # special routine for booleans array.each do |item| return 0 unless item.is_a?(TrueClass) || item.is_a?(FalseClass) end else array.each do |item| return 0 unless item.is_a? klass end end -1 * get_q_type(array[0]) end |
#get_q_type(item) ⇒ Object
Helper method to infer Q type from native Ruby types
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/q-ruby-driver/q_io.rb', line 240 def get_q_type(item) if item.is_a? Exception Q_TYPE_EXCEPTION elsif item.is_a?(TrueClass) || item.is_a?(FalseClass) Q_TYPE_BOOLEAN elsif item.is_a? Bignum Q_TYPE_LONG elsif item.is_a? Fixnum Q_TYPE_INT elsif item.is_a? Float Q_TYPE_FLOAT elsif item.is_a? String Q_TYPE_CHAR_VECTOR elsif item.is_a? Symbol Q_TYPE_SYMBOL # not yet supported # elsif item.is_a? Date # Q_TYPE_DATE # elsif item.is_a? DateTime # Q_TYPE_DATETIME # elsif item.is_a? Time # Q_TYPE_TIME elsif item.is_a? Array get_q_array_type(item) elsif item.is_a? Hash Q_TYPE_FLIP else raise QIOException.new("Cannot infer Q type from #{item.class.to_s}") end end |
#read_atom(type) ⇒ Object
Extracts atom types into Ruby types
138 139 140 141 142 143 144 145 |
# File 'lib/q-ruby-driver/q_io.rb', line 138 def read_atom(type) raise QIOException.new "Cannot read atom type #{type}" unless (type>=-19 and type<0) byte_type, num_bytes = get_atom_pack(type) raw = (type==Q_TYPE_SYMBOL) ? self.readline("\x00") : self.read(num_bytes) value = raw.unpack(byte_type)[0] atom_to_ruby(value, type) end |
#read_boolean ⇒ Object
Short cut methods for reading atoms
164 165 166 |
# File 'lib/q-ruby-driver/q_io.rb', line 164 def read_boolean read_atom(Q_TYPE_BOOLEAN) end |
#read_byte ⇒ Object
167 168 169 |
# File 'lib/q-ruby-driver/q_io.rb', line 167 def read_byte read_atom(Q_TYPE_BYTE) end |
#read_char ⇒ Object
185 186 187 |
# File 'lib/q-ruby-driver/q_io.rb', line 185 def read_char read_atom(Q_TYPE_CHAR) end |
#read_date ⇒ Object
197 198 199 |
# File 'lib/q-ruby-driver/q_io.rb', line 197 def read_date read_atom(Q_TYPE_DATE) end |
#read_datetime ⇒ Object
200 201 202 |
# File 'lib/q-ruby-driver/q_io.rb', line 200 def read_datetime read_atom(Q_TYPE_DATETIME) end |
#read_dictionary ⇒ Object
Reads a dictionary into a Ruby Hash
111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/q-ruby-driver/q_io.rb', line 111 def read_dictionary # The first item is a vector containing the dictionary keys keys = read_item keys = [keys] unless keys.is_a? Array # The second item is a list containing the values of each key values = read_item values = [values] unless values.is_a? Array hash = {} keys.zip(values) { |k,v| hash[k]=v } return hash end |
#read_flip ⇒ Object
Decodes a flip table into a Ruby Hash
126 127 128 129 |
# File 'lib/q-ruby-driver/q_io.rb', line 126 def read_flip self.read(1) read_item # should be a dictionary end |
#read_float ⇒ Object
182 183 184 |
# File 'lib/q-ruby-driver/q_io.rb', line 182 def read_float read_atom(Q_TYPE_FLOAT) end |
#read_int ⇒ Object
173 174 175 |
# File 'lib/q-ruby-driver/q_io.rb', line 173 def read_int read_atom(Q_TYPE_INT) end |
#read_item(type = nil) ⇒ Object
Reads the next item and extracts it into a Ruby type Will extract vectors, dictionaries, lists, etc. recursively
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/q-ruby-driver/q_io.rb', line 59 def read_item(type = nil) type = read_byte() if type.nil? case type when Q_TYPE_EXCEPTION then raise QException.new(read_symbol) when Q_ATOM_TYPES then return read_atom(type) when Q_TYPE_LIST then return read_list when Q_VECTOR_TYPES then return read_vector(type) when Q_TYPE_FLIP then return read_flip when Q_TYPE_DICTIONARY then return read_dictionary when 100 then read_symbol return read_item when 101..103 then return read_byte == 0 && type == 101 ? nil : "func"; when 104 then read_int.times { read_item } return "func" when 105..255 then read_item return "func" else raise "Cannot read unknown type #{type}" end end |
#read_list ⇒ Object
Decodes a list into an array
132 133 134 135 |
# File 'lib/q-ruby-driver/q_io.rb', line 132 def read_list length = self.read(5).unpack("c1I")[1] length.times.map { read_item } end |
#read_long ⇒ Object
176 177 178 |
# File 'lib/q-ruby-driver/q_io.rb', line 176 def read_long read_atom(Q_TYPE_LONG) end |
#read_message ⇒ Object
Decodes a binary Q message into Ruby types
37 38 39 40 |
# File 'lib/q-ruby-driver/q_io.rb', line 37 def () self.read(8) # skip message header return read_item end |
#read_message_header ⇒ Object
Extracts length and message type from the message header
43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/q-ruby-driver/q_io.rb', line 43 def header = self.read(8).unpack("H2H2H4I") length = header[3] case header[1] when "00" then msg_type = :async when "01" then msg_type = :sync when "02" then msg_type = :response end return length, msg_type end |
#read_minute ⇒ Object
206 207 208 |
# File 'lib/q-ruby-driver/q_io.rb', line 206 def read_minute read_atom(Q_TYPE_MINUTE) end |
#read_month ⇒ Object
194 195 196 |
# File 'lib/q-ruby-driver/q_io.rb', line 194 def read_month read_atom(Q_TYPE_MONTH) end |
#read_real ⇒ Object
179 180 181 |
# File 'lib/q-ruby-driver/q_io.rb', line 179 def read_real read_atom(Q_TYPE_REAL) end |
#read_second ⇒ Object
209 210 211 |
# File 'lib/q-ruby-driver/q_io.rb', line 209 def read_second read_atom(Q_TYPE_SECOND) end |
#read_short ⇒ Object
170 171 172 |
# File 'lib/q-ruby-driver/q_io.rb', line 170 def read_short read_atom(Q_TYPE_SHORT) end |
#read_symbol ⇒ Object
188 189 190 |
# File 'lib/q-ruby-driver/q_io.rb', line 188 def read_symbol read_atom(Q_TYPE_SYMBOL) end |
#read_time ⇒ Object
212 213 214 |
# File 'lib/q-ruby-driver/q_io.rb', line 212 def read_time read_atom(Q_TYPE_TIME) end |
#read_timespan ⇒ Object
203 204 205 |
# File 'lib/q-ruby-driver/q_io.rb', line 203 def read_timespan read_atom(Q_TYPE_TIMESPAN) end |
#read_timestamp ⇒ Object
191 192 193 |
# File 'lib/q-ruby-driver/q_io.rb', line 191 def read_atom(Q_TYPE_TIMESTAMP) end |
#read_vector(type) ⇒ Object
Reads a vector into a Ruby Array
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/q-ruby-driver/q_io.rb', line 93 def read_vector(type) length = self.read(5).unpack("c1I")[1] byte_type, num_bytes = get_atom_pack(-type) if type==Q_TYPE_SYMBOL_VECTOR raw = length.times.map{ self.readline("\x00") }.inject{|str, n| str + n} value = raw.unpack(byte_type*length) else raw = self.read(length*num_bytes) value = raw.unpack(byte_type+length.to_s) end # char vectors are returned as strings # all other types are returned as arrays (type == Q_TYPE_CHAR_VECTOR) ? value[0] : value.map{|i| atom_to_ruby(i, -type)} end |
#ruby_to_atom(value, type) ⇒ Object
392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/q-ruby-driver/q_io.rb', line 392 def ruby_to_atom(value, type) case type when Q_TYPE_BOOLEAN then ruby_to_boolean value when Q_TYPE_SYMBOL then ruby_to_symbol value when Q_TYPE_EXCEPTION then ruby_to_exception value else value end end |
#ruby_to_boolean(value) ⇒ Object
405 406 407 408 409 410 411 412 413 |
# File 'lib/q-ruby-driver/q_io.rb', line 405 def ruby_to_boolean(value) if value.is_a? TrueClass 1 elsif value.is_a? FalseClass 0 else value == true end end |
#ruby_to_exception(value) ⇒ Object
417 418 419 420 421 422 423 |
# File 'lib/q-ruby-driver/q_io.rb', line 417 def ruby_to_exception(value) if value.is_a? Exception value. else value.to_s end end |
#ruby_to_symbol(value) ⇒ Object
414 415 416 |
# File 'lib/q-ruby-driver/q_io.rb', line 414 def ruby_to_symbol(value) value.to_s end |
#write_atom(value, type) ⇒ Object
Encodes atom types
363 364 365 366 |
# File 'lib/q-ruby-driver/q_io.rb', line 363 def write_atom(value, type) raise QIOException.new "Cannot write atom type #{type}" unless ((type>=-19 and type<0) || type==Q_TYPE_EXCEPTION) self.write [ruby_to_atom(value, type)].pack(get_atom_pack(type)[0]) end |
#write_boolean(value) ⇒ Object
Atom type write shortcut methods
426 427 428 |
# File 'lib/q-ruby-driver/q_io.rb', line 426 def write_boolean(value) write_atom(value, Q_TYPE_BOOLEAN) end |
#write_byte(value) ⇒ Object
429 430 431 |
# File 'lib/q-ruby-driver/q_io.rb', line 429 def write_byte(value) write_atom(value, Q_TYPE_BYTE) end |
#write_char(value) ⇒ Object
447 448 449 |
# File 'lib/q-ruby-driver/q_io.rb', line 447 def write_char(value) write_atom(value, Q_TYPE_CHAR) end |
#write_date(value) ⇒ Object
462 463 464 |
# File 'lib/q-ruby-driver/q_io.rb', line 462 def write_date(value) write_atom(value, Q_TYPE_DATE) end |
#write_datetime(value) ⇒ Object
465 466 467 |
# File 'lib/q-ruby-driver/q_io.rb', line 465 def write_datetime(value) write_atom(value, Q_TYPE_DATETIME) end |
#write_dictionary(hash) ⇒ Object
Encodes a dictionary
349 350 351 352 353 354 |
# File 'lib/q-ruby-driver/q_io.rb', line 349 def write_dictionary(hash) write_type Q_TYPE_SYMBOL_VECTOR write_vector hash.keys, Q_TYPE_SYMBOL_VECTOR write_type Q_TYPE_LIST write_list hash.values end |
#write_exception(value) ⇒ Object
453 454 455 |
# File 'lib/q-ruby-driver/q_io.rb', line 453 def write_exception(value) write_atom(value, Q_TYPE_EXCEPTION) end |
#write_flip(hash) ⇒ Object
Encodes a flip table
357 358 359 360 |
# File 'lib/q-ruby-driver/q_io.rb', line 357 def write_flip(hash) self.write ["00"].pack("H1") write_item(hash, 99) # dictionary end |
#write_float(value) ⇒ Object
444 445 446 |
# File 'lib/q-ruby-driver/q_io.rb', line 444 def write_float(value) write_atom(value, Q_TYPE_FLOAT) end |
#write_int(value) ⇒ Object
435 436 437 |
# File 'lib/q-ruby-driver/q_io.rb', line 435 def write_int(value) write_atom(value, Q_TYPE_INT) end |
#write_item(item, type = nil) ⇒ Object
Encodes a type into the IPC representation no native support for the following atom types: byte, short, real, char
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/q-ruby-driver/q_io.rb', line 294 def write_item(item, type=nil) type=get_q_type(item) if type.nil? write_type type case type when Q_TYPE_EXCEPTION then write_exception item when Q_ATOM_TYPES then write_atom item, type when Q_TYPE_LIST then write_list item when Q_TYPE_CHAR_VECTOR then write_string item when 1..9, 11..19 then # Q_VECTOR_TYPES minus Q_TYPE_CHAR_VECTOR write_vector item, type when Q_TYPE_FLIP then write_flip item when Q_TYPE_DICTIONARY then write_dictionary item else raise QIOException.new "Cannot write type #{type}" end end |
#write_list(array) ⇒ Object
Encodes a list
342 343 344 345 346 |
# File 'lib/q-ruby-driver/q_io.rb', line 342 def write_list(array) raise QIOException("Cannot write empty list") if array.empty? self.write ["00", array.length].pack("H1I1") array.each { |item| write_item(item) } end |
#write_long(value) ⇒ Object
438 439 440 |
# File 'lib/q-ruby-driver/q_io.rb', line 438 def write_long(value) write_atom(value, Q_TYPE_LONG) end |
#write_message(message, msg_type = :async) ⇒ Object
Writes a Ruby object to a Q message
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/q-ruby-driver/q_io.rb', line 219 def (, msg_type=:async) offset = self.pos self.write ["01"].pack("H*") self.write case msg_type when :async then ["00"].pack("H*") when :sync then ["01"].pack("H*") when :response then ["02"].pack("H*") else raise QIOException.new("Cannot write unknown message type #{msg_type.to_s}") end self.write ["0000"].pack("H*") self.pos += 4 # will write size here write_item() # write size size = self.pos - offset self.pos = offset+4 write_int(size) # set position to end of buffer self.pos = offset + size end |
#write_minute(value) ⇒ Object
471 472 473 |
# File 'lib/q-ruby-driver/q_io.rb', line 471 def write_minute(value) write_atom(value, Q_TYPE_MINUTE) end |
#write_month(value) ⇒ Object
459 460 461 |
# File 'lib/q-ruby-driver/q_io.rb', line 459 def write_month(value) write_atom(value, Q_TYPE_MONTH) end |
#write_real(value) ⇒ Object
441 442 443 |
# File 'lib/q-ruby-driver/q_io.rb', line 441 def write_real(value) write_atom(value, Q_TYPE_REAL) end |
#write_second(value) ⇒ Object
474 475 476 |
# File 'lib/q-ruby-driver/q_io.rb', line 474 def write_second(value) write_atom(value, Q_TYPE_SECOND) end |
#write_short(value) ⇒ Object
432 433 434 |
# File 'lib/q-ruby-driver/q_io.rb', line 432 def write_short(value) write_atom(value, Q_TYPE_SHORT) end |
#write_string(item) ⇒ Object
Encodes a string as a char vector
335 336 337 338 339 |
# File 'lib/q-ruby-driver/q_io.rb', line 335 def write_string(item) value = item.is_a?(String) ? item.scan(/./) : item # convert string into a char array self.write ["00", value.length].pack("H1I") self.write value.pack("A"*value.length) end |
#write_symbol(value) ⇒ Object
450 451 452 |
# File 'lib/q-ruby-driver/q_io.rb', line 450 def write_symbol(value) write_atom(value, Q_TYPE_SYMBOL) end |
#write_time(value) ⇒ Object
477 478 479 |
# File 'lib/q-ruby-driver/q_io.rb', line 477 def write_time(value) write_atom(value, Q_TYPE_TIME) end |
#write_timespan(value) ⇒ Object
468 469 470 |
# File 'lib/q-ruby-driver/q_io.rb', line 468 def write_timespan(value) write_atom(value, Q_TYPE_TIMESPAN) end |
#write_timestamp(value) ⇒ Object
456 457 458 |
# File 'lib/q-ruby-driver/q_io.rb', line 456 def (value) write_atom(value, Q_TYPE_TIMESTAMP) end |
#write_type(type) ⇒ Object
Writes the type byte
318 319 320 |
# File 'lib/q-ruby-driver/q_io.rb', line 318 def write_type(type) self.write [type].pack("c1") end |
#write_vector(array, type = nil) ⇒ Object
Encodes an array as a vector
323 324 325 326 327 328 329 330 331 332 |
# File 'lib/q-ruby-driver/q_io.rb', line 323 def write_vector(array, type=nil) raise QIOException("Cannot write empty vector") if array.empty? type = -1 * get_q_type(array[0]) if type.nil? self.write ["00", array.length].pack("H1I") if type==Q_TYPE_SYMBOL_VECTOR array.each{|x| self.write [ruby_to_atom(x, -type)].pack( get_atom_pack(-type)[0] ) } else self.write array.map{|x| ruby_to_atom(x, -type)}.pack( get_atom_pack(-type)[0] + array.length.to_s ) end end |