//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// platform_t.cpp
//
// test some platform assumptions

#include "stdcore.h"
#include "platform.h"
#include "test/test.h"
#include "error.h"

using namespace std;

/////////////////////////////////////////////////////////
// UTIL FUNCTION PROTOTYPES
/////////////////////////////////////////////////////////
void TestAlignment();
void TestSizes();

/////////////////////////////////////////////////////////
// TEMPLATIZED UTIL FUNCTION PROTOTYPES
/////////////////////////////////////////////////////////
template< class E, class T >
bool CanBeRepresentedAs( E e, T t );

/////////////////////////////////////////////////////////
// TEMPLATIZED UTIL CLASSES DECLARATIONS
/////////////////////////////////////////////////////////

// Constructing this class will write to a memory location
// offset by ALIGN_SIZE.  If it chokes, you'll get a bus error
template<int ALIGN_SIZE> 
class AlignMe
{
    public:
        AlignMe();
    private:
        byte a[sizeof(int64)+ALIGN_SIZE];  // we want to be able to access a int64 at address [ALIGN_SIZE]
};

/////////////////////////////////////////////////////////
// MAIN TEST FUNCTION
/////////////////////////////////////////////////////////

void TestPlatform()
{
    TestAlignment();
    TestSizes();
}

/////////////////////////////////////////////////////////
// TEMPLATIZED UTIL CLASSES IMPLEMENTATIONS
/////////////////////////////////////////////////////////

template<int ALIGN_SIZE> 
AlignMe<ALIGN_SIZE>::AlignMe()
{
    TCOUT << _T("Testing alignment of size ") << ALIGN_SIZE << std::endl;

    // access a double in the byte array to see if it is aligned.  if it isn't and the CPU
    // can't handle it, you'll get a bus error
    
    // this should choke if the CPU can't 
    // handle misaligned memory access
    int32* pi = (int32*)&a[ALIGN_SIZE];
    TCOUT << _T("Testing alignment of an int32...") << std::endl;
    TCOUT << _T("Reading...") << std::endl;
    int32 i = *pi; // access memory for read
    TCOUT << _T("Read succeeded.") << std::endl;
    TCOUT << _T("Writing...") << std::endl;
    *pi = i; // access memory for write
    TCOUT << _T("Write succeeded.") << std::endl;

    // this should choke if the CPU can't 
    // handle misaligned memory access
    int64* pb = (int64*)&a[ALIGN_SIZE];
    TCOUT << _T("Testing alignment of an int64...") << std::endl;
    TCOUT << _T("Reading...") << std::endl;
    int64 I = *pb; // access memory for read
    TCOUT << _T("Read succeeded") << std::endl;
    TCOUT << _T("Writing...") << std::endl;
    *pb = I; // access memory for write
    TCOUT << _T("Write succeeded.") << std::endl;
    
    TCOUT << _T("Alignment of ") << ALIGN_SIZE << _T(" ") << ( ALIGN_SIZE == 1 ? _T("byte") : _T("bytes") ) << _T(" is OK") << std::endl
          << _T("=========================================\n");
}


/////////////////////////////////////////////////////////
// UTIL FUNCTIONS
/////////////////////////////////////////////////////////

void TestAlignment()
{
    // - - - - - - - - - - - - - - - - - - - - - -  
    // empirically determine byte alignment -- see AlignMe definition
    // The last AlignMe to be successfully constructed 
    // - - - - - - - - - - - - - - - - - - - - - -  

    TCOUT << _T("Testing for byte alignment\n")
          << _T("=========================================\n");

    AlignMe<128> a128;
    AlignMe<64> a64;
    AlignMe<32> a32;
    AlignMe<16> a16;
    AlignMe<8> a8;
    AlignMe<4> a4;
    AlignMe<2> a2;
    AlignMe<1> a1;

    // - - - - - - - - - - - - - - - - - - - - - -  
    // test a misaligned memory access -- if this 
    // chokes, your CPU can't handle such accesses
    // (if it works, your CPU may still not handle
    // such accesses: it may have handled the 
    // hardware interrupt that might have occured.
    // - - - - - - - - - - - - - - - - - - - - - -  
    byte a[sizeof(int32)+sizeof(byte)];

    // this should be fine
    a[0] = 0xAB;

    
    TCOUT << _T("=========================================\n") 
          << _T("About to test memory access off by 1 byte\n") 
          << _T("If you do not see a confirmation after this line, the test choked") 
          << std::endl;

    // this should choke if the CPU can't 
    // handle misaligned memory access        
    int32* pi = (int32*)&a[1];
    *pi = *pi; // misaligned access (read and write)
    
    TCOUT << _T("Misaligned access OK.") << std::endl;

    // - - - - - - - - - - - - - - - - - - - - - -  
    // make sure our BYTE_ALIGN value is correct --
    // OK, if the above test failed, then comment it out and try this one.
    // if it works, then our BYTE_ALIGN value is large enough
    // - - - - - - - - - - - - - - - - - - - - - - 
    
    byte b[2*sizeof(BYTE_ALIGN)];

    // this should be fine
    b[0] = 0xAB;

    TCOUT << _T("=========================================\n") 
          << _T("About to test memory access off by ") << BYTE_ALIGN 
          << _T(" ") << ( BYTE_ALIGN == 1 ? _T("byte") : _T("bytes") ) << std::endl
          << _T("If you do not see a confirmation after this line, the test choked") 
          << std::endl;

    // this should choke if the CPU can't 
    // handle misaligned memory access        
    pi = (int32*)&b[BYTE_ALIGN];
    *pi = *pi; // aligned (hopefully) access (read and write)
    
    TCOUT << _T("Aligned access OK.  BYTE_ALIGN value of ") << BYTE_ALIGN << _T(" is good.") << std::endl;    
    TCOUT << _T("=========================================\n");
}

void TestSizes()
{
    /*
    ASSERT( CanBeRepresentedAs( char(), char() ) );
    ASSERT( CanBeRepresentedAs( char(), unsigned char() ) );
    ASSERT( CanBeRepresentedAs( unsigned char(), char() ) );
    ASSERT( CanBeRepresentedAs( unsigned char(), unsigned char() ) );
    ASSERT( CanBeRepresentedAs( signed char(), char() ) );
    ASSERT( CanBeRepresentedAs( char(), signed char() ) );
    ASSERT( CanBeRepresentedAs( signed char(), signed char() ) );
    ASSERT( CanBeRepresentedAs( signed char(), unsigned char() ) );
    ASSERT( CanBeRepresentedAs( char(), signed char() ) );
    */
}
 
/////////////////////////////////////////////////////////
// TEMPLATIZED UTIL FUNCTION IMPEMENTATIONS
/////////////////////////////////////////////////////////   

template< class E, class T >
bool CanBeRepresentedAs( E e, T t )
{
    ASSERT( std::numeric_limits< E >::is_specialized );
    ASSERT( std::numeric_limits< T >::is_specialized );

    bool fReturn = true;

    fReturn &= ( std::numeric_limits< E >::min() >= std::numeric_limits< T >::min() );
    fReturn &= ( std::numeric_limits< E >::max() <= std::numeric_limits< T >::max() );

    return fReturn;
}
