class DBus::PacketMarshaller
D-Bus packet marshaller class
Class
that handles the conversion (marshalling) of Ruby objects to (binary) payload data.
Attributes
@return [:little,:big]
The current or result packet. FIXME: allow access only when marshalling is finished @return [String]
Public Class Methods
Source
# File lib/dbus/marshall.rb, line 346 def self.make_variant(value) # TODO: mix in _make_variant to String, Integer... if value == true ["b", true] elsif value == false ["b", false] elsif value.nil? ["b", nil] elsif value.is_a? Float ["d", value] elsif value.is_a? Symbol ["s", value.to_s] elsif value.is_a? Array ["av", value.map { |i| make_variant(i) }] elsif value.is_a? Hash h = {} value.each_key { |k| h[k] = make_variant(value[k]) } key_type = if value.empty? "s" else t, = make_variant(value.first.first) t end ["a{#{key_type}v}", h] elsif value.respond_to? :to_str ["s", value.to_str] elsif value.respond_to? :to_int i = value.to_int if Data::Int32.range.cover?(i) ["i", i] elsif Data::Int64.range.cover?(i) ["x", i] else ["t", i] end end end
Make a [signature, value] pair for a variant
Source
# File lib/dbus/marshall.rb, line 172 def initialize(offset = 0, endianness: HOST_ENDIANNESS) @endianness = endianness @packet = "" @offset = offset # for correct alignment of nested marshallers end
Create a new marshaller, setting the current packet to the empty packet.
Public Instance Methods
Source
# File lib/dbus/marshall.rb, line 190 def align(alignment) pad_count = num_align(@offset + @packet.bytesize, alignment) - @offset @packet = @packet.ljust(pad_count, 0.chr) end
Align the buffer with NULL (0) bytes on a byte length of alignment.
Source
# File lib/dbus/marshall.rb, line 224 def append(type, val) raise TypeException, "Cannot send nil" if val.nil? type = type.chr if type.is_a?(Integer) type = Type::Parser.new(type).parse[0] if type.is_a?(String) # type is [Type] now data_class = Data::BY_TYPE_CODE[type.sigtype] if data_class.nil? raise NotImplementedError, "sigtype: #{type.sigtype} (#{type.sigtype.chr})" end if data_class.fixed? align(data_class.alignment) data = data_class.new(val) @packet += data.marshall(endianness) elsif data_class.basic? val = val.value if val.is_a?(Data::Basic) align(data_class.size_class.alignment) size_data = data_class.size_class.new(val.bytesize) @packet += size_data.marshall(endianness) # Z* makes a binary string, as opposed to interpolation @packet += [val].pack("Z*") else case type.sigtype when Type::VARIANT append_variant(val) when Type::ARRAY val = val.exact_value if val.is_a?(Data::Array) append_array(type.child, val) when Type::STRUCT, Type::DICT_ENTRY val = val.exact_value if val.is_a?(Data::Struct) || val.is_a?(Data::DictEntry) unless val.is_a?(Array) || val.is_a?(Struct) type_name = Type::TYPE_MAPPING[type.sigtype].first raise TypeException, "#{type_name} expects an Array or Struct, seen #{val.class}" end if type.sigtype == Type::DICT_ENTRY && val.size != 2 raise TypeException, "DICT_ENTRY expects a pair" end if type.members.size != val.size type_name = Type::TYPE_MAPPING[type.sigtype].first raise TypeException, "#{type_name} has #{val.size} elements but type info for #{type.members.size}" end struct do type.members.zip(val).each do |t, v| append(t, v) end end else raise NotImplementedError, "sigtype: #{type.sigtype} (#{type.sigtype.chr})" end end end
Append a value val to the packet based on its type.
Host native endianness is used, declared in Message#marshall
@param type [SingleCompleteType] (or Integer or {Type}) @param val [::Object]
Source
# File lib/dbus/marshall.rb, line 323 def append_array(child_type, val) if val.is_a?(Hash) raise TypeException, "Expected an Array but got a Hash" if child_type.sigtype != Type::DICT_ENTRY # Damn ruby rocks here val = val.to_a end # If string is received and ay is expected, explode the string if val.is_a?(String) && child_type.sigtype == Type::BYTE val = val.bytes end if !val.is_a?(Enumerable) raise TypeException, "Expected an Enumerable of #{child_type.inspect} but got a #{val.class}" end array(child_type) do val.each do |elem| append(child_type, elem) end end end
@param child_type [Type]
Source
# File lib/dbus/marshall.rb, line 283 def append_variant(val) vartype = nil if val.is_a?(DBus::Data::Variant) vartype = val.member_type vardata = val.exact_value elsif val.is_a?(DBus::Data::Container) vartype = val.type vardata = val.exact_value elsif val.is_a?(DBus::Data::Base) vartype = val.type vardata = val.value elsif val.is_a?(Array) && val.size == 2 case val[0] when Type vartype, vardata = val # Ambiguous but easy to use, because Type # cannot construct "as" "a{sv}" easily when String begin parsed = Type::Parser.new(val[0]).parse vartype = parsed[0] if parsed.size == 1 vardata = val[1] rescue Type::SignatureException # no assignment end end end if vartype.nil? vartype, vardata = PacketMarshaller.make_variant(val) vartype = Type::Parser.new(vartype).parse[0] end append(Data::Signature.type, vartype.to_s) align(vartype.alignment) sub = PacketMarshaller.new(@offset + @packet.bytesize, endianness: endianness) sub.append(vartype, vardata) @packet += sub.packet end
Source
# File lib/dbus/marshall.rb, line 197 def array(type) # Thanks to Peter Rullmann for this line align(4) sizeidx = @packet.bytesize @packet += "ABCD" align(type.alignment) contentidx = @packet.bytesize yield sz = @packet.bytesize - contentidx raise InvalidPacketException if sz > 67_108_864 sz_data = Data::UInt32.new(sz) @packet[sizeidx...sizeidx + 4] = sz_data.marshall(endianness) end
Append the array type type to the packet and allow for appending the child elements.
Source
# File lib/dbus/marshall.rb, line 179 def num_align(num, alignment) case alignment when 1, 2, 4, 8 bits = alignment - 1 (num + bits) & ~bits else raise ArgumentError, "Unsupported alignment #{alignment}" end end
Round num up to the specified power of two, alignment
Source
# File lib/dbus/marshall.rb, line 213 def struct align(8) yield end
Align and allow for appending struct fields.