# -*- coding: binary -*-
module Rex
module Poly

require 'rex/poly/register'
require 'rex/poly/block'
require 'rex/poly/machine'

###
#
# This class encapsulates the state of a single polymorphic block set
# generation.  It tracks the current set of consumed registers, the linear
# list of blocks generated, the end-result buffer, and the phase of
# generation.  The fields exposed by the State class are intended for use only
# by the polymorphic generation subsystem and should not be modified directly.
#
###
class State

  #
  # Initializes the polymorphic generation state.
  #
  def initialize
    @block_list = nil
    reset
  end

  #
  # Resets the generation state to have a plain start by clearing all
  # consumed registers, resetting the polymorphic buffer back to its
  # beginning and destroying any block generation state.
  #
  def reset
    # Reset the generation flag on any blocks in the block list
    @block_list.each { |block|
      block[0].generated = false
    } if (@block_list)

    @regnums     = Hash.new
    @buffer      = ''
    @block_list  = []
    @curr_offset = 0
    @first_phase = true
    @badchars    = nil
  end

  #
  # Returns true if the supplied register number is already consumed.
  #
  def consumed_regnum?(regnum)
    @regnums[regnum]
  end

  #
  # Consumes a register number, thus removing it from the pool that can be
  # assigned.  The consumed register number is returned to the caller.
  #
  def consume_regnum(regnum)
    raise RuntimeError, "Register #{regnum} is already consumed." if (consumed_regnum?(regnum))

    @regnums[regnum] = true

    regnum
  end

  #
  # Acquires a register number that has not already been consumed from the
  # supplied register number set and consumes it, returning the selected
  # register number to the caller.  The register number is selected from the
  # set at random.
  #
  def consume_regnum_from_set(regnum_set)
    # Pick a random starting point within the supplied set.
    idx = rand(regnum_set.length)

    # Try each index in the set.
    regnum_set.length.times { |x|
      regnum = regnum_set[(idx + x) % regnum_set.length]

      next if (consumed_regnum?(regnum))

      return consume_regnum(regnum)
    }

    # If we get through the entire iteration without finding a register,
    # then we are out of registers to assign.
    raise RuntimeError, "No registers are available to consume from the set"
  end

  #
  # Eliminates a register number from the consumed pool so that it can be
  # used in the future.  This happens after a block indicates that a register
  # has been clobbered.
  #
  def defecate_regnum(regnum)
    @regnums.delete(regnum)
  end

  #
  # The buffer state for the current polymorphic generation.  This stores the
  # end-result of a call to generate on a LogicalBlock.
  #
  attr_accessor :buffer

  #
  # The linear list of blocks that is generated by calling the generate
  # method on a LogicalBlock.
  #
  attr_accessor :block_list

  #
  # The current offset into the polymorphic buffer that is being generated.
  # This is updated as blocks are appended to the block_list.
  #
  attr_accessor :curr_offset

  #
  # A boolean field that is used by the LogicalBlock class to track whether
  # or not it is in the first phase (generating the block list), or in the
  # second phase (generating the polymorphic buffer).  This phases are used
  # to indicate whether or not the offset_of and regnum_of methods will
  # return actual results.
  #
  attr_accessor :first_phase

  #
  # Characters to avoid when selecting permutations, if any.
  #
  attr_accessor :badchars

end

end
end
