class Ronin::Support::Binary::CTypes::UnionType

Represents a ‘union` type.

@api private

@since 1.0.0

Attributes

alignment[R]

The alignment, in bytes, for the union type, so that all members within the union type are themselves aligned.

@return [Integer]

members[R]

The members of the union type.

@return [Hash{Symbol => Member}]

size[R]

The size of the union type.

@return [Integer, Float::INFINITY]

Public Class Methods

build(fields, alignment: nil) click to toggle source

Builds the union type from the given fields.

@param [Hash{Symbol => Type}] fields

The field names and types for the union type.

@param [Integer, nil] alignment

Custom alignment to use instead of the union's alignment.

@return [UnionType]

The new union type.
# File lib/ronin/support/binary/ctypes/union_type.rb, line 138
def self.build(fields, alignment: nil)
  members       = {}
  max_size      = 0
  max_alignment = 0

  fields.each do |name,type|
    members[name] = Member.new(type)

    # omit infinite sizes from the union size
    if (type.size > max_size) && (type.size != Float::INFINITY)
      max_size = type.size
    end

    max_alignment = type.alignment if type.alignment > max_alignment
  end

  return new(members, size:      max_size,
                      alignment: alignment || max_alignment)
end
new(members, size: , alignment: ) click to toggle source

Initializes the union type.

@param [Hash{Symbol => Member}] members

The members for the union type.

@param [Integer] size

The size of the union type.

@param [Integer] alignment

The alignment of the union type.
Calls superclass method
# File lib/ronin/support/binary/ctypes/union_type.rb, line 118
def initialize(members, size: , alignment: )
  @members   = members
  @size      = size
  @alignment = alignment

  super(pack_string: nil)
end

Public Instance Methods

align(new_alignment) click to toggle source

Creates a copy of the union type with a different {#alignment}.

@param [Integer] new_alignment

The new alignment for the new union type.

@return [ScalarType]

The new union type.
# File lib/ronin/support/binary/ctypes/union_type.rb, line 188
def align(new_alignment)
  self.class.new(@members, size:        @size,
                           alignment:   new_alignment)
end
length() click to toggle source

The number of members within the union.

@return [Integer]

# File lib/ronin/support/binary/ctypes/union_type.rb, line 175
def length
  @members.length
end
pack(hash) click to toggle source

Packs a hash of values into the member’s binary layout.

@param [Hash{Symbol => Integer, Float, String}] hash

The hash of values to pack.

@return [String]

The packed binary data.
# File lib/ronin/support/binary/ctypes/union_type.rb, line 202
def pack(hash)
  unknown_keys = hash.keys - @members.keys

  unless unknown_keys.empty?
    raise(ArgumentError,"unknown union members: #{unknown_keys.map(&:inspect).join(', ')}")
  end

  buffer = if @size == Float::INFINITY
             String.new(encoding: Encoding::ASCII_8BIT)
           else
             String.new("\0" * @size, encoding: Encoding::ASCII_8BIT)
           end

  hash.each do |name,value|
    member = @members.fetch(name)
    data   = member.type.pack(value)

    if data.bytesize <= @size
      # if the packed data fits into buffer, overlay it
      buffer[0,data.bytesize] = data
    else
      # otherwise replace the buffer with the larger data
      buffer = data
    end
  end

  return buffer
end
uninitialized_value() click to toggle source

Creates a new Hash of the union’s uninitialized members.

@return [Hash]

The uninitialized values for the new union's members.
# File lib/ronin/support/binary/ctypes/union_type.rb, line 164
def uninitialized_value
  @members.transform_values do |member|
    member.type.uninitialized_value
  end
end
unpack(data) click to toggle source

Unpacks binary data into a Hash of values using the union’s binary layout.

@param [String] data

The binary data to unpack.

@return [Hash{Symbol => Integer, Float, String, nil}]

The unpacked hash.
# File lib/ronin/support/binary/ctypes/union_type.rb, line 241
def unpack(data)
  hash = {}

  @members.each do |name,member|
    slice = if member.size == Float::INFINITY
              data
            else
              data.byteslice(0,member.size)
            end

    hash[name] = member.type.unpack(slice)
  end

  return hash
end