class IcAgent::Candid
Constants
- ALL_TYPES
- MULTI_TYPES
- PREFIX
- RESULT_TYPE
- SINGLE_TYPES
Public Class Methods
build_type(raw_table, table, entry)
click to toggle source
# File lib/ic_agent/candid.rb, line 1417 def self.build_type(raw_table, table, entry) ty = entry[0] if ty == TypeIds::Vec if ty >= raw_table.length raise ValueError, 'type index out of range' end t = get_type(raw_table, table, entry[1]) if t.nil? t = table[t] end return BaseTypes::vec(t) elsif ty == TypeIds::Opt if ty >= raw_table.length raise ValueError, 'type index out of range' end t = get_type(raw_table, table, entry[1]) if t.nil? t = table[t] end return BaseTypes::opt(t) elsif ty == TypeIds::Record fields = {} entry[1].each do |hash, t| name = "_#{hash.to_s}_" if t >= raw_table.length raise ValueError, 'type index out of range' end temp = get_type(raw_table, table, t) fields[name] = temp end record = BaseTypes::record(fields) tup = record.try_as_tuple() if tup.is_a?(Array) return BaseTypes::tuple(*tup) else return record end elsif ty == TypeIds::Variant fields = {} entry[1].each do |hash, t| name = "_#{hash.to_s}_" if t >= raw_table.length raise ValueError, 'type index out of range' end temp = get_type(raw_table, table, t) fields[name] = temp end return BaseTypes::variant(fields) elsif ty == TypeIds::Func return BaseTypes::func([], [], []) elsif ty == TypeIds::Service return BaseTypes::service({}) else raise ValueError, "Illegal op_code: #{ty}" end end
decode(data, ret_types = nil)
click to toggle source
@param [Object] data: decode a bytes value @param [nil] ret_types def decode(retTypes, data):
# File lib/ic_agent/candid.rb, line 1644 def self.decode(data, ret_types = nil) pipe = Pipe.new(data) if data.length < PREFIX.length raise ValueError, 'Message length smaller than prefix number' end prefix_buffer = safe_read(pipe, PREFIX.length).hex2str if prefix_buffer != PREFIX raise ValueError, "Wrong prefix:#{prefix_buffer}expected prefix: DIDL" end raw_table, raw_types = read_type_table(pipe) if ret_types if ret_types.class != Array ret_types = [ret_types] end if raw_types.length < ret_types.length raise ValueError, 'Wrong number of return value' end end table = [] raw_table.length.times do table.append(BaseTypes.rec) end raw_table.each_with_index do |entry, i| t = build_type(raw_table, table, entry) table[i].fill(t) end types = [] raw_types.each do |t| types.append(get_type(raw_table, table, t)) end outputs = [] types.each_with_index do |t, i| outputs.append({ 'type' => t.name, 'value' => t.decode_value(pipe, types[i]) }) end outputs end
encode(params)
click to toggle source
@param [Object] params = [{type, value}] @return data = b'DIDL' + len(params) + encoded types + encoded values
# File lib/ic_agent/candid.rb, line 1601 def self.encode(params) arg_types = [] args = [] params.each do |p| arg_types << p[:type] args << p[:value] end if arg_types.length != args.length raise ValueError, 'Wrong number of message arguments' end typetable = TypeTable.new arg_types.each do |item| item.build_type_table(typetable) end pre = unicode_to_hex(PREFIX) table = unicode_to_hex(typetable.encode()) length = leb128_string_hex(args.length) typs = '' arg_types.each do |t| typs += unicode_to_hex(t.encode_type(typetable)) end vals = '' args.each_with_index do |arg, i| t = arg_types[i] unless t.covariant(args[i]) raise TypeError, "Invalid #{t.display} argument: #{args[i]}" end vals += unicode_to_hex(t.encode_value(args[i])) end return pre + table + length + typs + vals end
get_type(raw_table, table, t)
click to toggle source
# File lib/ic_agent/candid.rb, line 1544 def self.get_type(raw_table, table, t) if t < -24 raise ValueError, 'not supported type' end if t < 0 case t when -1 return BaseTypes.null when -2 return BaseTypes.bool when -3 return BaseTypes.nat when -4 return BaseTypes.int when -5 return BaseTypes.nat8 when -6 return BaseTypes.nat16 when -7 return BaseTypes.nat32 when -8 return BaseTypes.nat64 when -9 return BaseTypes.int8 when -10 return BaseTypes.int16 when -11 return BaseTypes.int32 when -12 return BaseTypes.int64 when -13 return BaseTypes.float32 when -14 return BaseTypes.float64 when -15 return BaseTypes.text when -16 return BaseTypes.reserved when -17 return BaseTypes.empty when -24 return BaseTypes.principal else raise ValueError, "Illegal op_code: #{t}" end end if t >= raw_table.length raise ValueError, 'type index out of range' end return table[t] end
leb128_string_hex(p_str)
click to toggle source
# File lib/ic_agent/candid.rb, line 1409 def self.leb128_string_hex(p_str) LEB128.encode_signed(p_str).string.to_hex end
leb128i_decode(pipe)
click to toggle source
# File lib/ic_agent/candid.rb, line 1379 def self.leb128i_decode(pipe) length = pipe.buffer.length count = 0 (0...length).each do |i| count = i if pipe.buffer[i] < '80' # 0x80 if pipe.buffer[i] < '40' # 0x40 return leb128u_decode(pipe) end break end end res = StringIO.new res.putc(safe_read(pipe, count + 1).hex) LEB128.decode_signed(res) end
leb128u_decode(pipe)
click to toggle source
through Pipe
to decode bytes
# File lib/ic_agent/candid.rb, line 1368 def self.leb128u_decode(pipe) res = StringIO.new loop do byte = safe_read_byte(pipe) res.putc(byte.hex) break if byte < '80' || pipe.length.zero? end LEB128.decode_signed(res) end
read_type_table(pipe)
click to toggle source
# File lib/ic_agent/candid.rb, line 1478 def self.read_type_table(pipe) type_table = [] type_table_len = leb128u_decode(pipe).to_i type_table_len.times do ty = leb128i_decode(pipe) if [TypeIds::Opt, TypeIds::Vec].include?(ty) t = leb128i_decode(pipe) type_table << [ty, t] elsif [TypeIds::Record, TypeIds::Variant].include?(ty) fields = [] obj_length = leb128u_decode(pipe) prev_hash = -1 obj_length.times do hash = leb128u_decode(pipe) if hash >= 2**32 raise ValueError, 'field id out of 32-bit range' end if prev_hash.is_a?(Integer) && prev_hash >= hash raise ValueError, 'field id collision or not sorted' end prev_hash = hash t = leb128i_decode(pipe) fields << [hash, t] end type_table << [ty, fields] elsif ty == TypeIds::Func 2.times do fun_len = leb128u_decode(pipe) fun_len.times { leb128i_decode(pipe) } end ann_len = leb128u_decode(pipe) safe_read(pipe, ann_len) type_table << [ty, nil] elsif ty == TypeIds::Service serv_len = leb128u_decode(pipe) serv_len.times do l = leb128u_decode(pipe) safe_read(pipe, l) leb128i_decode(pipe) end type_table << [ty, nil] else raise ValueError, "Illegal op_code: #{ty}" end end raw_list = [] types_len = leb128u_decode(pipe).to_i types_len.times do raw_list << leb128i_decode(pipe) end [type_table, raw_list] end
safe_read(pipe, num)
click to toggle source
# File lib/ic_agent/candid.rb, line 1397 def self.safe_read(pipe, num) raise ArgumentError, 'unexpected end of buffer' if pipe.length < num pipe.read(num) end
safe_read_byte(pipe)
click to toggle source
# File lib/ic_agent/candid.rb, line 1403 def self.safe_read_byte(pipe) raise ArgumentError, 'unexpected end of buffer' if pipe.length < 1 pipe.readbyte end
unicode_to_hex(u_code)
click to toggle source
# File lib/ic_agent/candid.rb, line 1413 def self.unicode_to_hex(u_code) u_code.to_hex end