/*
* File:        StExecutableImage.h
*
* Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
* See included license file for license details.
*/
#if !defined(_StExecutableImage_h_)
#define _StExecutableImage_h_

#include "stdafx.h"
#include <list>

/*!
* \brief Used to build a representation of memory regions.
*
* An intermediate representation of the memory regions and segments loaded
* from an executable file. Also used to find contiguous segments that are
* specified separately in the source file.
*
* When regions are added, an attempt is made to coalesce contiguous regions.
* In order for this to succeed, the touching regions must be of the same
* type and have the same permissions. Regions are also kept sorted by their
* address range as they are added.
*
* \todo Implement alignment support.
*/
class StExecutableImage
{
public:
       //! Possible types of memory regions.
       typedef enum {
               TEXT_REGION,    //!< A region containing data or instructions.
               FILL_REGION             //!< Region to be initialized with zero bytes.
       } MemoryRegionType;

       //! Memory region flag constants.
       enum {
               REGION_READ_FLAG = 1,   //!< Region is readable.
               REGION_WRITE_FLAG = 2,  //!< Region is writable.
               REGION_EXEC_FLAG = 4,   //!< Region may contain executable code.

               REGION_RW_FLAG = REGION_READ_FLAG | REGION_WRITE_FLAG,  //!< Region is read-write.

               //! Mask to access only permissions flags for a region.
               REGION_PERM_FLAG_MASK = 0x7
       };

       /*!
        * Representation of a contiguous region of memory.
    *
    * \todo Add comparison operators so we can use the STL sort algorithm.
        */
       struct MemoryRegion
       {
               MemoryRegionType m_type;        //!< Memory region type.
               uint32_t m_address;     //!< The 32-bit start address of this region.
               uint32_t m_length;      //!< Number of bytes in this region.
               uint8_t * m_data;       //!< Pointer to data. Will be NULL for FILL_REGION type.
               unsigned m_flags;       //!< Flags for the region.

       //! \brief Calculates the address of the last byte occupied by this region.
       inline uint32_t endAddress() const { return m_address + m_length - 1; }

       //! \brief Equality operator.
       bool operator == (const MemoryRegion & other) const;
       };

       //! A list of #StExecutableImage::MemoryRegion objects.
       typedef std::list<MemoryRegion> MemoryRegionList;

   //! The iterator type used to access #StExecutableImage::MemoryRegion objects. This type
   //! is used by the methods #getRegionBegin() and #getRegionEnd().
       typedef MemoryRegionList::const_iterator const_iterator;

   //! The possible actions for regions matching an address filter range.
   typedef enum {
       ADDR_FILTER_NONE,       //!< Do nothing.
       ADDR_FILTER_ERROR,      //!< Raise an error exception.
       ADDR_FILTER_WARNING,    //!< Raise a warning exception.
       ADDR_FILTER_CROP        //!< Don't include the matching address range in the executable image.
   } AddressFilterAction;

   /*!
    * An address filter consists of a single address range and an action. If a
    * memory region overlaps the filter's range then the action will be performed.
    * The possible filter actions are defined by the #AddressFilterAction enumeration.
    */
   struct AddressFilter
   {
       AddressFilterAction m_action;   //!< Action to be performed when the filter is matched.
       uint32_t m_fromAddress; //!< Start address of the filter. Should be lower than or equal to #m_toAddress.
       uint32_t m_toAddress;   //!< End address of the filter. Should be higher than or equal to #m_fromAddress.
       unsigned m_priority;     //!< Priority for this filter. Zero is the lowest priority.

       //! \brief Constructor.
       AddressFilter(AddressFilterAction action, uint32_t from, uint32_t to, unsigned priority=0)
       :   m_action(action), m_fromAddress(from), m_toAddress(to), m_priority(priority)
       {
       }

       //! \brief Test routine.
       bool matchesMemoryRegion(const MemoryRegion & region) const;

       //! \brief Compares two address filter objects.
       int compare(const AddressFilter & other) const;

       //! \name Comparison operators
       //@{
       inline bool operator < (const AddressFilter & other) const { return compare(other) == -1; }
       inline bool operator > (const AddressFilter & other) const { return compare(other) == 1; }
       inline bool operator == (const AddressFilter & other) const { return compare(other) == 0; }
       inline bool operator <= (const AddressFilter & other) const { return compare(other) != 1; }
       inline bool operator >= (const AddressFilter & other) const { return compare(other) != -1; }
       //@}
   };

   //! List of #StExecutableImage::AddressFilter objects.
   typedef std::list<AddressFilter> AddressFilterList;

   //! The exception class raised for the #ADDR_FILTER_ERROR and #ADDR_FILTER_WARNING
   //! filter actions.
   class address_filter_exception
   {
   public:
       //! \brief Constructor.
       //!
       //! A local copy of \a matchingFilter is made, in case the image and/or filter
       //! are on the stack and would be disposed of when the exception is raised.
       address_filter_exception(bool isError, std::string & imageName, const AddressFilter & matchingFilter)
       : m_isError(isError), m_imageName(imageName), m_filter(matchingFilter)
       {
       }

       //! \brief Returns true if the exception is an error. Otherwise the exception
       //!     is for a warning.
       inline bool isError() const { return m_isError; }

       //! \brief
       inline std::string getImageName() const { return m_imageName; }

       //! \brief
       inline const AddressFilter & getMatchingFilter() const { return m_filter; }

   protected:
       bool m_isError;
       std::string m_imageName;
       AddressFilter m_filter;
   };

public:
       //! \brief Constructor.
       StExecutableImage(int inAlignment=256);

       //! \brief Copy constructor.
       StExecutableImage(const StExecutableImage & inOther);

       //! \brief Destructor.
       virtual ~StExecutableImage();

       //! \name Image name
       //! Methods for getting and setting the image name.
       //@{
       //! \brief Sets the image's name to \a inName.
       virtual void setName(const std::string & inName);

       //! \brief Returns a copy of the image's name.
       virtual std::string getName() const;
       //@}

       //! \name Regions
       //! Methods to add and access memory regions.
       //@{
       //! \brief Add a region to be filled with zeroes.
       virtual void addFillRegion(uint32_t inAddress, unsigned inLength);

       //! \brief Add a region containing data to be loaded.
       virtual void addTextRegion(uint32_t inAddress, const uint8_t * inData, unsigned inLength);

       //! \brief Returns the total number of regions.
       //!
       //! Note that this count may not be the same as the number of calls to
       //! addFillRegion() and addTextRegion() due to region coalescing.
       inline unsigned getRegionCount() const { return static_cast<unsigned>(m_image.size()); }

       //! \brief Returns a reference to the region specified by \a inIndex.
       const MemoryRegion & getRegionAtIndex(unsigned inIndex) const;

   //! \brief Return an iterator to the first region.
       inline const_iterator getRegionBegin() const { return m_image.begin(); }

   //! \brief Return an iterator to the next-after-last region.
       inline const_iterator getRegionEnd() const { return m_image.end(); }
       //@}

       //! \name Entry point
       //@{
       //! \brief Sets the entry point address.
       inline void setEntryPoint(uint32_t inEntryAddress) { m_entry = inEntryAddress; m_hasEntry = true; }

       //! \brief Returns true if an entry point has been set.
       inline bool hasEntryPoint() const { return m_hasEntry; }

       //! \brief Returns the entry point address.
       inline uint32_t getEntryPoint() const { return hasEntryPoint() ? m_entry : 0; }
       //@}

   //! \name Address filter
   //@{
   //! \brief Add a new address filter.
   virtual void addAddressFilter(const AddressFilter & filter);

   //! \brief Add multiple address filters at once.
   //!
   //! The template argument \a _T must be an iterator or const iterator that
   //! dereferences to an StExecutableImage::AddressFilter reference. All filters
   //! from \a from to \a to will be added to the address filter list.
   template<typename _T> void addAddressFilters(_T from, _T to)
   {
       _T it = from;
       for (; it != to; ++it)
       {
           addAddressFilter(*it);
       }
   }

   //! \brief Remove all active filters.
   virtual void clearAddressFilters();

   //! \brief Process all active filters and perform associated actions.
   virtual void applyAddressFilters();
   //@}

protected:
       std::string m_name;     //!< The name of the image (can be a file name, for instance).
       int m_alignment;        //!< The required address alignment for each memory region.
       bool m_hasEntry;        //!< True if an entry point has been set.
       uint32_t m_entry;       //!< Entry point address.
       MemoryRegionList m_image;       //!< The memory regions.
   AddressFilterList m_filters;    //!< List of active address filters.

   //! \brief Deletes the portion \a region that overlaps \a filter.
   void cropRegionToFilter(MemoryRegion & region, const AddressFilter & filter);

       //! \brief Inserts the region in sorted order or merges with one already in the image.
       void insertOrMergeRegion(MemoryRegion & inRegion);

       //! \brief Merges two memory regions into one.
       void mergeRegions(MemoryRegion & inOldRegion, MemoryRegion & inNewRegion);
};

#endif // _StExecutableImage_h_