
/*! @file CoverageMapBase.cc
 *  @brief CoverageMapBase Implementation
 *
 *  This file contains the implementation of the functions
 *  which provide a base level of functionality of a CoverageMap.
 */

#include <limits.h>

#include <iostream>
#include <iomanip>

#include <rld.h>
#include <rtems-utils.h>

#include "CoverageMapBase.h"

namespace Coverage {

  AddressInfo::AddressInfo()
    : isStartOfInstruction( false ),
      wasExecuted( false ),
      isBranch( false ),
      isNop( false ),
      wasTaken( false ),
      wasNotTaken( false )
  {
  }

  AddressRange::AddressRange()
    : lowAddress( 0 ),
      highAddress( 0 )
  {
  }

  AddressRange::AddressRange(
    const std::string& name,
    uint32_t           lowAddress,
    uint32_t           highAddress)
    : fileName( name ),
      lowAddress( lowAddress ),
      highAddress( highAddress )
  {
    info.resize( size() );
  }

  size_t AddressRange::size() const
  {
    return highAddress - lowAddress + 1;
  }

  bool AddressRange::inside( uint32_t address ) const
  {
    return address >= lowAddress && address <= highAddress;
  }

  AddressInfo& AddressRange::get( uint32_t address )
  {
    if ( !inside( address ) ) {
      throw rld::error( "address outside range", "AddressRange::get" );
    }

    size_t slot = address - lowAddress;

    if ( slot >= info.size () ) {
      throw rld::error( "address slot not found", "AddressRange::get" );
    }

    return info[slot];
  }

  const AddressInfo& AddressRange::get( uint32_t address ) const
  {
    if ( !inside( address ) ) {
      throw rld::error( "address outside range", "AddressRange::get" );
    }

    size_t slot = address - lowAddress;

    if ( slot >= info.size() ) {
      throw rld::error( "address slot not found", "AddressRange::get" );
    }

    return info[slot];
  }

  void AddressRange::dump( std::ostream& out, bool show_slots ) const
  {
    rtems::utils::ostream_guard old_state( out );

    out << std::hex << std::setfill( '0' )
        << "Address range: low = " << std::setw( 8 ) << lowAddress
        << " high = " << std::setw( 8 ) << highAddress
        << std::endl;

    if (show_slots) {
      size_t slot = 0;

      for ( auto& i : info ) {
        out << std::hex << std::setfill( '0' )
            << "0x" << std::setw( 8 ) << slot++ + lowAddress
            << "- isStartOfInstruction:"
            << (char*) ( i.isStartOfInstruction ? "yes" : "no" )
            << " wasExecuted:"
            << (char*) ( i.wasExecuted ? "yes" : "no" )
            << "\n           isBranch:"
            << (char*) ( i.isBranch ? "yes" : "no" )
            << " wasTaken:"
            << (char*) ( i.wasTaken ? "yes" : "no" )
            << " wasNotTaken:"
            << (char*) ( i.wasNotTaken ? "yes" : "no" )
            << std::dec << std::setfill( ' ' )
            << std::endl;
      }
    }
  }

  CoverageMapBase::CoverageMapBase(
    const std::string& exefileName,
    uint32_t           low,
    uint32_t           high
  ) : exefileName( exefileName )
  {
    Ranges.push_back( AddressRange( exefileName, low, high ) );
  }

  CoverageMapBase::~CoverageMapBase()
  {
  }

  void CoverageMapBase::Add( uint32_t low, uint32_t high )
  {
    Ranges.push_back( AddressRange( exefileName, low, high ) );
  }

  bool CoverageMapBase::validAddress( const uint32_t address ) const
  {
    for ( auto& r : Ranges ) {
      if ( r.inside( address ) ) {
        return true;
      }
    }

    return false;
  }

  void CoverageMapBase::dump() const
  {
    std::cerr << "Coverage Map Contents\n";

    for ( auto& r : Ranges ) {
      r.dump( std::cerr );
    }
  }

  uint32_t CoverageMapBase::getSize() const
  {
    size_t size = 0;

    for ( auto& r : Ranges ) {
      size += r.size ();
    }

    return size;
  }

  uint32_t CoverageMapBase::getSizeOfRange( size_t index ) const
  {
    return Ranges.at( index ).size();
  }

  bool CoverageMapBase::getBeginningOfInstruction(
    uint32_t  address,
    uint32_t* beginning
  ) const
  {
    bool         status = false;
    uint32_t     start;
    AddressRange range;

    status = getRange( address, range );
    if ( status != true ) {
      return status;
    }

    start = address;

    while ( start >= range.lowAddress ) {
      if ( isStartOfInstruction( start - range.lowAddress ) ) {
        *beginning = start;
        status = true;
        break;
      } else {
        start--;
      }
    }

    return status;
  }

  int32_t CoverageMapBase::getFirstLowAddress() const
  {
    /*
     * This is broken, do not trust it.
     */
    return Ranges.front().lowAddress;
  }

  uint32_t CoverageMapBase::getLowAddressOfRange( size_t index ) const
  {
    return Ranges.at( index ).lowAddress;
  }

  bool CoverageMapBase::getRange( uint32_t address, AddressRange& range ) const
  {
    for ( auto r : Ranges ) {
      if ( r.inside( address ) ) {
        range.lowAddress  = r.lowAddress;
        range.highAddress = r.highAddress;
        range.info        = r.info;
        return true;
      }
    }

    return false;
  }

  AddressInfo& CoverageMapBase::getInfo( uint32_t address )
  {
    for ( auto& r : Ranges ) {
      if ( r.inside( address ) ) {
        return r.get( address );
      }
    }

    throw rld::error( "address out of bounds", "CoverageMapBase::getInfo" );
  }

  const AddressInfo& CoverageMapBase::getInfo( uint32_t address ) const
  {
    for ( auto& r : Ranges ) {
      if ( r.inside( address ) ) {
        return r.get( address );
      }
    }

    throw rld::error( "address out of bounds", "CoverageMapBase::getInfo" );
  }

  void CoverageMapBase::setIsStartOfInstruction( uint32_t  address )
  {
    if ( validAddress( address ) ) {
      getInfo( address ).isStartOfInstruction = true;
    }
  }

  bool CoverageMapBase::isStartOfInstruction( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return false;
    }

    return getInfo( address ).isStartOfInstruction;
  }

  void CoverageMapBase::setWasExecuted( uint32_t address )
  {
    if ( validAddress( address ) ) {
      getInfo( address ).wasExecuted += 1;
    }
  }

  void CoverageMapBase::sumWasExecuted( uint32_t address, uint32_t addition )
  {
    if ( validAddress( address ) ) {
      getInfo( address ).wasExecuted += addition;
    }
  }

  bool CoverageMapBase::wasExecuted( uint32_t address ) const
  {
    bool result = false;

    if ( validAddress( address ) && ( getInfo( address ).wasExecuted > 0 ) ) {
      result = true;
    }

    return result;
  }

  uint32_t CoverageMapBase::getWasExecuted( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return 0;
    }

    return getInfo( address ).wasExecuted;
  }

  void CoverageMapBase::setIsBranch( uint32_t address )
  {
    if ( validAddress( address ) ) {
      getInfo( address ).isBranch = true;
    }
  }

  bool CoverageMapBase::isNop( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return false;
    }

    return getInfo( address ).isNop;
  }

  void CoverageMapBase::setIsNop( uint32_t address )
  {
    if ( !validAddress( address ) ) {
      return;
    }

    getInfo( address ).isNop = true;
  }

  bool CoverageMapBase::isBranch( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return false;
    }

    return getInfo( address ).isBranch;
  }

  void CoverageMapBase::setWasTaken( uint32_t address )
  {
    if ( !validAddress( address ) ) {
      return;
    }

    getInfo( address ).wasTaken += 1;
  }

  void CoverageMapBase::setWasNotTaken( uint32_t address )
  {
    if ( !validAddress( address ) ) {
      return;
    }

    getInfo( address ).wasNotTaken += 1;
  }

  bool CoverageMapBase::wasAlwaysTaken( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return false;
    }

    const AddressInfo& info = getInfo( address );

    return info.wasTaken && !info.wasNotTaken;
  }

  bool CoverageMapBase::wasNeverTaken( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return false;
    }

    const AddressInfo& info = getInfo( address );

    return !info.wasTaken && info.wasNotTaken;
  }

  bool CoverageMapBase::wasNotTaken( uint32_t address ) const
  {
    bool result = true;

    if ( !validAddress( address ) ) {
      result = false;
    } else if ( getInfo( address ).wasNotTaken <= 0 ) {
      result = false;
    }

    return result;
  }

  void CoverageMapBase::sumWasNotTaken( uint32_t address, uint32_t addition )
  {
    if ( validAddress( address ) ) {
      getInfo( address ).wasNotTaken += addition;
    }
  }

  uint32_t CoverageMapBase::getWasNotTaken( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return 0;
    }

    return getInfo( address ).wasNotTaken;
  }

  bool CoverageMapBase::wasTaken( uint32_t address ) const
  {
    bool result = true;

    if ( !validAddress( address ) ) {
      result = false;
    } else if ( getInfo( address ).wasTaken <= 0 ) {
      result = false;
    }

    return result;
  }

  void CoverageMapBase::sumWasTaken( uint32_t address, uint32_t addition )
  {
    if ( !validAddress( address ) ) {
      return;
    }

    getInfo( address ).wasTaken += addition;
  }

  uint32_t CoverageMapBase::getWasTaken( uint32_t address ) const
  {
    if ( !validAddress( address ) ) {
      return 0;
    }

    return getInfo( address ).wasTaken;
  }
}
