<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regactivex.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 16:39:15 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#define UNICODE

#include <windows.h>
#include <msiquery.h>
#include <string.h>
#include <malloc.h>

#define CHART_COMPONENT 1
#define DRAW_COMPONENT 2
#define IMPRESS_COMPONENT 4
#define CALC_COMPONENT 8
#define WRITER_COMPONENT 16
#define MATH_COMPONENT 32

typedef int ( __stdcall * DllNativeProc ) ( int, BOOL );

BOOL UnicodeEquals( wchar_t* pStr1, wchar_t* pStr2 )
{
    if ( pStr1 == NULL && pStr2 == NULL )
        return TRUE;
    else if ( pStr1 == NULL || pStr2 == NULL )
        return FALSE;

    while( *pStr1 == *pStr2 && *pStr1 && *pStr2 )
        pStr1++, pStr2++;

    return ( *pStr1 == 0 && *pStr2 == 0 );
}

//----------------------------------------------------------
char* UnicodeToAnsiString( wchar_t* pUniString )
{
    int len = WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, 0, 0, 0, 0 );

    char* buff = reinterpret_cast<char*>( malloc( len ) );

    WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, buff, len, 0, 0 );

    return buff;
}

//----------------------------------------------------------
void RegisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllRegisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
void UnregisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllUnregisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
BOOL GetMsiProp( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
{
    DWORD sz = 0;
       if ( MsiGetProperty( hMSI, pPropName, L"", &sz ) == ERROR_MORE_DATA )
       {
           sz++;
           DWORD nbytes = sz * sizeof( wchar_t );
           wchar_t* buff = reinterpret_cast<wchar_t*>( malloc( nbytes ) );
           ZeroMemory( buff, nbytes );
           MsiGetProperty( hMSI, pPropName, buff, &sz );
           *ppValue = buff;

        return TRUE;
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetActiveXControlPath( MSIHANDLE hMSI, char** ppActiveXPath )
{
    wchar_t* pProgPath = NULL;
    if ( GetMsiProp( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath )
       {
        char* pCharProgPath = UnicodeToAnsiString( pProgPath );
        if ( pCharProgPath )
        {
            int nLen = strlen( pCharProgPath );
            *ppActiveXPath = reinterpret_cast<char*>( malloc( nLen + 23 ) );
            strncpy( *ppActiveXPath, pCharProgPath, nLen );
            strncpy( (*ppActiveXPath) + nLen, "program\\so_activex.dll", 22 );
            (*ppActiveXPath)[nLen+22] = 0;

            free( pCharProgPath );

            return TRUE;
        }

        free( pProgPath );
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode )
{
    // for now the chart is always installed
    nOldInstallMode = CHART_COMPONENT;
    nInstallMode = CHART_COMPONENT;
    nDeinstallMode = 0;

    INSTALLSTATE current_state;
       INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Wrt_Bin", &current_state, &future_state ) )
    {
        // analyze writer installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= WRITER_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= WRITER_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= WRITER_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Calc_Bin", &current_state, &future_state ) )
    {
        // analyze calc installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= CALC_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= CALC_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= CALC_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Draw_Bin", &current_state, &future_state ) )
    {
        // analyze draw installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= DRAW_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= DRAW_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= DRAW_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Impress_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= IMPRESS_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= IMPRESS_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= IMPRESS_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Math_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= MATH_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= MATH_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= MATH_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    return TRUE;
}

//----------------------------------------------------------
BOOL MakeInstallForAllUsers( MSIHANDLE hMSI )
{
    BOOL bResult = FALSE;
    wchar_t* pVal = NULL;
    if ( GetMsiProp( hMSI, L"ALLUSERS", &pVal ) && pVal )
    {
        bResult = UnicodeEquals( pVal , L"1" );
        free( pVal );
    }

    return bResult;
}

//----------------------------------------------------------
extern "C" UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI )
{
    int nOldInstallMode = 0;
    int nInstallMode = 0;
    int nDeinstallMode = 0;

    // MessageBox(NULL, L"InstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );
        char* pActiveXPath = NULL;
        if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath
        && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) )
        {
            if ( future_state == INSTALLSTATE_LOCAL )
            {
                // the control is installed in the new selected configuration

                if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode )
                    UnregisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );

                if ( nInstallMode )
                    RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );
            }
            else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            {
                if ( nOldInstallMode )
                    UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser );
            }
        }

        if ( pActiveXPath )
            free( pActiveXPath );
    }
    else
    {
        // assert( FALSE );
    }

    return ERROR_SUCCESS;
}

//----------------------------------------------------------
extern "C" __stdcall UINT DeinstallActiveXControl( MSIHANDLE hMSI )
{
    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    // MessageBox(NULL, L"DeinstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        char* pActiveXPath = NULL;
        if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath )
        {
            BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );

            wchar_t* rm = NULL;
            if ( GetMsiProp( hMSI, L"REMOVE", &rm ) && rm && UnicodeEquals( rm, L"ALL" ) )
            {
                UnregisterActiveXNative( pActiveXPath,
                                        CHART_COMPONENT
                                        | DRAW_COMPONENT
                                        | IMPRESS_COMPONENT
                                        | CALC_COMPONENT
                                        | WRITER_COMPONENT,
                                        bInstallForAllUser );
            }

            if ( rm )
                free( rm );

            free( pActiveXPath );
        }
    }

    return ERROR_SUCCESS;
}

<commit_msg>INTEGRATION: CWS fwk29 (1.8.46); FILE MERGED 2006/01/04 15:27:49 mav 1.8.46.1: #i57588# deregister starmath as well<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: regactivex.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: rt $ $Date: 2006-02-07 10:20:02 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#define UNICODE

#include <windows.h>
#include <msiquery.h>
#include <string.h>
#include <malloc.h>

#define CHART_COMPONENT 1
#define DRAW_COMPONENT 2
#define IMPRESS_COMPONENT 4
#define CALC_COMPONENT 8
#define WRITER_COMPONENT 16
#define MATH_COMPONENT 32

typedef int ( __stdcall * DllNativeProc ) ( int, BOOL );

BOOL UnicodeEquals( wchar_t* pStr1, wchar_t* pStr2 )
{
    if ( pStr1 == NULL && pStr2 == NULL )
        return TRUE;
    else if ( pStr1 == NULL || pStr2 == NULL )
        return FALSE;

    while( *pStr1 == *pStr2 && *pStr1 && *pStr2 )
        pStr1++, pStr2++;

    return ( *pStr1 == 0 && *pStr2 == 0 );
}

//----------------------------------------------------------
char* UnicodeToAnsiString( wchar_t* pUniString )
{
    int len = WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, 0, 0, 0, 0 );

    char* buff = reinterpret_cast<char*>( malloc( len ) );

    WideCharToMultiByte(
        CP_ACP, 0, pUniString, -1, buff, len, 0, 0 );

    return buff;
}

//----------------------------------------------------------
void RegisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllRegisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
void UnregisterActiveXNative( const char* pActiveXPath, int nMode, BOOL InstallForAllUser )
{
    // For Win98/WinME the values should be written to the local machine
    OSVERSIONINFO       aVerInfo;
    aVerInfo.dwOSVersionInfoSize = sizeof( aVerInfo );
    if ( GetVersionEx( &aVerInfo ) && aVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT )
        InstallForAllUser = TRUE;

    HINSTANCE hModule = LoadLibraryExA( pActiveXPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH );
    if( !( hModule <= ( HINSTANCE )HINSTANCE_ERROR ) )
    {
        DllNativeProc pNativeProc = ( DllNativeProc )GetProcAddress( hModule, "DllUnregisterServerNative" );
        if( pNativeProc!=NULL )
            ( *pNativeProc )( nMode, InstallForAllUser );

        FreeLibrary( hModule );
    }
}

//----------------------------------------------------------
BOOL GetMsiProp( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue )
{
    DWORD sz = 0;
       if ( MsiGetProperty( hMSI, pPropName, L"", &sz ) == ERROR_MORE_DATA )
       {
           sz++;
           DWORD nbytes = sz * sizeof( wchar_t );
           wchar_t* buff = reinterpret_cast<wchar_t*>( malloc( nbytes ) );
           ZeroMemory( buff, nbytes );
           MsiGetProperty( hMSI, pPropName, buff, &sz );
           *ppValue = buff;

        return TRUE;
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetActiveXControlPath( MSIHANDLE hMSI, char** ppActiveXPath )
{
    wchar_t* pProgPath = NULL;
    if ( GetMsiProp( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath )
       {
        char* pCharProgPath = UnicodeToAnsiString( pProgPath );
        if ( pCharProgPath )
        {
            int nLen = strlen( pCharProgPath );
            *ppActiveXPath = reinterpret_cast<char*>( malloc( nLen + 23 ) );
            strncpy( *ppActiveXPath, pCharProgPath, nLen );
            strncpy( (*ppActiveXPath) + nLen, "program\\so_activex.dll", 22 );
            (*ppActiveXPath)[nLen+22] = 0;

            free( pCharProgPath );

            return TRUE;
        }

        free( pProgPath );
    }

    return FALSE;
}

//----------------------------------------------------------
BOOL GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode )
{
    // for now the chart is always installed
    nOldInstallMode = CHART_COMPONENT;
    nInstallMode = CHART_COMPONENT;
    nDeinstallMode = 0;

    INSTALLSTATE current_state;
       INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Wrt_Bin", &current_state, &future_state ) )
    {
        // analyze writer installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= WRITER_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= WRITER_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= WRITER_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Calc_Bin", &current_state, &future_state ) )
    {
        // analyze calc installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= CALC_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= CALC_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= CALC_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Draw_Bin", &current_state, &future_state ) )
    {
        // analyze draw installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= DRAW_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= DRAW_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= DRAW_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Impress_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= IMPRESS_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= IMPRESS_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= IMPRESS_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_p_Math_Bin", &current_state, &future_state ) )
    {
        // analyze impress installation mode
        if ( current_state == INSTALLSTATE_LOCAL )
            nOldInstallMode |= MATH_COMPONENT;

        if ( future_state == INSTALLSTATE_LOCAL )
            nInstallMode |= MATH_COMPONENT;
           else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            nDeinstallMode |= MATH_COMPONENT;
    }
    else
    {
        // assert( FALSE );
    }

    return TRUE;
}

//----------------------------------------------------------
BOOL MakeInstallForAllUsers( MSIHANDLE hMSI )
{
    BOOL bResult = FALSE;
    wchar_t* pVal = NULL;
    if ( GetMsiProp( hMSI, L"ALLUSERS", &pVal ) && pVal )
    {
        bResult = UnicodeEquals( pVal , L"1" );
        free( pVal );
    }

    return bResult;
}

//----------------------------------------------------------
extern "C" UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI )
{
    int nOldInstallMode = 0;
    int nInstallMode = 0;
    int nDeinstallMode = 0;

    // MessageBox(NULL, L"InstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );
        char* pActiveXPath = NULL;
        if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath
        && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) )
        {
            if ( future_state == INSTALLSTATE_LOCAL )
            {
                // the control is installed in the new selected configuration

                if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode )
                    UnregisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );

                if ( nInstallMode )
                    RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser );
            }
            else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT )
            {
                if ( nOldInstallMode )
                    UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser );
            }
        }

        if ( pActiveXPath )
            free( pActiveXPath );
    }
    else
    {
        // assert( FALSE );
    }

    return ERROR_SUCCESS;
}

//----------------------------------------------------------
extern "C" __stdcall UINT DeinstallActiveXControl( MSIHANDLE hMSI )
{
    INSTALLSTATE current_state;
    INSTALLSTATE future_state;

    // MessageBox(NULL, L"DeinstallActiveXControl", L"Information", MB_OK | MB_ICONINFORMATION);

    if ( ERROR_SUCCESS == MsiGetFeatureState( hMSI, L"gm_o_Activexcontrol", &current_state, &future_state ) )
    {
        char* pActiveXPath = NULL;
        if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath )
        {
            BOOL bInstallForAllUser = MakeInstallForAllUsers( hMSI );

            wchar_t* rm = NULL;
            if ( GetMsiProp( hMSI, L"REMOVE", &rm ) && rm && UnicodeEquals( rm, L"ALL" ) )
            {
                UnregisterActiveXNative( pActiveXPath,
                                        CHART_COMPONENT
                                        | DRAW_COMPONENT
                                        | IMPRESS_COMPONENT
                                        | CALC_COMPONENT
                                        | WRITER_COMPONENT
                                        | MATH_COMPONENT,
                                        bInstallForAllUser );
            }

            if ( rm )
                free( rm );

            free( pActiveXPath );
        }
    }

    return ERROR_SUCCESS;
}

<|endoftext|>


<commit_before>#include <iostream>
using namespace std;
// ROOT
#include <TTree.h>
#include <TFile.h>
#include <TMath.h>
#include <TGraph.h>
using namespace TMath;
// UNIC
#include <UNIC/Units.h>
#include <UNIC/Constants.h>
using namespace UNIC;
// MAD
#include <MAD/GeCrystal.h>
using namespace MAD;
const int N = 500;
// df at i
double d(double *f, int i)
{
   if (i<4 || i>2*N-4) return 0;
   return (f[i+1]-f[i-1])/2; // 2 points
   //return (-f[i+2]+8*f[i+1]-8*f[i-1]+f[i-2])/12; // 4
   //return (-f[i+4]-8*f[i+2]+128*f[i+1]-128*f[i-1]+8*f[i-2]+f[i-4])/180; // 6
}
// thin plasma tube
int main(int argc, char** argv)
{
   double dt=1e-7*ns; // large dt causes E to decrease too fast
   double dx=0.5*nm; // small dx may cause artificial oscillation
   double Ee=1000*volt/cm;
   double R=1e-6*cm, mean=0, sigma=R/3, height=300./(Pi()*R*R);
   bool norm;

   GeCrystal ge;
   double epsilon=ge.Epsilon()*epsilon0;
   double Q=Abs(electron_charge);
   //double De=0.046*cm2/s, Dh=0.046*cm2/s;

   // initialize arrays
   // p and n are number densities
   double x[2*N+1], p[2*N+1], n[2*N+1], E[2*N+1], pE[2*N+1],nE[2*N+1];
   for (int i=0; i<2*N+1; i++) {
      x[i]=(i-N)*dx;
      p[i]=n[i]=height*Gaus(x[i],mean=0,sigma,norm=kTRUE);
      if (p[i]<1e-5/cm3) p[i]=0;
      if (n[i]<1e-5/cm3) n[i]=0;
      E[i]=Ee;
      pE[i]=p[i]*E[i];
      nE[i]=n[i]*E[i];
   }
   // slopes
   //double dp[2*N+1], dn[2*N+1], dE[2*N+1], dpE[2*N+1], dnE[2*N+1];
   //for (int i=1; i<2*N; i++) {
   //   dp[i] = (p[i+1]-p[i-1])/2;
   //   dn[i] = (n[i+1]-n[i-1])/2;
   //   dE[i] = (-E[i+2]+8*E[i+1]-8*E[i-1]+E[i-2])/12;
   //   dpE[i] = (-pE[i+2]+8*pE[i+1]-8*pE[i-1]+pE[i-2])/12;
   //   dnE[i] = (-nE[i+2]+8*nE[i+1]-8*nE[i-1]+nE[i-2])/12;
   //}
   //dp[2*N] = dp[0] = 0;
   //dn[2*N] = dn[0] = 0;
   //dE[2*N] = dE[0] = 0;
   //dpE[2*N] = dpE[0] = 0;
   //dnE[2*N] = dnE[0] = 0;

   //double d2p[2*N+1], d2n[2*N+1];
   //for (int i=1; i<2*N; i++) {
   //   d2p[i] = (dp[i+1]-dp[i-1])/2;
   //   d2n[i] = (dn[i+1]-dn[i-1])/2;
   //}
   //d2p[2*N] = d2p[0] = 0;
   //d2n[2*N] = d2n[0] = 0;

   //double phi[2*N+1]; // weighting potential
   //for (int i=0; i<2*N+1; i++) phi[i]=float(i)/2/N;

   ////Transverse diffusion
   //double Re[2*N+1], Rh[2*N+1];
   //for (int i=0; i<2*N+1; i++){
   //   Re[i]=18*n[i]*De/R/R;
   //   Rh[i]=18*p[i]*Dh/R/R;
   //}

   // output
   TFile *output = new TFile("tube.root","recreate");
   TTree *t = new TTree("t","time slices");
   t->Branch("x",x,Form("x[%d]/D",2*N+1));
   t->Branch("p",p,Form("p[%d]/D",2*N+1));
   t->Branch("n",n,Form("n[%d]/D",2*N+1));
   t->Branch("E",E,Form("E[%d]/D",2*N+1));
//   t->Branch("pE",pE,Form("pE[%d]/D",2*N+1));
//   t->Branch("nE",nE,Form("nE[%d]/D",2*N+1));
//   t->Branch("dp",dp,Form("dp[%d]/D",2*N+1));
//   t->Branch("dn",dn,Form("dn[%d]/D",2*N+1));
//   t->Branch("dE",dE,Form("dE[%d]/D",2*N+1));
//   t->Branch("dpE",dpE,Form("dpE[%d]/D",2*N+1));
//   t->Branch("dnE",dnE,Form("dnE[%d]/D",2*N+1));
//   t->Branch("d2p",d2p,Form("d2p[%d]/D",2*N+1));
//   t->Branch("d2n",d2n,Form("d2n[%d]/D",2*N+1));
//   t->Branch("Re",Re,Form("Re[%d]/D",2*N+1));
//   t->Branch("Rh",Rh,Form("Rh[%d]/D",2*N+1));
//   t->Branch("dx",&dx,"dx/D");
//   t->Branch("dt",&dt,"dt/D");
   // save units into tree
   double CM3=cm3, CM2=cm2, CM=cm, UM=um, V=volt, SEC=s, NS=ns, NM=nm;
   t->Branch("cm3",&CM3,"cm3/D");
   t->Branch("cm2",&CM2,"cm2/D");
   t->Branch("V",&V,"V/D");
   t->Branch("cm",&CM,"cm/D");
   t->Branch("um",&UM,"um/D");
   t->Branch("nm",&NM,"nm/D");
   t->Branch("ns",&NS,"ns/D");
   t->Branch("s",&SEC,"s/D");
//   t->Branch("eps",&epsilon,"eps/D");
//   t->Branch("q",&Q,"q/D");

   int iStep=0, nSteps = 100;
   //double time[1000]={0}, charge[1000]={0};

   // evolve
   if (argc>1) nSteps = atoi(argv[1]);
   while (iStep<nSteps) {
      if (iStep%100==0) cout<<iStep<<" steps passed"<<endl;
      t->Fill();
      // update charge
      //for (int i=0; i<2*N+1; i++) {
      //   charge[iStep]+=(p[i]-n[i])*phi[i];
      //}
      //time[iStep]=iStep*dt;
      // update electron and hole distributions
      for (int i=0; i<2*N+1; i++) {
         //double dn_dt = ge.Mu('e', n[i])*(dn[i]/dx*E[i]+n[i]*dE[i]/dx);
         //double dp_dt = -ge.Mu('h', p[i])*(dp[i]/dx*E[i]+p[i]*dE[i]/dx);
         double dn_dt = ge.Mu('e', n[i])*d(nE,i)/dx;
         double dp_dt = -ge.Mu('h', p[i])*d(pE,i)/dx;
         if (Abs(dn_dt*dt)<1e-10/cm3) dn_dt=0;
         if (Abs(dp_dt*dt)<1e-10/cm3) dp_dt=0;
         n[i]+=dn_dt*dt;
         p[i]+=dp_dt*dt;
         if (n[i]<0) n[i]=0;
         if (p[i]<0) p[i]=0;
      }
      // update electric field distribution
      for (int i=0; i<2*N+1; i++) {
         E[i]=0;
         for (int j=0; j<i; j++)
            E[i]+=(p[j]-n[j])*(1-(i-j)*dx/sqrt((i-j)*(i-j)*dx*dx+R*R));
         for (int j=i+1; j<2*N+1; j++)
            E[i]+=(n[j]-p[j])*(1-(j-i)*dx/sqrt((j-i)*(j-i)*dx*dx+R*R));
         E[i]*=Q*dx/2/epsilon;
         E[i]+=Ee;
         if (E[i]<1e-10*volt/cm) E[i]=0; // large dt may over evolve things
      }

      // update pE[i]and nE[i]
      for (int i=0; i<2*N+1; i++){
         pE[i] = p[i]*E[i];
         nE[i] = n[i]*E[i];
      }
      // update slopes
      //for (int i=1; i<2*N; i++) {
      //   dp[i] = (p[i+1]-p[i-1])/2;
      //   dn[i] = (n[i+1]-n[i-1])/2;
      //   dE[i] = (-E[i+2]+8*E[i+1]-8*E[i-1]+E[i-2])/12;
      //   dpE[i] = (-pE[i+2]+8*pE[i+1]-8*pE[i-1]+pE[i-2])/12;
      //   dnE[i] = (-nE[i+2]+8*nE[i+1]-8*nE[i-1]+nE[i-2])/12;
      //}
      //dp[2*N] = dp[0] = 0;
      //dn[2*N] = dn[0] = 0;
      //dE[2*N] = dE[0] = 0;
      //dpE[2*N] = dpE[0] = 0;
      //dnE[2*N] = dnE[0] = 0;

      //// update second derivative
      //for (int i=1; i<2*N; i++) {
      //   d2p[i] = (dp[i+1]-dp[i-1])/2;
      //   d2n[i] = (dn[i+1]-dn[i-1])/2;
      //}
      //d2p[2*N] = d2p[0] = 0;
      //d2n[2*N] = d2n[0] = 0;

      ////update transverse diffusion
      //for (int i=0; i<2*N+1; i++){
      //   Re[i]=(18*n[i]*De/R/R)*((18*De*iStep*dt/R/R)+1);
      //   Rh[i]=(18*p[i]*Dh/R/R)*((18*Dh*iStep*dt/R/R)+1);
      //}

      iStep++;
   }
   t->Write("t",6);

   // TGraph *g = new TGraph(nSteps,time,charge);
   // g->Write("g");

   output->Close();

   return 0;
}
<commit_msg>enabled loading old results<commit_after>#include <iostream>
using namespace std;
// ROOT
#include <TTree.h>
#include <TFile.h>
#include <TMath.h>
#include <TChain.h>
#include <TGraph.h>
using namespace TMath;
// UNIC
#include <UNIC/Units.h>
#include <UNIC/Constants.h>
using namespace UNIC;
// MAD
#include <MAD/GeCrystal.h>
using namespace MAD;
const int N = 1000;
// df at i
double d(double *f, int i)
{
   if (i<1 || i>2*N-1) return 0;
   return (f[i+1]-f[i-1])/2; // 2 points
   //if (i<2 || i>2*N-2) return 0;
   //return (-f[i+2]+8*f[i+1]-8*f[i-1]+f[i-2])/12; // 4
   //if (i<4 || i>2*N-4) return 0;
   //return (-f[i+4]-8*f[i+2]+128*f[i+1]-128*f[i-1]+8*f[i-2]+f[i-4])/180; // 6
}
// thin plasma tube
int main(int argc, char** argv)
{
   double dt=1e-6*ns; // large dt causes E to decrease too fast
   double dx=0.5*nm; // small dx may cause artificial oscillation
   double Ee=1000*volt/cm;
   double R=1e-6*cm, mean=0, sigma=R/3, height=300./(Pi()*R*R);
   bool norm;

   GeCrystal ge;
   double epsilon=ge.Epsilon()*epsilon0;
   double Q=Abs(electron_charge);
   //double De=0.046*cm2/s, Dh=0.046*cm2/s;

   // initialize arrays
   // p and n are number densities
   double x[2*N+1], p[2*N+1], n[2*N+1], E[2*N+1], pE[2*N+1],nE[2*N+1];
   if (argc==3) {
      TChain *ti = new TChain("t");
      ti->Add(argv[2]);
      ti->SetBranchAddress("x",x);
      ti->SetBranchAddress("p",p);
      ti->SetBranchAddress("n",n);
      ti->SetBranchAddress("E",E);
      ti->GetEntry(ti->GetEntries()-1);
   } else {
      for (int i=0; i<2*N+1; i++) {
         x[i]=(i-N)*dx;
         p[i]=n[i]=height*Gaus(x[i],mean=0,sigma,norm=kTRUE);
         if (p[i]<1e-5/cm3) p[i]=0;
         if (n[i]<1e-5/cm3) n[i]=0;
         E[i]=Ee;
      }
   }
   for (int i=0; i<2*N+1; i++) {
      pE[i]=p[i]*E[i];
      nE[i]=n[i]*E[i];
   }

   // output
   TFile *output = new TFile("tube.root","recreate");
   TTree *t = new TTree("t","time slices");
   t->Branch("x",x,Form("x[%d]/D",2*N+1));
   t->Branch("p",p,Form("p[%d]/D",2*N+1));
   t->Branch("n",n,Form("n[%d]/D",2*N+1));
   t->Branch("E",E,Form("E[%d]/D",2*N+1));
   // save units into tree
   double CM3=cm3, CM2=cm2, CM=cm, UM=um, V=volt, SEC=s, NS=ns, NM=nm;
   t->Branch("cm3",&CM3,"cm3/D");
   t->Branch("cm2",&CM2,"cm2/D");
   t->Branch("V",&V,"V/D");
   t->Branch("cm",&CM,"cm/D");
   t->Branch("um",&UM,"um/D");
   t->Branch("nm",&NM,"nm/D");
   t->Branch("ns",&NS,"ns/D");
   t->Branch("s",&SEC,"s/D");

   int iStep=0, nSteps = 100;

   // evolve
   if (argc>1) nSteps = atoi(argv[1]);
   while (iStep<nSteps) {
      if (iStep%100==0) cout<<iStep<<" steps passed"<<endl;
      t->Fill();
      // update electron and hole distributions
      for (int i=0; i<2*N+1; i++) {
         //double dn_dt = ge.Mu('e', n[i])*(dn[i]/dx*E[i]+n[i]*dE[i]/dx);
         //double dp_dt = -ge.Mu('h', p[i])*(dp[i]/dx*E[i]+p[i]*dE[i]/dx);
         double dn_dt = ge.Mu('e', n[i])*d(nE,i)/dx;
         double dp_dt = -ge.Mu('h', p[i])*d(pE,i)/dx;
         if (Abs(dn_dt*dt)<1e-10/cm3) dn_dt=0;
         if (Abs(dp_dt*dt)<1e-10/cm3) dp_dt=0;
         n[i]+=dn_dt*dt;
         p[i]+=dp_dt*dt;
         if (n[i]<0) n[i]=0;
         if (p[i]<0) p[i]=0;
      }
      // update electric field distribution
      for (int i=0; i<2*N+1; i++) {
         E[i]=0;
         for (int j=0; j<i; j++)
            E[i]+=(p[j]-n[j])*(1-(i-j)*dx/sqrt((i-j)*(i-j)*dx*dx+R*R));
         for (int j=i+1; j<2*N+1; j++)
            E[i]+=(n[j]-p[j])*(1-(j-i)*dx/sqrt((j-i)*(j-i)*dx*dx+R*R));
         E[i]*=Q*dx/2/epsilon;
         E[i]+=Ee;
         if (E[i]<1e-10*volt/cm) E[i]=0; // large dt may over evolve things
      }

      // update pE[i]and nE[i]
      for (int i=0; i<2*N+1; i++){
         pE[i] = p[i]*E[i];
         nE[i] = n[i]*E[i];
      }

      iStep++;
   }
   t->Write("t",6);

   output->Close();

   return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012 Google
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __BASE_TRIE_HH__
#define __BASE_TRIE_HH__

#include <cassert>
#include <iostream>

#include "base/cprintf.hh"
#include "base/logging.hh"
#include "base/types.hh"

// Key has to be an integral type.
template <class Key, class Value>
class Trie
{
  protected:
    struct Node
    {
        Key key;
        Key mask;

        bool
        matches(Key test)
        {
            return (test & mask) == key;
        }

        Value *value;

        Node *parent;
        Node *kids[2];

        Node(Key _key, Key _mask, Value *_val) :
            key(_key & _mask), mask(_mask), value(_val),
            parent(NULL)
        {
            kids[0] = NULL;
            kids[1] = NULL;
        }

        void
        clear()
        {
            if (kids[1]) {
                kids[1]->clear();
                delete kids[1];
                kids[1] = NULL;
            }
            if (kids[0]) {
                kids[0]->clear();
                delete kids[0];
                kids[0] = NULL;
            }
        }

        void
        dump(std::ostream &os, int level)
        {
            for (int i = 1; i < level; i++) {
                ccprintf(os, "|");
            }
            if (level == 0)
                ccprintf(os, "Root ");
            else
                ccprintf(os, "+ ");
            ccprintf(os, "(%p, %p, %#X, %#X, %p)\n",
                     parent, this, key, mask, value);
            if (kids[0])
                kids[0]->dump(os, level + 1);
            if (kids[1])
                kids[1]->dump(os, level + 1);
        }
    };

  protected:
    Node head;

  public:
    typedef Node *Handle;

    Trie() : head(0, 0, NULL)
    {}

    static const unsigned MaxBits = sizeof(Key) * 8;

  private:
    /**
     * A utility method which checks whether the key being looked up lies
     * beyond the Node being examined. If so, it returns true and advances the
     * node being examined.
     * @param parent The node we're currently "at", which can be updated.
     * @param kid The node we may want to move to.
     * @param key The key we're looking for.
     * @param new_mask The mask to use when matching against the key.
     * @return Whether the current Node was advanced.
     */
    bool
    goesAfter(Node **parent, Node *kid, Key key, Key new_mask)
    {
        if (kid && kid->matches(key) && (kid->mask & new_mask) == kid->mask) {
            *parent = kid;
            return true;
        } else {
            return false;
        }
    }

    /**
     * A utility method which extends a mask value one more bit towards the
     * lsb. This is almost just a signed right shift, except that the shifted
     * in bits are technically undefined. This is also slightly complicated by
     * the zero case.
     * @param orig The original mask to extend.
     * @return The extended mask.
     */
    Key
    extendMask(Key orig)
    {
        // Just in case orig was 0.
        const Key msb = ULL(1) << (MaxBits - 1);
        return orig | (orig >> 1) | msb;
    }

    /**
     * Method which looks up the Handle corresponding to a particular key. This
     * is useful if you want to delete the Handle corresponding to a key since
     * the "remove" function takes a Handle as its argument.
     * @param key The key to look up.
     * @return The first Handle matching this key, or NULL if none was found.
     */
    Handle
    lookupHandle(Key key)
    {
        Node *node = &head;
        while (node) {
            if (node->value)
                return node;

            if (node->kids[0] && node->kids[0]->matches(key))
                node = node->kids[0];
            else if (node->kids[1] && node->kids[1]->matches(key))
                node = node->kids[1];
            else
                node = NULL;
        }

        return NULL;
    }

  public:
    /**
     * Method which inserts a key/value pair into the trie.
     * @param key The key which can later be used to look up this value.
     * @param width How many bits of the key (from msb) should be used.
     * @param val A pointer to the value to store in the trie.
     * @return A Handle corresponding to this value.
     */
    Handle
    insert(Key key, unsigned width, Value *val)
    {
        // We use NULL value pointers to mark internal nodes of the trie, so
        // we don't allow inserting them as real values.
        assert(val);

        // Build a mask which masks off all the bits we don't care about.
        Key new_mask = ~(Key)0;
        if (width < MaxBits)
            new_mask <<= (MaxBits - width);
        // Use it to tidy up the key.
        key &= new_mask;

        // Walk past all the nodes this new node will be inserted after. They
        // can be ignored for the purposes of this function.
        Node *node = &head;
        while (goesAfter(&node, node->kids[0], key, new_mask) ||
               goesAfter(&node, node->kids[1], key, new_mask))
        {}
        assert(node);

        Key cur_mask = node->mask;
        // If we're already where the value needs to be...
        if (cur_mask == new_mask) {
            assert(!node->value);
            node->value = val;
            return node;
        }

        for (unsigned int i = 0; i < 2; i++) {
            Node *&kid = node->kids[i];
            Node *new_node;
            if (!kid) {
                // No kid. Add a new one.
                new_node = new Node(key, new_mask, val);
                new_node->parent = node;
                kid = new_node;
                return new_node;
            }

            // Walk down the leg until something doesn't match or we run out
            // of bits.
            Key last_mask;
            bool done;
            do {
                last_mask = cur_mask;
                cur_mask = extendMask(cur_mask);
                done = ((key & cur_mask) != (kid->key & cur_mask)) ||
                    last_mask == new_mask;
            } while (!done);
            cur_mask = last_mask;

            // If this isn't the right leg to go down at all, skip it.
            if (cur_mask == node->mask)
                continue;

            // At the point we walked to above, add a new node.
            new_node = new Node(key, cur_mask, NULL);
            new_node->parent = node;
            kid->parent = new_node;
            new_node->kids[0] = kid;
            kid = new_node;

            // If we ran out of bits, the value goes right here.
            if (cur_mask == new_mask) {
                new_node->value = val;
                return new_node;
            }

            // Still more bits to deal with, so add a new node for that path.
            new_node = new Node(key, new_mask, val);
            new_node->parent = kid;
            kid->kids[1] = new_node;
            return new_node;
        }

        panic("Reached the end of the Trie insert function!\n");
        return NULL;
    }

    /**
     * Method which looks up the Value corresponding to a particular key.
     * @param key The key to look up.
     * @return The first Value matching this key, or NULL if none was found.
     */
    Value *
    lookup(Key key)
    {
        Node *node = lookupHandle(key);
        if (node)
            return node->value;
        else
            return NULL;
    }

    /**
     * Method to delete a value from the trie.
     * @param node A Handle to remove.
     * @return The Value pointer from the removed entry.
     */
    Value *
    remove(Handle handle)
    {
        Node *node = handle;
        Value *val = node->value;
        if (node->kids[1]) {
            assert(node->value);
            node->value = NULL;
            return val;
        }
        if (!node->parent)
            panic("Trie: Can't remove root node.\n");

        Node *parent = node->parent;

        // If there's a kid, fix up it's parent pointer.
        if (node->kids[0])
            node->kids[0]->parent = parent;
        // Figure out which kid we are, and update our parent's pointers.
        if (parent->kids[0] == node)
            parent->kids[0] = node->kids[0];
        else if (parent->kids[1] == node)
            parent->kids[1] = node->kids[0];
        else
            panic("Trie: Inconsistent parent/kid relationship.\n");
        // Make sure if the parent only has one kid, it's kid[0].
        if (parent->kids[1] && !parent->kids[0]) {
            parent->kids[0] = parent->kids[1];
            parent->kids[1] = NULL;
        }

        // If the parent has less than two kids and no cargo and isn't the
        // root, delete it too.
        if (!parent->kids[1] && !parent->value && parent->parent)
            remove(parent);
        delete node;
        return val;
    }

    /**
     * Method to lookup a value from the trie and then delete it.
     * @param key The key to look up and then remove.
     * @return The Value pointer from the removed entry, if any.
     */
    Value *
    remove(Key key)
    {
        Handle handle = lookupHandle(key);
        if (!handle)
            return NULL;
        return remove(handle);
    }

    /**
     * A method which removes all key/value pairs from the trie. This is more
     * efficient than trying to remove elements individually.
     */
    void
    clear()
    {
        head.clear();
    }

    /**
     * A debugging method which prints the contents of this trie.
     * @param title An identifying title to put in the dump header.
     */
    void
    dump(const char *title, std::ostream &os=std::cout)
    {
        ccprintf(os, "**************************************************\n");
        ccprintf(os, "*** Start of Trie: %s\n", title);
        ccprintf(os, "*** (parent, me, key, mask, value pointer)\n");
        ccprintf(os, "**************************************************\n");
        head.dump(os, 0);
    }
};

#endif
<commit_msg>base: Add static assert to trie<commit_after>/*
 * Copyright (c) 2012 Google
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __BASE_TRIE_HH__
#define __BASE_TRIE_HH__

#include <cassert>
#include <iostream>
#include <type_traits>

#include "base/cprintf.hh"
#include "base/logging.hh"
#include "base/types.hh"

/**
 * A trie is a tree-based data structure used for data retrieval. It uses
 * bits masked from the msb of the key to to determine a value's location,
 * so its lookups have their worst case time dictated by the key's size.
 *
 * @tparam Key Type of the key of the tree nodes. Must be an integral type.
 * @tparam Value Type of the values associated to the keys.
 */
template <class Key, class Value>
class Trie
{
  protected:
    static_assert(std::is_integral<Key>::value,
        "Key has to be an integral type");

    struct Node
    {
        Key key;
        Key mask;

        bool
        matches(Key test)
        {
            return (test & mask) == key;
        }

        Value *value;

        Node *parent;
        Node *kids[2];

        Node(Key _key, Key _mask, Value *_val) :
            key(_key & _mask), mask(_mask), value(_val),
            parent(NULL)
        {
            kids[0] = NULL;
            kids[1] = NULL;
        }

        void
        clear()
        {
            if (kids[1]) {
                kids[1]->clear();
                delete kids[1];
                kids[1] = NULL;
            }
            if (kids[0]) {
                kids[0]->clear();
                delete kids[0];
                kids[0] = NULL;
            }
        }

        void
        dump(std::ostream &os, int level)
        {
            for (int i = 1; i < level; i++) {
                ccprintf(os, "|");
            }
            if (level == 0)
                ccprintf(os, "Root ");
            else
                ccprintf(os, "+ ");
            ccprintf(os, "(%p, %p, %#X, %#X, %p)\n",
                     parent, this, key, mask, value);
            if (kids[0])
                kids[0]->dump(os, level + 1);
            if (kids[1])
                kids[1]->dump(os, level + 1);
        }
    };

  protected:
    Node head;

  public:
    typedef Node *Handle;

    Trie() : head(0, 0, NULL)
    {}

    static const unsigned MaxBits = sizeof(Key) * 8;

  private:
    /**
     * A utility method which checks whether the key being looked up lies
     * beyond the Node being examined. If so, it returns true and advances the
     * node being examined.
     * @param parent The node we're currently "at", which can be updated.
     * @param kid The node we may want to move to.
     * @param key The key we're looking for.
     * @param new_mask The mask to use when matching against the key.
     * @return Whether the current Node was advanced.
     */
    bool
    goesAfter(Node **parent, Node *kid, Key key, Key new_mask)
    {
        if (kid && kid->matches(key) && (kid->mask & new_mask) == kid->mask) {
            *parent = kid;
            return true;
        } else {
            return false;
        }
    }

    /**
     * A utility method which extends a mask value one more bit towards the
     * lsb. This is almost just a signed right shift, except that the shifted
     * in bits are technically undefined. This is also slightly complicated by
     * the zero case.
     * @param orig The original mask to extend.
     * @return The extended mask.
     */
    Key
    extendMask(Key orig)
    {
        // Just in case orig was 0.
        const Key msb = ULL(1) << (MaxBits - 1);
        return orig | (orig >> 1) | msb;
    }

    /**
     * Method which looks up the Handle corresponding to a particular key. This
     * is useful if you want to delete the Handle corresponding to a key since
     * the "remove" function takes a Handle as its argument.
     * @param key The key to look up.
     * @return The first Handle matching this key, or NULL if none was found.
     */
    Handle
    lookupHandle(Key key)
    {
        Node *node = &head;
        while (node) {
            if (node->value)
                return node;

            if (node->kids[0] && node->kids[0]->matches(key))
                node = node->kids[0];
            else if (node->kids[1] && node->kids[1]->matches(key))
                node = node->kids[1];
            else
                node = NULL;
        }

        return NULL;
    }

  public:
    /**
     * Method which inserts a key/value pair into the trie.
     * @param key The key which can later be used to look up this value.
     * @param width How many bits of the key (from msb) should be used.
     * @param val A pointer to the value to store in the trie.
     * @return A Handle corresponding to this value.
     */
    Handle
    insert(Key key, unsigned width, Value *val)
    {
        // We use NULL value pointers to mark internal nodes of the trie, so
        // we don't allow inserting them as real values.
        assert(val);

        // Build a mask which masks off all the bits we don't care about.
        Key new_mask = ~(Key)0;
        if (width < MaxBits)
            new_mask <<= (MaxBits - width);
        // Use it to tidy up the key.
        key &= new_mask;

        // Walk past all the nodes this new node will be inserted after. They
        // can be ignored for the purposes of this function.
        Node *node = &head;
        while (goesAfter(&node, node->kids[0], key, new_mask) ||
               goesAfter(&node, node->kids[1], key, new_mask))
        {}
        assert(node);

        Key cur_mask = node->mask;
        // If we're already where the value needs to be...
        if (cur_mask == new_mask) {
            assert(!node->value);
            node->value = val;
            return node;
        }

        for (unsigned int i = 0; i < 2; i++) {
            Node *&kid = node->kids[i];
            Node *new_node;
            if (!kid) {
                // No kid. Add a new one.
                new_node = new Node(key, new_mask, val);
                new_node->parent = node;
                kid = new_node;
                return new_node;
            }

            // Walk down the leg until something doesn't match or we run out
            // of bits.
            Key last_mask;
            bool done;
            do {
                last_mask = cur_mask;
                cur_mask = extendMask(cur_mask);
                done = ((key & cur_mask) != (kid->key & cur_mask)) ||
                    last_mask == new_mask;
            } while (!done);
            cur_mask = last_mask;

            // If this isn't the right leg to go down at all, skip it.
            if (cur_mask == node->mask)
                continue;

            // At the point we walked to above, add a new node.
            new_node = new Node(key, cur_mask, NULL);
            new_node->parent = node;
            kid->parent = new_node;
            new_node->kids[0] = kid;
            kid = new_node;

            // If we ran out of bits, the value goes right here.
            if (cur_mask == new_mask) {
                new_node->value = val;
                return new_node;
            }

            // Still more bits to deal with, so add a new node for that path.
            new_node = new Node(key, new_mask, val);
            new_node->parent = kid;
            kid->kids[1] = new_node;
            return new_node;
        }

        panic("Reached the end of the Trie insert function!\n");
        return NULL;
    }

    /**
     * Method which looks up the Value corresponding to a particular key.
     * @param key The key to look up.
     * @return The first Value matching this key, or NULL if none was found.
     */
    Value *
    lookup(Key key)
    {
        Node *node = lookupHandle(key);
        if (node)
            return node->value;
        else
            return NULL;
    }

    /**
     * Method to delete a value from the trie.
     * @param node A Handle to remove.
     * @return The Value pointer from the removed entry.
     */
    Value *
    remove(Handle handle)
    {
        Node *node = handle;
        Value *val = node->value;
        if (node->kids[1]) {
            assert(node->value);
            node->value = NULL;
            return val;
        }
        if (!node->parent)
            panic("Trie: Can't remove root node.\n");

        Node *parent = node->parent;

        // If there's a kid, fix up it's parent pointer.
        if (node->kids[0])
            node->kids[0]->parent = parent;
        // Figure out which kid we are, and update our parent's pointers.
        if (parent->kids[0] == node)
            parent->kids[0] = node->kids[0];
        else if (parent->kids[1] == node)
            parent->kids[1] = node->kids[0];
        else
            panic("Trie: Inconsistent parent/kid relationship.\n");
        // Make sure if the parent only has one kid, it's kid[0].
        if (parent->kids[1] && !parent->kids[0]) {
            parent->kids[0] = parent->kids[1];
            parent->kids[1] = NULL;
        }

        // If the parent has less than two kids and no cargo and isn't the
        // root, delete it too.
        if (!parent->kids[1] && !parent->value && parent->parent)
            remove(parent);
        delete node;
        return val;
    }

    /**
     * Method to lookup a value from the trie and then delete it.
     * @param key The key to look up and then remove.
     * @return The Value pointer from the removed entry, if any.
     */
    Value *
    remove(Key key)
    {
        Handle handle = lookupHandle(key);
        if (!handle)
            return NULL;
        return remove(handle);
    }

    /**
     * A method which removes all key/value pairs from the trie. This is more
     * efficient than trying to remove elements individually.
     */
    void
    clear()
    {
        head.clear();
    }

    /**
     * A debugging method which prints the contents of this trie.
     * @param title An identifying title to put in the dump header.
     */
    void
    dump(const char *title, std::ostream &os=std::cout)
    {
        ccprintf(os, "**************************************************\n");
        ccprintf(os, "*** Start of Trie: %s\n", title);
        ccprintf(os, "*** (parent, me, key, mask, value pointer)\n");
        ccprintf(os, "**************************************************\n");
        head.dump(os, 0);
    }
};

#endif
<|endoftext|>


<commit_before>/*
Copyright (c) 2009 Sony Pictures Imageworks, et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of color operations.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"

#include "OpenImageIO/varyingref.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

extern DECLOP (triple_ctr);   // definition is elsewhere

namespace {

static Color3
hsv_to_rgb (float h, float s, float v)
{
    // Reference for this technique: Foley & van Dam
    if (s < 0.0001f) {
      return Color3 (v, v, v);
    } else {
        h = 6.0f * (h - floorf(h));  // expand to [0..6)
        int hi = (int) h;
        float f = h - hi;
        float p = v * (1.0f-s);
        float q = v * (1.0f-s*f);
        float t = v * (1.0f-s*(1.0f-f));
        switch (hi) {
        case 0 : return Color3 (v, t, p);
        case 1 : return Color3 (q, v, p);
        case 2 : return Color3 (p, v, t);
        case 3 : return Color3 (p, q, v);
        case 4 : return Color3 (t, p, v);
        default: return Color3 (v, p, q);
	}
    }
}

static Color3
hsl_to_rgb (float h, float s, float l)
{
    // Easiest to convert hsl -> hsv, then hsv -> RGB (per Foley & van Dam)
    float v = (l <= 0.5) ? (l * (1.0f + s)) : (l * (1.0f - s) + s);
    if (v <= 0.0f) {
        return Color3 (0.0f, 0.0f, 0.0f);
    } else {
	float min = 2.0f * l - v;
	s = (v - min) / v;
	return hsv_to_rgb (h, s, v);
    }
}

static Color3
YIQ_to_rgb (float Y, float I, float Q)
{
    return Color3 (Y + 0.9557f * I + 0.6199f * Q,
                   Y - 0.2716f * I - 0.6469f * Q,
                   Y - 1.1082f * I + 1.7051f * Q);
}

static Color3
xyz_to_rgb (float x, float y, float z)
{
    return Color3 ( 3.240479f * x + -1.537150f * y + -0.498535f * z,
                   -0.969256f * x +  1.875991f * y +  0.041556f * z,
                    0.055648f * x + -0.204043f * y +  1.057311f * z);
}

static Color3
to_rgb (ustring fromspace, float a, float b, float c, ShadingExecution *exec)
{
    if (fromspace == Strings::RGB || fromspace == Strings::rgb)
        return Color3 (a, b, c);
    if (fromspace == Strings::hsv)
        return hsv_to_rgb (a, b, c);
    if (fromspace == Strings::hsl)
        return hsl_to_rgb (a, b, c);
    if (fromspace == Strings::YIQ)
        return YIQ_to_rgb (a, b, c);
    if (fromspace == Strings::xyz)
        return xyz_to_rgb (a, b, c);

    exec->error ("Unknown color space \"%s\"", fromspace.c_str());
    return Color3 (a, b, c);
}

class Luminance {
public:
    Luminance (ShadingExecution *) { }
    void operator() (float &result, const Color3 &c) {
       result = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
    }
};

/// Implementation of the constructor "color (string, float, float, float)".
///
DECLOP (color_ctr_transform)
{
    Symbol &Result (exec->sym (args[0]));
    Symbol &Space (exec->sym (args[1]));
    Symbol &X (exec->sym (args[2]));
    Symbol &Y (exec->sym (args[3]));
    Symbol &Z (exec->sym (args[4]));

    // Adjust the result's uniform/varying status
    bool vary = (Space.is_varying() |
                 X.is_varying() | Y.is_varying() | Z.is_varying() );
    exec->adjust_varying (Result, vary, false /* can't alias */);

    // FIXME -- clear derivs for now, make it right later.
    if (Result.has_derivs ())
        exec->zero_derivs (Result);

    VaryingRef<Color3> result ((Color3 *)Result.data(), Result.step());
    VaryingRef<ustring> space ((ustring *)Space.data(), Space.step());
    VaryingRef<float> x ((float *)X.data(), X.step());
    VaryingRef<float> y ((float *)Y.data(), Y.step());
    VaryingRef<float> z ((float *)Z.data(), Z.step());

    Matrix44 M;
    if (result.is_uniform()) {
        // Everything is uniform
        *result = to_rgb (*space, *x, *y, *z, exec);
    } else if (! vary) {
        // Result is varying, but everything else is uniform
        Color3 r = to_rgb (*space, *x, *y, *z, exec);
        for (int i = beginpoint;  i < endpoint;  ++i)
            if (runflags[i])
                result[i] = r;
    } else {
        // Fully varying case
        for (int i = beginpoint;  i < endpoint;  ++i)
            if (runflags[i])
                result[i] = to_rgb (space[i], x[i], y[i], z[i], exec);
    }
}

};  // End anonymous namespace

DECLOP (OP_color)
{
    DASSERT (nargs == 4 || nargs == 5);
    Symbol &Result (exec->sym (args[0]));
    bool using_space = (nargs == 5);
    Symbol &Space (exec->sym (args[1]));
    Symbol &X (exec->sym (args[1+using_space]));
    Symbol &Y (exec->sym (args[2+using_space]));
    Symbol &Z (exec->sym (args[3+using_space]));
    DASSERT (! Result.typespec().is_closure() &&
            ! X.typespec().is_closure() && ! Y.typespec().is_closure() &&
            ! Z.typespec().is_closure() && ! Space.typespec().is_closure());

    // We allow two flavors: point = point(float,float,float) and
    // point = point(string,float,float,float)
    if (Result.typespec().is_triple() && X.typespec().is_float() &&
          Y.typespec().is_float() && Z.typespec().is_float() &&
          (using_space == false || Space.typespec().is_string())) {
        OpImpl impl = NULL;
        if (using_space)
            impl = color_ctr_transform;  // special case: colors different
        else
            impl = triple_ctr;
        impl (exec, nargs, args, runflags, beginpoint, endpoint);
        // Use the specialized one for next time!  Never have to check the
        // types or do the other sanity checks again.
        // FIXME -- is this thread-safe?
        exec->op().implementation (impl);
    } else {
        exec->error_arg_types ();
        ASSERT (0 && "Function arg type can't be handled");
    }
}

DECLOP (OP_luminance)
{
    DASSERT (nargs == 2);
    Symbol &Result (exec->sym (args[0]));
    Symbol &C (exec->sym (args[1]));
    DASSERT (! Result.typespec().is_closure() && ! C.typespec().is_closure());
    DASSERT (Result.typespec().is_float() && C.typespec().is_triple());

    unary_op_guts_noderivs<Float, Color3, Luminance> (Result, C, exec, runflags,
                                                      beginpoint, endpoint);
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<commit_msg>Adding derivatives to luminance() (trac#140)<commit_after>/*
Copyright (c) 2009 Sony Pictures Imageworks, et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/////////////////////////////////////////////////////////////////////////
/// \file
///
/// Shader interpreter implementation of color operations.
///
/////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "oslexec_pvt.h"
#include "oslops.h"
#include "dual.h"

#include "OpenImageIO/varyingref.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif
namespace OSL {
namespace pvt {

extern DECLOP (triple_ctr);   // definition is elsewhere

namespace {

static Color3
hsv_to_rgb (float h, float s, float v)
{
    // Reference for this technique: Foley & van Dam
    if (s < 0.0001f) {
      return Color3 (v, v, v);
    } else {
        h = 6.0f * (h - floorf(h));  // expand to [0..6)
        int hi = (int) h;
        float f = h - hi;
        float p = v * (1.0f-s);
        float q = v * (1.0f-s*f);
        float t = v * (1.0f-s*(1.0f-f));
        switch (hi) {
        case 0 : return Color3 (v, t, p);
        case 1 : return Color3 (q, v, p);
        case 2 : return Color3 (p, v, t);
        case 3 : return Color3 (p, q, v);
        case 4 : return Color3 (t, p, v);
        default: return Color3 (v, p, q);
	}
    }
}

static Color3
hsl_to_rgb (float h, float s, float l)
{
    // Easiest to convert hsl -> hsv, then hsv -> RGB (per Foley & van Dam)
    float v = (l <= 0.5) ? (l * (1.0f + s)) : (l * (1.0f - s) + s);
    if (v <= 0.0f) {
        return Color3 (0.0f, 0.0f, 0.0f);
    } else {
	float min = 2.0f * l - v;
	s = (v - min) / v;
	return hsv_to_rgb (h, s, v);
    }
}

static Color3
YIQ_to_rgb (float Y, float I, float Q)
{
    return Color3 (Y + 0.9557f * I + 0.6199f * Q,
                   Y - 0.2716f * I - 0.6469f * Q,
                   Y - 1.1082f * I + 1.7051f * Q);
}

static Color3
xyz_to_rgb (float x, float y, float z)
{
    return Color3 ( 3.240479f * x + -1.537150f * y + -0.498535f * z,
                   -0.969256f * x +  1.875991f * y +  0.041556f * z,
                    0.055648f * x + -0.204043f * y +  1.057311f * z);
}

static Color3
to_rgb (ustring fromspace, float a, float b, float c, ShadingExecution *exec)
{
    if (fromspace == Strings::RGB || fromspace == Strings::rgb)
        return Color3 (a, b, c);
    if (fromspace == Strings::hsv)
        return hsv_to_rgb (a, b, c);
    if (fromspace == Strings::hsl)
        return hsl_to_rgb (a, b, c);
    if (fromspace == Strings::YIQ)
        return YIQ_to_rgb (a, b, c);
    if (fromspace == Strings::xyz)
        return xyz_to_rgb (a, b, c);

    exec->error ("Unknown color space \"%s\"", fromspace.c_str());
    return Color3 (a, b, c);
}

class Luminance {
public:
    Luminance (ShadingExecution *) { }
    void operator() (float &result, const Color3 &c) {
        result = 0.2126f * c[0] + 0.7152f * c[1] + 0.0722f * c[2];
    }
    void operator() (Dual2<float> &result, const Dual2<Color3> &c) {
        Dual2<float> c0 (c.val()[0], c.dx()[0], c.dy()[0]);
        Dual2<float> c1 (c.val()[1], c.dx()[1], c.dy()[1]);
        Dual2<float> c2 (c.val()[2], c.dx()[2], c.dy()[2]);
        result = 0.2126f * c0 + 0.7152f * c1 + 0.0722f * c2;
    }
};

/// Implementation of the constructor "color (string, float, float, float)".
///
DECLOP (color_ctr_transform)
{
    Symbol &Result (exec->sym (args[0]));
    Symbol &Space (exec->sym (args[1]));
    Symbol &X (exec->sym (args[2]));
    Symbol &Y (exec->sym (args[3]));
    Symbol &Z (exec->sym (args[4]));

    // Adjust the result's uniform/varying status
    bool vary = (Space.is_varying() |
                 X.is_varying() | Y.is_varying() | Z.is_varying() );
    exec->adjust_varying (Result, vary, false /* can't alias */);

    // FIXME -- clear derivs for now, make it right later.
    if (Result.has_derivs ())
        exec->zero_derivs (Result);

    VaryingRef<Color3> result ((Color3 *)Result.data(), Result.step());
    VaryingRef<ustring> space ((ustring *)Space.data(), Space.step());
    VaryingRef<float> x ((float *)X.data(), X.step());
    VaryingRef<float> y ((float *)Y.data(), Y.step());
    VaryingRef<float> z ((float *)Z.data(), Z.step());

    Matrix44 M;
    if (result.is_uniform()) {
        // Everything is uniform
        *result = to_rgb (*space, *x, *y, *z, exec);
    } else if (! vary) {
        // Result is varying, but everything else is uniform
        Color3 r = to_rgb (*space, *x, *y, *z, exec);
        for (int i = beginpoint;  i < endpoint;  ++i)
            if (runflags[i])
                result[i] = r;
    } else {
        // Fully varying case
        for (int i = beginpoint;  i < endpoint;  ++i)
            if (runflags[i])
                result[i] = to_rgb (space[i], x[i], y[i], z[i], exec);
    }
}

};  // End anonymous namespace

DECLOP (OP_color)
{
    DASSERT (nargs == 4 || nargs == 5);
    Symbol &Result (exec->sym (args[0]));
    bool using_space = (nargs == 5);
    Symbol &Space (exec->sym (args[1]));
    Symbol &X (exec->sym (args[1+using_space]));
    Symbol &Y (exec->sym (args[2+using_space]));
    Symbol &Z (exec->sym (args[3+using_space]));
    DASSERT (! Result.typespec().is_closure() &&
            ! X.typespec().is_closure() && ! Y.typespec().is_closure() &&
            ! Z.typespec().is_closure() && ! Space.typespec().is_closure());

    // We allow two flavors: point = point(float,float,float) and
    // point = point(string,float,float,float)
    if (Result.typespec().is_triple() && X.typespec().is_float() &&
          Y.typespec().is_float() && Z.typespec().is_float() &&
          (using_space == false || Space.typespec().is_string())) {
        OpImpl impl = NULL;
        if (using_space)
            impl = color_ctr_transform;  // special case: colors different
        else
            impl = triple_ctr;
        impl (exec, nargs, args, runflags, beginpoint, endpoint);
        // Use the specialized one for next time!  Never have to check the
        // types or do the other sanity checks again.
        // FIXME -- is this thread-safe?
        exec->op().implementation (impl);
    } else {
        exec->error_arg_types ();
        ASSERT (0 && "Function arg type can't be handled");
    }
}

DECLOP (OP_luminance)
{
    DASSERT (nargs == 2);
    Symbol &Result (exec->sym (args[0]));
    Symbol &C (exec->sym (args[1]));
    DASSERT (! Result.typespec().is_closure() && ! C.typespec().is_closure());
    DASSERT (Result.typespec().is_float() && C.typespec().is_triple());

    unary_op_guts<Float, Color3, Luminance> (Result, C, exec, runflags, beginpoint, endpoint);
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2012 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "config.h"

#include <algorithm>
#include <vector>

#include "bgfetcher.h"
#include "ep.h"
#include "kvshard.h"
#include "executorthread.h"

const double BgFetcher::sleepInterval = MIN_SLEEP_TIME;

void BgFetcher::start() {
    bool inverse = false;
    pendingFetch.compare_exchange_strong(inverse, true);
    ExecutorPool* iom = ExecutorPool::get();
    ExTask task = new BgFetcherTask(&(store->getEPEngine()), this,
                                      Priority::BgFetcherPriority, false);
    this->setTaskId(task->getId());
    iom->schedule(task, READER_TASK_IDX);
    assert(taskId > 0);
}

void BgFetcher::stop() {
    bool inverse = true;
    pendingFetch.compare_exchange_strong(inverse, false);
    assert(taskId > 0);
    ExecutorPool::get()->cancel(taskId);
}

void BgFetcher::notifyBGEvent(void) {
    ++stats.numRemainingBgJobs;
    bool inverse = false;
    if (pendingFetch.compare_exchange_strong(inverse, true)) {
        assert(taskId > 0);
        ExecutorPool::get()->wake(taskId);
    }
}

size_t BgFetcher::doFetch(uint16_t vbId) {
    hrtime_t startTime(gethrtime());
    LOG(EXTENSION_LOG_DEBUG, "BgFetcher is fetching data, vBucket = %d "
        "numDocs = %d, startTime = %lld\n", vbId, items2fetch.size(),
        startTime/1000000);

    shard->getROUnderlying()->getMulti(vbId, items2fetch);

    size_t totalfetches = 0;
    std::vector<bgfetched_item_t> fetchedItems;
    vb_bgfetch_queue_t::iterator itr = items2fetch.begin();
    for (; itr != items2fetch.end(); ++itr) {
        const std::string &key = (*itr).first;
        std::list<VBucketBGFetchItem *> &requestedItems = (*itr).second;
        std::list<VBucketBGFetchItem *>::iterator itm = requestedItems.begin();
        for(; itm != requestedItems.end(); ++itm) {
            if ((*itm)->value.getStatus() == ENGINE_TMPFAIL &&
                (*itm)->canRetry()) {
                // underlying kvstore failed to fetch requested data
                // don't return the failed request yet. Will requeue
                // it for retry later
                LOG(EXTENSION_LOG_WARNING,
                        "Warning: bgfetcher failed to fetch data for vb = %d "
                        "key = %s retry = %d\n", vbId, key.c_str(),
                        (*itm)->getRetryCount());
                continue;
            }
            fetchedItems.push_back(std::make_pair(key, *itm));
            ++totalfetches;
        }
    }

    if (totalfetches > 0) {
        store->completeBGFetchMulti(vbId, fetchedItems, startTime);
        stats.getMultiHisto.add((gethrtime()-startTime)/1000, totalfetches);
    }

    // failed requests will get requeued for retry within clearItems()
    clearItems(vbId);
    return totalfetches;
}

void BgFetcher::clearItems(uint16_t vbId) {
    vb_bgfetch_queue_t::iterator itr = items2fetch.begin();

    for(; itr != items2fetch.end(); ++itr) {
        // every fetched item belonging to the same key shares
        // a single data buffer, just delete it from the first fetched item
        std::list<VBucketBGFetchItem *> &doneItems = (*itr).second;
        VBucketBGFetchItem *firstItem = doneItems.front();
        firstItem->delValue();

        std::list<VBucketBGFetchItem *>::iterator dItr = doneItems.begin();
        for (; dItr != doneItems.end(); ++dItr) {
            if ((*dItr)->value.getStatus() != ENGINE_TMPFAIL ||
                !(*dItr)->canRetry()) {
                delete *dItr;
            } else {
                RCPtr<VBucket> vb = store->getVBuckets().getBucket(vbId);
                assert(vb);
                (*dItr)->incrRetryCount();
                LOG(EXTENSION_LOG_DEBUG, "BgFetcher is re-queueing failed "
                    "request for vb = %d key = %s retry = %d\n",
                    vbId, (*itr).first.c_str(), (*dItr)->getRetryCount());
                vb->queueBGFetchItem((*itr).first, *dItr, this);
            }
        }
    }
}

bool BgFetcher::run(size_t tid) {
    assert(tid > 0);
    size_t num_fetched_items = 0;
    bool inverse = true;
    pendingFetch.compare_exchange_strong(inverse, false);

    std::vector<uint16_t> bg_vbs;
    LockHolder lh(queueMutex);
    std::set<uint16_t>::iterator it = pendingVbs.begin();
    for (; it != pendingVbs.end(); ++it) {
        bg_vbs.push_back(*it);
    }
    pendingVbs.clear();
    lh.unlock();

    std::vector<uint16_t>::iterator ita = bg_vbs.begin();
    for (; ita != bg_vbs.end(); ++ita) {
        uint16_t vbId = *ita;
        RCPtr<VBucket> vb = shard->getBucket(vbId);
        if (vb && vb->getBGFetchItems(items2fetch)) {
            num_fetched_items += doFetch(vbId);
            items2fetch.clear();
        }
    }

    stats.numRemainingBgJobs.fetch_sub(num_fetched_items);

    if (!pendingFetch.load()) {
        // wait a bit until next fetch request arrives
        double sleep = std::max(store->getBGFetchDelay(), sleepInterval);
        ExecutorPool::get()->snooze(taskId, sleep);

        if (pendingFetch.load()) {
            // check again a new fetch request could have arrived
            // right before calling above snooze()
            ExecutorPool::get()->snooze(taskId, 0);
        }
    }
    return true;
}

bool BgFetcher::pendingJob() {
    std::vector<int> vbIds = shard->getVBuckets();
    size_t numVbuckets = vbIds.size();
    for (size_t i = 0; i < numVbuckets; ++i) {
        RCPtr<VBucket> vb = shard->getBucket(vbIds[i]);
        if (vb && vb->hasPendingBGFetchItems()) {
            return true;
        }
    }
    return false;
}
<commit_msg>Reschedule the vbucket bg fetch task if its DB file is not created<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2012 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#include "config.h"

#include <algorithm>
#include <vector>

#include "bgfetcher.h"
#include "ep.h"
#include "kvshard.h"
#include "executorthread.h"

const double BgFetcher::sleepInterval = MIN_SLEEP_TIME;

void BgFetcher::start() {
    bool inverse = false;
    pendingFetch.compare_exchange_strong(inverse, true);
    ExecutorPool* iom = ExecutorPool::get();
    ExTask task = new BgFetcherTask(&(store->getEPEngine()), this,
                                      Priority::BgFetcherPriority, false);
    this->setTaskId(task->getId());
    iom->schedule(task, READER_TASK_IDX);
    assert(taskId > 0);
}

void BgFetcher::stop() {
    bool inverse = true;
    pendingFetch.compare_exchange_strong(inverse, false);
    assert(taskId > 0);
    ExecutorPool::get()->cancel(taskId);
}

void BgFetcher::notifyBGEvent(void) {
    ++stats.numRemainingBgJobs;
    bool inverse = false;
    if (pendingFetch.compare_exchange_strong(inverse, true)) {
        assert(taskId > 0);
        ExecutorPool::get()->wake(taskId);
    }
}

size_t BgFetcher::doFetch(uint16_t vbId) {
    hrtime_t startTime(gethrtime());
    LOG(EXTENSION_LOG_DEBUG, "BgFetcher is fetching data, vBucket = %d "
        "numDocs = %d, startTime = %lld\n", vbId, items2fetch.size(),
        startTime/1000000);

    shard->getROUnderlying()->getMulti(vbId, items2fetch);

    size_t totalfetches = 0;
    std::vector<bgfetched_item_t> fetchedItems;
    vb_bgfetch_queue_t::iterator itr = items2fetch.begin();
    for (; itr != items2fetch.end(); ++itr) {
        const std::string &key = (*itr).first;
        std::list<VBucketBGFetchItem *> &requestedItems = (*itr).second;
        std::list<VBucketBGFetchItem *>::iterator itm = requestedItems.begin();
        for(; itm != requestedItems.end(); ++itm) {
            if ((*itm)->value.getStatus() == ENGINE_TMPFAIL &&
                (*itm)->canRetry()) {
                // underlying kvstore failed to fetch requested data
                // don't return the failed request yet. Will requeue
                // it for retry later
                LOG(EXTENSION_LOG_WARNING,
                        "Warning: bgfetcher failed to fetch data for vb = %d "
                        "key = %s retry = %d\n", vbId, key.c_str(),
                        (*itm)->getRetryCount());
                continue;
            }
            fetchedItems.push_back(std::make_pair(key, *itm));
            ++totalfetches;
        }
    }

    if (totalfetches > 0) {
        store->completeBGFetchMulti(vbId, fetchedItems, startTime);
        stats.getMultiHisto.add((gethrtime()-startTime)/1000, totalfetches);
    }

    // failed requests will get requeued for retry within clearItems()
    clearItems(vbId);
    return totalfetches;
}

void BgFetcher::clearItems(uint16_t vbId) {
    vb_bgfetch_queue_t::iterator itr = items2fetch.begin();

    for(; itr != items2fetch.end(); ++itr) {
        // every fetched item belonging to the same key shares
        // a single data buffer, just delete it from the first fetched item
        std::list<VBucketBGFetchItem *> &doneItems = (*itr).second;
        VBucketBGFetchItem *firstItem = doneItems.front();
        firstItem->delValue();

        std::list<VBucketBGFetchItem *>::iterator dItr = doneItems.begin();
        for (; dItr != doneItems.end(); ++dItr) {
            if ((*dItr)->value.getStatus() != ENGINE_TMPFAIL ||
                !(*dItr)->canRetry()) {
                delete *dItr;
            } else {
                RCPtr<VBucket> vb = store->getVBuckets().getBucket(vbId);
                assert(vb);
                (*dItr)->incrRetryCount();
                LOG(EXTENSION_LOG_DEBUG, "BgFetcher is re-queueing failed "
                    "request for vb = %d key = %s retry = %d\n",
                    vbId, (*itr).first.c_str(), (*dItr)->getRetryCount());
                vb->queueBGFetchItem((*itr).first, *dItr, this);
            }
        }
    }
}

bool BgFetcher::run(size_t tid) {
    assert(tid > 0);
    size_t num_fetched_items = 0;
    bool inverse = true;
    pendingFetch.compare_exchange_strong(inverse, false);

    std::vector<uint16_t> bg_vbs;
    LockHolder lh(queueMutex);
    std::set<uint16_t>::iterator it = pendingVbs.begin();
    for (; it != pendingVbs.end(); ++it) {
        bg_vbs.push_back(*it);
    }
    pendingVbs.clear();
    lh.unlock();

    std::vector<uint16_t>::iterator ita = bg_vbs.begin();
    for (; ita != bg_vbs.end(); ++ita) {
        uint16_t vbId = *ita;
        if (store->getVBuckets().isBucketCreation(vbId)) {
            // Requeue the bg fetch task if a vbucket DB file is not
            // created yet.
            lh.lock();
            pendingVbs.insert(vbId);
            lh.unlock();
            bool inverse = false;
            pendingFetch.compare_exchange_strong(inverse, true);
            continue;
        }
        RCPtr<VBucket> vb = shard->getBucket(vbId);
        if (vb && vb->getBGFetchItems(items2fetch)) {
            num_fetched_items += doFetch(vbId);
            items2fetch.clear();
        }
    }

    stats.numRemainingBgJobs.fetch_sub(num_fetched_items);

    if (!pendingFetch.load()) {
        // wait a bit until next fetch request arrives
        double sleep = std::max(store->getBGFetchDelay(), sleepInterval);
        ExecutorPool::get()->snooze(taskId, sleep);

        if (pendingFetch.load()) {
            // check again a new fetch request could have arrived
            // right before calling above snooze()
            ExecutorPool::get()->snooze(taskId, 0);
        }
    }
    return true;
}

bool BgFetcher::pendingJob() {
    std::vector<int> vbIds = shard->getVBuckets();
    size_t numVbuckets = vbIds.size();
    for (size_t i = 0; i < numVbuckets; ++i) {
        RCPtr<VBucket> vb = shard->getBucket(vbIds[i]);
        if (vb && vb->hasPendingBGFetchItems()) {
            return true;
        }
    }
    return false;
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
using namespace std;

#include "assembler.hpp"
#include "common/util.hpp"
#include "cell.hpp"
#include "symbol-ref-cell.hpp"
#include "value-cell.hpp"

static void parseToken(const string &token, int address, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    size_t colonIndex = token.find(':');
    string valueStr(colonIndex == string::npos ? token : token.substr(colonIndex + 1));

    if (colonIndex != string::npos)
    {
        string symbol(token.substr(0, colonIndex));
        string valueStr(token.substr(colonIndex + 1));
        symbolTable.insert(make_pair(symbol, address));
    }

    int value;
    if (tryLexicalCast(valueStr, value))
    {
        cells.push_back(unique_ptr<Cell>(new ValueCell(address, value)));
    }
    else
    {
        cells.push_back(unique_ptr<Cell>(new SymbolRefCell(address, valueStr)));
    }
}

static void tokenizeCodeSection(ifstream &file, int &address, int &lineNumber, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    string line;
    for (; getline(file, line); ++lineNumber)
    {
        if (line.compare(".data") == 0)
        {
            ++lineNumber;
            return;
        }

        string tokens[3];
        istringstream stream(line);
        string token;
        int tokenCount = 0;
        while (stream >> token)
        {
            if (tokenCount >= extent<decltype(tokens)>::value)
            {
                throw runtime_error(Formatter() << "Too many tokens at line " << (lineNumber + 1));
            }
            tokens[tokenCount++] = token;
        }

        if (tokenCount == 0)
        {
            continue;
        }

        switch (tokenCount)
        {
        case 1:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[0], address + 1, symbolTable, cells);
            cells.push_back(unique_ptr<Cell>(new ValueCell(address + 2, address + 3)));
            break;
        case 2:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[1], address + 1, symbolTable, cells);
            cells.push_back(unique_ptr<Cell>(new ValueCell(address + 2, address + 3)));
            break;
        case 3:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[1], address + 1, symbolTable, cells);
            parseToken(tokens[2], address + 2, symbolTable, cells);
            break;
        default:
            throw runtime_error("Unexpected");
        }

        address += 3;
    }
}

static void tokenizeDataSection(ifstream &file, int &address, int &lineNumber, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    string line;
    for (; getline(file, line); ++lineNumber)
    {
        istringstream stream(line);
        string token;
        while (stream >> token)
        {
            parseToken(token, address, symbolTable, cells);
            ++address;
        }
    }
}

static void emit(const map<string, int> &symbolTable, const vector<unique_ptr<Cell>> &cells, vector<char> &ops)
{
    ops.reserve(cells.size());
    for (auto &cell : cells)
    {
        int op = cell->emit(symbolTable);
        ops.push_back(castInt<char>(op));
    }
}

void Assembler::assemble(const string &fileName, vector<char> &ops, const bool dumpSymbols)
{
    map<string, int> symbolTable;
    vector<unique_ptr<Cell>> cells;

    ifstream file(fileName);
    int address = 0;
    int lineNumber = 0;

    tokenizeCodeSection(file, address, lineNumber, symbolTable, cells);
    tokenizeDataSection(file, address, lineNumber, symbolTable, cells);

    if (dumpSymbols)
    {
        if (!symbolTable.empty())
        {
            cout << "Symbol table" << endl;
            for (auto &pair : symbolTable)
            {
                cout << "  symbol: " << pair.first << " = " << pair.second << endl;
            }
        }

        if (!cells.empty())
        {
            cout << "Cells" << endl;
            for (auto &cell : cells)
            {
                cout << "  cell: " << cell->toString() << endl;
            }
        }
    }

    emit(symbolTable, cells, ops);
}
<commit_msg>Fix g++ compiler warning 'sign-compare'<commit_after>#include <fstream>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
using namespace std;

#include "assembler.hpp"
#include "common/util.hpp"
#include "cell.hpp"
#include "symbol-ref-cell.hpp"
#include "value-cell.hpp"

static void parseToken(const string &token, int address, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    size_t colonIndex = token.find(':');
    string valueStr(colonIndex == string::npos ? token : token.substr(colonIndex + 1));

    if (colonIndex != string::npos)
    {
        string symbol(token.substr(0, colonIndex));
        string valueStr(token.substr(colonIndex + 1));
        symbolTable.insert(make_pair(symbol, address));
    }

    int value;
    if (tryLexicalCast(valueStr, value))
    {
        cells.push_back(unique_ptr<Cell>(new ValueCell(address, value)));
    }
    else
    {
        cells.push_back(unique_ptr<Cell>(new SymbolRefCell(address, valueStr)));
    }
}

static void tokenizeCodeSection(ifstream &file, int &address, int &lineNumber, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    string line;
    for (; getline(file, line); ++lineNumber)
    {
        if (line.compare(".data") == 0)
        {
            ++lineNumber;
            return;
        }

        string tokens[3];
        istringstream stream(line);
        string token;
        size_t tokenCount = 0;
        while (stream >> token)
        {
            if (tokenCount >= extent<decltype(tokens)>::value)
            {
                throw runtime_error(Formatter() << "Too many tokens at line " << (lineNumber + 1));
            }
            tokens[tokenCount++] = token;
        }

        if (tokenCount == 0)
        {
            continue;
        }

        switch (tokenCount)
        {
        case 1:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[0], address + 1, symbolTable, cells);
            cells.push_back(unique_ptr<Cell>(new ValueCell(address + 2, address + 3)));
            break;
        case 2:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[1], address + 1, symbolTable, cells);
            cells.push_back(unique_ptr<Cell>(new ValueCell(address + 2, address + 3)));
            break;
        case 3:
            parseToken(tokens[0], address + 0, symbolTable, cells);
            parseToken(tokens[1], address + 1, symbolTable, cells);
            parseToken(tokens[2], address + 2, symbolTable, cells);
            break;
        default:
            throw runtime_error("Unexpected");
        }

        address += 3;
    }
}

static void tokenizeDataSection(ifstream &file, int &address, int &lineNumber, map<string, int> &symbolTable, vector<unique_ptr<Cell>> &cells)
{
    string line;
    for (; getline(file, line); ++lineNumber)
    {
        istringstream stream(line);
        string token;
        while (stream >> token)
        {
            parseToken(token, address, symbolTable, cells);
            ++address;
        }
    }
}

static void emit(const map<string, int> &symbolTable, const vector<unique_ptr<Cell>> &cells, vector<char> &ops)
{
    ops.reserve(cells.size());
    for (auto &cell : cells)
    {
        int op = cell->emit(symbolTable);
        ops.push_back(castInt<char>(op));
    }
}

void Assembler::assemble(const string &fileName, vector<char> &ops, const bool dumpSymbols)
{
    map<string, int> symbolTable;
    vector<unique_ptr<Cell>> cells;

    ifstream file(fileName);
    int address = 0;
    int lineNumber = 0;

    tokenizeCodeSection(file, address, lineNumber, symbolTable, cells);
    tokenizeDataSection(file, address, lineNumber, symbolTable, cells);

    if (dumpSymbols)
    {
        if (!symbolTable.empty())
        {
            cout << "Symbol table" << endl;
            for (auto &pair : symbolTable)
            {
                cout << "  symbol: " << pair.first << " = " << pair.second << endl;
            }
        }

        if (!cells.empty())
        {
            cout << "Cells" << endl;
            for (auto &cell : cells)
            {
                cout << "  cell: " << cell->toString() << endl;
            }
        }
    }

    emit(symbolTable, cells, ops);
}
<|endoftext|>