<commit_before>#include <maya/MFnTypedAttribute.h>
#include <maya/MDataHandle.h>
#include <maya/MPointArray.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnIntArrayData.h>

#include "CurveMeshInputNode.h"
#include "MayaTypeID.h"
#include "util.h"

#include <cassert>

MString CurveMeshInputNode::typeName = "houdiniCurveMeshInput";

MTypeId CurveMeshInputNode::typeId = MayaTypeID_HoudiniCurveMeshInput;

MObject
    CurveMeshInputNode::theInputCurves,
    CurveMeshInputNode::theOutputObjectMetaData;

void*
CurveMeshInputNode::creator()
{
    CurveMeshInputNode* ret = new CurveMeshInputNode();
    return ret;
}

MStatus
CurveMeshInputNode::initialize()
{
    MFnTypedAttribute tAttr;
    theInputCurves = tAttr.create( "inputCurves", "incs",
                                    MFnData::kNurbsCurve );
    tAttr.setArray(true);

    addAttribute(theInputCurves);

    theOutputObjectMetaData = tAttr.create(
        "outputObjectMetaData", "oomd",
        MFnData::kIntArray
    );

    tAttr.setStorable(false);
    tAttr.setWritable(false);

    addAttribute(theOutputObjectMetaData);

    attributeAffects(theInputCurves, theOutputObjectMetaData);

    return MS::kSuccess;
}

CurveMeshInputNode::CurveMeshInputNode()
    : myAssetId(-1)
{
}

CurveMeshInputNode::~CurveMeshInputNode()
{
    if ( myAssetId > 0 )
    {
        CHECK_HAPI(HAPI_DestroyAsset(myAssetId));
    }
}

void
CurveMeshInputNode::postConstructor()
{
}

MStatus
CurveMeshInputNode::setDependentsDirty(const MPlug& plugBeingDirtied,
        MPlugArray& affectedPlugs)
{
    MStatus status;

    return MS::kSuccess;
}

MStatus
CurveMeshInputNode::compute(const MPlug& plug, MDataBlock& data)
{
    MStatus status;

    if ( plug != theOutputObjectMetaData )
    {
        return MPxTransform::compute(plug, data);
    }

    if ( myAssetId < 0 )
    {
        CHECK_HAPI( HAPI_CreateInputAsset(&myAssetId, NULL) );
        if ( !Util::statusCheckLoop() )
        {
            DISPLAY_ERROR(
                MString("Unexpected error when creating input asset.")
            );
        }

        MDataHandle metaDataHandle = data.outputValue(theOutputObjectMetaData);

        // Meta data
        MFnIntArrayData ffIAD;
        MIntArray metaDataArray;
        metaDataArray.append(myAssetId);
        metaDataArray.append(0); // object ID
        MObject newMetaData = ffIAD.create(metaDataArray);
        metaDataHandle.set(newMetaData);
    }

    MArrayDataHandle inputCurves(data.inputArrayValue(theInputCurves, &status));
    CHECK_MSTATUS_AND_RETURN_IT(status);

    const int nInputCurves = inputCurves.elementCount();
    if ( nInputCurves <= 0 )
    {
        data.setClean(plug);
        return MStatus::kSuccess;
    }

    HAPI_CurveInfo curveInfo = HAPI_CurveInfo_Create();
    curveInfo.curveType = HAPI_CURVETYPE_NURBS;
    curveInfo.isRational = false;

    std::vector<float>  cvP, cvPw;
    std::vector<int>    cvCounts;
    std::vector<float>  knots;
    std::vector<int>    orders;

    for ( int iCurve = 0; iCurve < nInputCurves; ++iCurve )
    {
        MDataHandle curveHandle = inputCurves.inputValue();
        MObject curveObject = curveHandle.asNurbsCurveTransformed();
        MFnNurbsCurve fnCurve( curveObject );

        const bool isPeriodic = fnCurve.form() == MFnNurbsCurve::kPeriodic;
        if ( iCurve == 0 )
        {
            curveInfo.isPeriodic = isPeriodic;
        }
        else if ( isPeriodic != curveInfo.isPeriodic )
        {
            DISPLAY_ERROR(
                MString("Curve has a non-matching periodicity, skipping")
            );
            continue;
        }

        ++curveInfo.curveCount;

        const int order = fnCurve.degree() + 1;
        if ( iCurve == 0 )
        {
            curveInfo.order = order;
        }
        else if ( curveInfo.order == HAPI_CURVE_ORDER_VARYING )
        {
            orders.push_back( order );
        }
        else if ( order != curveInfo.order )
        {
            orders.resize( curveInfo.curveCount - 1, curveInfo.order );
            curveInfo.order = HAPI_CURVE_ORDER_VARYING;
            orders.push_back( order );
        }

        MPointArray cvArray;
        CHECK_MSTATUS_AND_RETURN_IT( fnCurve.getCVs( cvArray ) );

        unsigned int nCVs = cvArray.length();

        // Maya provides fnCurve.degree() more cvs in its data definition
        // than Houdini for periodic curves--but they are conincident
        // with the first ones. Houdini ignores them, so we don't
        // output them.
        if ( curveInfo.isPeriodic && static_cast<int>(nCVs) > fnCurve.degree() )
        {
            nCVs -= fnCurve.degree();
        }

        cvCounts.push_back(nCVs);
        curveInfo.vertexCount += nCVs;

        for ( unsigned int iCV = 0; iCV < nCVs; ++iCV )
        {
            const MPoint& cv = cvArray[iCV];
            cvP.push_back(cv.x);
            cvP.push_back(cv.y);
            cvP.push_back(cv.z);

            if ( !curveInfo.isRational )
            {
                if ( cv.w != 1.0 )
                {
                    curveInfo.isRational = true;
                    cvPw.resize( curveInfo.curveCount - 1, 1.0f );
                    cvPw.push_back(cv.w);
                }
            }
            else
            {
                cvPw.push_back(cv.w);
            }
        }

        MDoubleArray knotsArray;
        CHECK_MSTATUS_AND_RETURN_IT( fnCurve.getKnots( knotsArray ) );

        if ( knotsArray.length() > 0 )
        {
            // Maya doesn't provide the first and last knots
            curveInfo.knotCount += knotsArray.length() + 2;

            knots.push_back( static_cast<float>(knotsArray[0]) );

            for ( unsigned int iKnot = 0; iKnot < knotsArray.length(); ++iKnot )
            {
                knots.push_back( static_cast<float>(knotsArray[iKnot]) );
            }

            knots.push_back(
                static_cast<float>( knotsArray[knotsArray.length() - 1] )
            );
        }

        if ( !inputCurves.next() )
        {
            break;
        }
    }

    curveInfo.hasKnots = curveInfo.knotCount > 0;

    HAPI_PartInfo partInfo = HAPI_PartInfo_Create();
    partInfo.vertexCount = partInfo.pointCount = curveInfo.vertexCount;
    partInfo.faceCount = curveInfo.curveCount;
    partInfo.isCurve = true;
    CHECK_HAPI( HAPI_SetPartInfo( myAssetId, 0, 0, &partInfo ) );

    CHECK_HAPI( HAPI_SetCurveInfo( myAssetId, 0, 0, 0, &curveInfo ) );
    CHECK_HAPI( HAPI_SetCurveCounts( myAssetId, 0, 0, 0,
                                    &cvCounts.front(), 0, cvCounts.size() ) );

    HAPI_AttributeInfo attrInfo = HAPI_AttributeInfo_Create();
    attrInfo.count = partInfo.pointCount;
    attrInfo.tupleSize = 3; // 3 floats per CV (x, y, z)
    attrInfo.exists = true;
    attrInfo.owner = HAPI_ATTROWNER_POINT;
    attrInfo.storage = HAPI_STORAGETYPE_FLOAT;

    CHECK_HAPI( HAPI_AddAttribute( myAssetId, 0, 0, "P", &attrInfo ) );

    CHECK_HAPI(
        HAPI_SetAttributeFloatData(
            myAssetId, 0, 0, "P", &attrInfo,
            &cvP.front(), 0, static_cast<int>(cvP.size() / 3)
        )
    );

    if ( curveInfo.isRational )
    {
        attrInfo.tupleSize = 1;
        CHECK_HAPI( HAPI_AddAttribute( myAssetId, 0, 0, "Pw", &attrInfo ) );

        CHECK_HAPI(
            HAPI_SetAttributeFloatData(
                myAssetId, 0, 0, "Pw", &attrInfo,
                &cvPw.front(), 0, static_cast<int>(cvPw.size())
            )
        );
    }

    if ( curveInfo.hasKnots )
    {
        CHECK_HAPI( HAPI_SetCurveKnots( myAssetId, 0, 0, 0, &knots.front(),
                                        0, static_cast<int>(knots.size()) ) );
    }

    CHECK_HAPI( HAPI_CommitGeo( myAssetId, 0, 0 ) );

    data.setClean(plug);
    return MStatus::kSuccess;
}

bool
CurveMeshInputNode::getInternalValueInContext(
        const MPlug &plug,
        MDataHandle &dataHandle,
        MDGContext &ctx)
{
    MStatus status;

    return MPxTransform::getInternalValueInContext(plug, dataHandle, ctx);
}

bool
CurveMeshInputNode::setInternalValueInContext(
        const MPlug &plug,
        const MDataHandle &dataHandle,
        MDGContext &ctx
        )
{
    MStatus status;

    return MPxTransform::setInternalValueInContext(plug, dataHandle, ctx);
}

int
CurveMeshInputNode::internalArrayCount(const MPlug &plug, const MDGContext &ctx) const
{
    return MPxTransform::internalArrayCount(plug, ctx);
}

void
CurveMeshInputNode::copyInternalData(MPxNode* node)
{
    MStatus status;

    MPxTransform::copyInternalData(node);
}
<commit_msg>hair system input - fixed a bug with Pw.<commit_after>#include <maya/MFnTypedAttribute.h>
#include <maya/MDataHandle.h>
#include <maya/MPointArray.h>
#include <maya/MFnNurbsCurve.h>
#include <maya/MFnIntArrayData.h>

#include "CurveMeshInputNode.h"
#include "MayaTypeID.h"
#include "util.h"

#include <cassert>

MString CurveMeshInputNode::typeName = "houdiniCurveMeshInput";

MTypeId CurveMeshInputNode::typeId = MayaTypeID_HoudiniCurveMeshInput;

MObject
    CurveMeshInputNode::theInputCurves,
    CurveMeshInputNode::theOutputObjectMetaData;

void*
CurveMeshInputNode::creator()
{
    CurveMeshInputNode* ret = new CurveMeshInputNode();
    return ret;
}

MStatus
CurveMeshInputNode::initialize()
{
    MFnTypedAttribute tAttr;
    theInputCurves = tAttr.create( "inputCurves", "incs",
                                    MFnData::kNurbsCurve );
    tAttr.setArray(true);

    addAttribute(theInputCurves);

    theOutputObjectMetaData = tAttr.create(
        "outputObjectMetaData", "oomd",
        MFnData::kIntArray
    );

    tAttr.setStorable(false);
    tAttr.setWritable(false);

    addAttribute(theOutputObjectMetaData);

    attributeAffects(theInputCurves, theOutputObjectMetaData);

    return MS::kSuccess;
}

CurveMeshInputNode::CurveMeshInputNode()
    : myAssetId(-1)
{
}

CurveMeshInputNode::~CurveMeshInputNode()
{
    if ( myAssetId > 0 )
    {
        CHECK_HAPI(HAPI_DestroyAsset(myAssetId));
    }
}

void
CurveMeshInputNode::postConstructor()
{
}

MStatus
CurveMeshInputNode::setDependentsDirty(const MPlug& plugBeingDirtied,
        MPlugArray& affectedPlugs)
{
    MStatus status;

    return MS::kSuccess;
}

MStatus
CurveMeshInputNode::compute(const MPlug& plug, MDataBlock& data)
{
    MStatus status;

    if ( plug != theOutputObjectMetaData )
    {
        return MPxTransform::compute(plug, data);
    }

    if ( myAssetId < 0 )
    {
        CHECK_HAPI( HAPI_CreateInputAsset(&myAssetId, NULL) );
        if ( !Util::statusCheckLoop() )
        {
            DISPLAY_ERROR(
                MString("Unexpected error when creating input asset.")
            );
        }

        MDataHandle metaDataHandle = data.outputValue(theOutputObjectMetaData);

        // Meta data
        MFnIntArrayData ffIAD;
        MIntArray metaDataArray;
        metaDataArray.append(myAssetId);
        metaDataArray.append(0); // object ID
        MObject newMetaData = ffIAD.create(metaDataArray);
        metaDataHandle.set(newMetaData);
    }

    MArrayDataHandle inputCurves(data.inputArrayValue(theInputCurves, &status));
    CHECK_MSTATUS_AND_RETURN_IT(status);

    const int nInputCurves = inputCurves.elementCount();
    if ( nInputCurves <= 0 )
    {
        data.setClean(plug);
        return MStatus::kSuccess;
    }

    HAPI_CurveInfo curveInfo = HAPI_CurveInfo_Create();
    curveInfo.curveType = HAPI_CURVETYPE_NURBS;
    curveInfo.isRational = false;

    std::vector<float>  cvP, cvPw;
    std::vector<int>    cvCounts;
    std::vector<float>  knots;
    std::vector<int>    orders;

    for ( int iCurve = 0; iCurve < nInputCurves; ++iCurve )
    {
        MDataHandle curveHandle = inputCurves.inputValue();
        MObject curveObject = curveHandle.asNurbsCurveTransformed();
        MFnNurbsCurve fnCurve( curveObject );

        const bool isPeriodic = fnCurve.form() == MFnNurbsCurve::kPeriodic;
        if ( iCurve == 0 )
        {
            curveInfo.isPeriodic = isPeriodic;
        }
        else if ( isPeriodic != curveInfo.isPeriodic )
        {
            DISPLAY_ERROR(
                MString("Curve has a non-matching periodicity, skipping")
            );
            continue;
        }

        const int order = fnCurve.degree() + 1;
        if ( iCurve == 0 )
        {
            curveInfo.order = order;
        }
        else if ( curveInfo.order == HAPI_CURVE_ORDER_VARYING )
        {
            orders.push_back( order );
        }
        else if ( order != curveInfo.order )
        {
            orders.resize( curveInfo.curveCount, curveInfo.order );
            curveInfo.order = HAPI_CURVE_ORDER_VARYING;
            orders.push_back( order );
        }

        MPointArray cvArray;
        CHECK_MSTATUS_AND_RETURN_IT( fnCurve.getCVs( cvArray ) );

        unsigned int nCVs = cvArray.length();

        // Maya provides fnCurve.degree() more cvs in its data definition
        // than Houdini for periodic curves -- but they are conincident
        // with the first ones. Houdini ignores them, so we don't
        // output them.
        if ( curveInfo.isPeriodic && static_cast<int>(nCVs) > fnCurve.degree() )
        {
            nCVs -= fnCurve.degree();
        }

        cvCounts.push_back(nCVs);

        for ( unsigned int iCV = 0; iCV < nCVs; ++iCV, ++curveInfo.vertexCount )
        {
            const MPoint& cv = cvArray[iCV];
            cvP.push_back(cv.x);
            cvP.push_back(cv.y);
            cvP.push_back(cv.z);

            if ( !curveInfo.isRational )
            {
                if ( cv.w != 1.0 )
                {
                    curveInfo.isRational = true;
                    cvPw.resize( curveInfo.vertexCount, 1.0f );
                    cvPw.push_back(cv.w);
                }
            }
            else
            {
                cvPw.push_back(cv.w);
            }
        }

        MDoubleArray knotsArray;
        CHECK_MSTATUS_AND_RETURN_IT( fnCurve.getKnots( knotsArray ) );

        if ( knotsArray.length() > 0 )
        {
            // Maya doesn't provide the first and last knots
            curveInfo.knotCount += knotsArray.length() + 2;

            knots.push_back( static_cast<float>(knotsArray[0]) );

            for ( unsigned int iKnot = 0; iKnot < knotsArray.length(); ++iKnot )
            {
                knots.push_back( static_cast<float>(knotsArray[iKnot]) );
            }

            knots.push_back(
                static_cast<float>( knotsArray[knotsArray.length() - 1] )
            );
        }

        ++curveInfo.curveCount;

        if ( !inputCurves.next() )
        {
            break;
        }
    }

    curveInfo.hasKnots = curveInfo.knotCount > 0;

    HAPI_PartInfo partInfo = HAPI_PartInfo_Create();
    partInfo.vertexCount = partInfo.pointCount = curveInfo.vertexCount;
    partInfo.faceCount = curveInfo.curveCount;
    partInfo.isCurve = true;
    CHECK_HAPI( HAPI_SetPartInfo( myAssetId, 0, 0, &partInfo ) );

    CHECK_HAPI( HAPI_SetCurveInfo( myAssetId, 0, 0, 0, &curveInfo ) );
    CHECK_HAPI( HAPI_SetCurveCounts( myAssetId, 0, 0, 0,
                                    &cvCounts.front(), 0, cvCounts.size() ) );

    HAPI_AttributeInfo attrInfo = HAPI_AttributeInfo_Create();
    attrInfo.count = partInfo.pointCount;
    attrInfo.tupleSize = 3; // 3 floats per CV (x, y, z)
    attrInfo.exists = true;
    attrInfo.owner = HAPI_ATTROWNER_POINT;
    attrInfo.storage = HAPI_STORAGETYPE_FLOAT;

    CHECK_HAPI( HAPI_AddAttribute( myAssetId, 0, 0, "P", &attrInfo ) );

    CHECK_HAPI(
        HAPI_SetAttributeFloatData(
            myAssetId, 0, 0, "P", &attrInfo,
            &cvP.front(), 0, static_cast<int>(cvP.size() / 3)
        )
    );

    if ( curveInfo.isRational )
    {
        attrInfo.tupleSize = 1;
        CHECK_HAPI( HAPI_AddAttribute( myAssetId, 0, 0, "Pw", &attrInfo ) );

        CHECK_HAPI(
            HAPI_SetAttributeFloatData(
                myAssetId, 0, 0, "Pw", &attrInfo,
                &cvPw.front(), 0, static_cast<int>(cvPw.size())
            )
        );
    }

    if ( curveInfo.hasKnots )
    {
        CHECK_HAPI( HAPI_SetCurveKnots( myAssetId, 0, 0, 0, &knots.front(),
                                        0, static_cast<int>(knots.size()) ) );
    }

    CHECK_HAPI( HAPI_CommitGeo( myAssetId, 0, 0 ) );

    data.setClean(plug);
    return MStatus::kSuccess;
}

bool
CurveMeshInputNode::getInternalValueInContext(
        const MPlug &plug,
        MDataHandle &dataHandle,
        MDGContext &ctx)
{
    MStatus status;

    return MPxTransform::getInternalValueInContext(plug, dataHandle, ctx);
}

bool
CurveMeshInputNode::setInternalValueInContext(
        const MPlug &plug,
        const MDataHandle &dataHandle,
        MDGContext &ctx
        )
{
    MStatus status;

    return MPxTransform::setInternalValueInContext(plug, dataHandle, ctx);
}

int
CurveMeshInputNode::internalArrayCount(const MPlug &plug, const MDGContext &ctx) const
{
    return MPxTransform::internalArrayCount(plug, ctx);
}

void
CurveMeshInputNode::copyInternalData(MPxNode* node)
{
    MStatus status;

    MPxTransform::copyInternalData(node);
}
<|endoftext|>


<commit_before>#include "ImeModule.h"
#include <string>
#include <ObjBase.h>
#include <msctf.h>
#include <Shlwapi.h>
#include <assert.h>
#include "Window.h"

using namespace Ime;
using namespace std;

ImeModule::ImeModule(HMODULE module, const CLSID& textServiceClsid):
	hInstance_(HINSTANCE(module)),
	textServiceClsid_(textServiceClsid),
	refCount_(1) {

	Window::registerClass(hInstance_);
}

ImeModule::~ImeModule(void) {
}

// Dll entry points implementations
HRESULT ImeModule::canUnloadNow() {
	// we own the last reference
	return refCount_ <= 1 ? S_OK : S_FALSE;
}

HRESULT ImeModule::getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) {
    if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) {
		// increase reference count
		AddRef();
		*ppvObj = (IClassFactory*)this; // our own object implements IClassFactory
		return NOERROR;
    }
	else
	    *ppvObj = NULL;
    return CLASS_E_CLASSNOTAVAILABLE;
}

HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID languageId) {
	// write info of our COM text service component to the registry
	// path: HKEY_CLASS_ROOT\\CLSID\\{xxxx-xxxx-xxxx-xx....}
	// This reguires Administrator permimssion to write to the registery
	// regsvr32 should be run with Administrator
	// For 64 bit dll, it seems that we need to write the key to
	// a different path to make it coexist with 32 bit version:
	// HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{xxx-xxx-...}
	// Reference: http://stackoverflow.com/questions/1105031/can-my-32-bit-and-64-bit-com-components-co-reside-on-the-same-machine

	HRESULT result = S_OK;

	// get path of our module
	wchar_t modulePath[MAX_PATH];
	DWORD modulePathLen = GetModuleFileNameW(hInstance_, modulePath, MAX_PATH);

	wstring regPath = L"CLSID\\";
	LPOLESTR clsidStr = NULL;
	if(StringFromCLSID(textServiceClsid_, &clsidStr) != ERROR_SUCCESS)
		return E_FAIL;
	regPath += clsidStr;
	CoTaskMemFree(clsidStr);

	HKEY hkey = NULL;
	if(::RegCreateKeyExW(HKEY_CLASSES_ROOT, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL) == ERROR_SUCCESS) {
		// write name of our IME
		::RegSetValueExW(hkey, NULL, 0, REG_SZ, (BYTE*)name, sizeof(wchar_t) * (wcslen(name) + 1));

		HKEY inProcServer32Key;
		if(::RegCreateKeyExW(hkey, L"InprocServer32", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inProcServer32Key, NULL) == ERROR_SUCCESS) {
			// store the path of our dll module in the registry
			::RegSetValueExW(inProcServer32Key, NULL, 0, REG_SZ, (BYTE*)modulePath, (modulePathLen + 1) * sizeof(wchar_t));
			// write threading model
			wchar_t apartmentStr[] = L"Apartment";
            ::RegSetValueExW(inProcServer32Key, L"ThreadingModel", 0, REG_SZ, (BYTE*)apartmentStr, 10 * sizeof(wchar_t));
			::RegCloseKey(inProcServer32Key);
		}
		else
			result = E_FAIL;
		::RegCloseKey(hkey);
    }
	else
		result = E_FAIL;

	// register the language profile
	if(result == S_OK) {
		result = E_FAIL;
		ITfInputProcessorProfiles *inputProcessProfiles = NULL;
		if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
			if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) {
				if(inputProcessProfiles->AddLanguageProfile(textServiceClsid_, languageId, profileGuid,
											name, -1, modulePath, modulePathLen, 0) == S_OK) {
					result = S_OK;
				}
			}
			inputProcessProfiles->Release();
		}
	}

	// register category
	if(result == S_OK) {
		ITfCategoryMgr *categoryMgr = NULL;
		if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) {
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}

			// register ourself as a display attribute provider
			// so later we can set change the look and feels of composition string.
			//if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid) != S_OK) {
			//	result = E_FAIL;
			//}

			// for Windows 8 store app support
			// TODO: according to a exhaustive Google search, I found that
			// TF_IPP_CAPS_IMMERSIVESUPPORT is required to make the IME work with Windows 8.
			// http://social.msdn.microsoft.com/Forums/windowsapps/en-US/4c422cf1-ceb4-413b-8a7c-6881946a4c63/how-to-set-a-flag-indicating-tsf-components-compatibility
			// Quote from the page: "To indicate that your IME is compatible with Windows Store apps, call RegisterCategory with GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT."

			// declare supporting immersive mode
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}

			// declare compatibility with Windows 8 system tray
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}
			categoryMgr->Release();
		}
	}
	return result;
}

HRESULT ImeModule::unregisterServer(const GUID& profileGuid) {
	// unregister the language profile
	ITfInputProcessorProfiles *inputProcessProfiles = NULL;
	if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
		inputProcessProfiles->Unregister(textServiceClsid_);
		inputProcessProfiles->Release();
	}

	// unregister categories
	ITfCategoryMgr *categoryMgr = NULL;
	if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) {
		categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_);
		categoryMgr->Release();
	}

	// delete the registry key
	wstring regPath = L"CLSID\\";
	LPOLESTR clsidStr = NULL;
	if(StringFromCLSID(textServiceClsid_, &clsidStr) == ERROR_SUCCESS) {
		regPath += clsidStr;
		CoTaskMemFree(clsidStr);
		::SHDeleteKey(HKEY_CLASSES_ROOT, regPath.c_str());
	}
	return S_OK;
}

// COM related stuff

// IUnknown
STDMETHODIMP ImeModule::QueryInterface(REFIID riid, void **ppvObj) {
    if (ppvObj == NULL)
        return E_INVALIDARG;

	if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
		*ppvObj = (IClassFactory*)this;
	else
		*ppvObj = NULL;

	if(*ppvObj) {
		AddRef();
		return S_OK;
	}
	return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) ImeModule::AddRef(void) {
	return ::InterlockedIncrement(&refCount_);
}

STDMETHODIMP_(ULONG) ImeModule::Release(void) {
	// NOTE: I think we do not need to use critical sections to
	// protect the operation as M$ did in their TSF samples.
	// Our ImeModule will be alive until dll unload so
	// it's not possible for an user application and TSF manager
	// to free our objecet since we always have refCount == 1.
	// The last reference is released in DllMain() when unloading.
	// Hence interlocked operations are enough here, I guess.
	assert(refCount_ > 0);
	if(::InterlockedExchangeSubtract(&refCount_, 1) == 1) {
		delete this;
	}
	return refCount_;
}

// IClassFactory
STDMETHODIMP ImeModule::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) {
	// FIXME: do we need to check riid here?
	TextService* service = createTextService();
	if(service) {
		*ppvObj = (void*)service;
		return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP ImeModule::LockServer(BOOL fLock) {
	if(fLock)
		AddRef();
	else
		Release();
	return S_OK;
}
<commit_msg>Correctly handle unregistration.<commit_after>#include "ImeModule.h"
#include <string>
#include <ObjBase.h>
#include <msctf.h>
#include <Shlwapi.h>
#include <assert.h>
#include "Window.h"

using namespace Ime;
using namespace std;

ImeModule::ImeModule(HMODULE module, const CLSID& textServiceClsid):
	hInstance_(HINSTANCE(module)),
	textServiceClsid_(textServiceClsid),
	refCount_(1) {

	Window::registerClass(hInstance_);
}

ImeModule::~ImeModule(void) {
}

// Dll entry points implementations
HRESULT ImeModule::canUnloadNow() {
	// we own the last reference
	return refCount_ <= 1 ? S_OK : S_FALSE;
}

HRESULT ImeModule::getClassObject(REFCLSID rclsid, REFIID riid, void **ppvObj) {
    if (IsEqualIID(riid, IID_IClassFactory) || IsEqualIID(riid, IID_IUnknown)) {
		// increase reference count
		AddRef();
		*ppvObj = (IClassFactory*)this; // our own object implements IClassFactory
		return NOERROR;
    }
	else
	    *ppvObj = NULL;
    return CLASS_E_CLASSNOTAVAILABLE;
}

HRESULT ImeModule::registerServer(wchar_t* name, const GUID& profileGuid, LANGID languageId) {
	// write info of our COM text service component to the registry
	// path: HKEY_CLASS_ROOT\\CLSID\\{xxxx-xxxx-xxxx-xx....}
	// This reguires Administrator permimssion to write to the registery
	// regsvr32 should be run with Administrator
	// For 64 bit dll, it seems that we need to write the key to
	// a different path to make it coexist with 32 bit version:
	// HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes\CLSID\{xxx-xxx-...}
	// Reference: http://stackoverflow.com/questions/1105031/can-my-32-bit-and-64-bit-com-components-co-reside-on-the-same-machine

	HRESULT result = S_OK;

	// get path of our module
	wchar_t modulePath[MAX_PATH];
	DWORD modulePathLen = GetModuleFileNameW(hInstance_, modulePath, MAX_PATH);

	wstring regPath = L"CLSID\\";
	LPOLESTR clsidStr = NULL;
	if(StringFromCLSID(textServiceClsid_, &clsidStr) != ERROR_SUCCESS)
		return E_FAIL;
	regPath += clsidStr;
	CoTaskMemFree(clsidStr);

	HKEY hkey = NULL;
	if(::RegCreateKeyExW(HKEY_CLASSES_ROOT, regPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hkey, NULL) == ERROR_SUCCESS) {
		// write name of our IME
		::RegSetValueExW(hkey, NULL, 0, REG_SZ, (BYTE*)name, sizeof(wchar_t) * (wcslen(name) + 1));

		HKEY inProcServer32Key;
		if(::RegCreateKeyExW(hkey, L"InprocServer32", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &inProcServer32Key, NULL) == ERROR_SUCCESS) {
			// store the path of our dll module in the registry
			::RegSetValueExW(inProcServer32Key, NULL, 0, REG_SZ, (BYTE*)modulePath, (modulePathLen + 1) * sizeof(wchar_t));
			// write threading model
			wchar_t apartmentStr[] = L"Apartment";
            ::RegSetValueExW(inProcServer32Key, L"ThreadingModel", 0, REG_SZ, (BYTE*)apartmentStr, 10 * sizeof(wchar_t));
			::RegCloseKey(inProcServer32Key);
		}
		else
			result = E_FAIL;
		::RegCloseKey(hkey);
    }
	else
		result = E_FAIL;

	// register the language profile
	if(result == S_OK) {
		result = E_FAIL;
		ITfInputProcessorProfiles *inputProcessProfiles = NULL;
		if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
			if(inputProcessProfiles->Register(textServiceClsid_) == S_OK) {
				if(inputProcessProfiles->AddLanguageProfile(textServiceClsid_, languageId, profileGuid,
											name, -1, modulePath, modulePathLen, 0) == S_OK) {
					result = S_OK;
				}
			}
			inputProcessProfiles->Release();
		}
	}

	// register category
	if(result == S_OK) {
		ITfCategoryMgr *categoryMgr = NULL;
		if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) {
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}

			// register ourself as a display attribute provider
			// so later we can set change the look and feels of composition string.
			//if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_DISPLAYATTRIBUTEPROVIDER, textServiceClsid) != S_OK) {
			//	result = E_FAIL;
			//}

			// for Windows 8 store app support
			// TODO: according to a exhaustive Google search, I found that
			// TF_IPP_CAPS_IMMERSIVESUPPORT is required to make the IME work with Windows 8.
			// http://social.msdn.microsoft.com/Forums/windowsapps/en-US/4c422cf1-ceb4-413b-8a7c-6881946a4c63/how-to-set-a-flag-indicating-tsf-components-compatibility
			// Quote from the page: "To indicate that your IME is compatible with Windows Store apps, call RegisterCategory with GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT."

			// declare supporting immersive mode
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}

			// declare compatibility with Windows 8 system tray
			if(categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_) != S_OK) {
				result = E_FAIL;
			}
			categoryMgr->Release();
		}
	}
	return result;
}

HRESULT ImeModule::unregisterServer(const GUID& profileGuid) {
	// unregister the language profile
	ITfInputProcessorProfiles *inputProcessProfiles = NULL;
	if(CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (void**)&inputProcessProfiles) == S_OK) {
		inputProcessProfiles->Unregister(textServiceClsid_);
		inputProcessProfiles->Release();
	}

	// unregister categories
	ITfCategoryMgr *categoryMgr = NULL;
	if(CoCreateInstance(CLSID_TF_CategoryMgr, NULL, CLSCTX_INPROC_SERVER, IID_ITfCategoryMgr, (void**)&categoryMgr) == S_OK) {
		categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIP_KEYBOARD, textServiceClsid_);

		// Windows 8 support
		categoryMgr->UnregisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT, textServiceClsid_);
		categoryMgr->RegisterCategory(textServiceClsid_, GUID_TFCAT_TIPCAP_SYSTRAYSUPPORT, textServiceClsid_);

		categoryMgr->Release();
	}

	// delete the registry key
	wstring regPath = L"CLSID\\";
	LPOLESTR clsidStr = NULL;
	if(StringFromCLSID(textServiceClsid_, &clsidStr) == ERROR_SUCCESS) {
		regPath += clsidStr;
		CoTaskMemFree(clsidStr);
		::SHDeleteKey(HKEY_CLASSES_ROOT, regPath.c_str());
	}
	return S_OK;
}

// COM related stuff

// IUnknown
STDMETHODIMP ImeModule::QueryInterface(REFIID riid, void **ppvObj) {
    if (ppvObj == NULL)
        return E_INVALIDARG;

	if(IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
		*ppvObj = (IClassFactory*)this;
	else
		*ppvObj = NULL;

	if(*ppvObj) {
		AddRef();
		return S_OK;
	}
	return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) ImeModule::AddRef(void) {
	return ::InterlockedIncrement(&refCount_);
}

STDMETHODIMP_(ULONG) ImeModule::Release(void) {
	// NOTE: I think we do not need to use critical sections to
	// protect the operation as M$ did in their TSF samples.
	// Our ImeModule will be alive until dll unload so
	// it's not possible for an user application and TSF manager
	// to free our objecet since we always have refCount == 1.
	// The last reference is released in DllMain() when unloading.
	// Hence interlocked operations are enough here, I guess.
	assert(refCount_ > 0);
	if(::InterlockedExchangeSubtract(&refCount_, 1) == 1) {
		delete this;
	}
	return refCount_;
}

// IClassFactory
STDMETHODIMP ImeModule::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObj) {
	// FIXME: do we need to check riid here?
	TextService* service = createTextService();
	if(service) {
		*ppvObj = (void*)service;
		return S_OK;
	}
	return S_FALSE;
}

STDMETHODIMP ImeModule::LockServer(BOOL fLock) {
	if(fLock)
		AddRef();
	else
		Release();
	return S_OK;
}
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <visionaray/get_normal.h>
#include <visionaray/get_shading_normal.h>

using namespace visionaray;

int main()
{

#if defined GET_NORMAL_TRIANGLE_PERFACE_FLOAT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float4>, primitive<unsigned>> hr;
    vector<3, simd::float4> n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_FLOAT8

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float8>, primitive<unsigned>> hr;
    vector<3, simd::float8> n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_INT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::int4>, primitive<unsigned>> hr;
    auto n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_MASK4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::mask4>, primitive<unsigned>> hr;
    auto n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_FLOAT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float4>, primitive<unsigned>> hr;
    vector<3, simd::float4> n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_FLOAT8

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float8>, primitive<unsigned>> hr;
    vector<3, simd::float8> n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_INT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::int4>, primitive<unsigned>> hr;
    auto n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_MASK4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::mask4>, primitive<unsigned>> hr;
    auto n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#endif

    return 0;
}
<commit_msg>Compile fix<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <visionaray/math/intersect.h> // hit_record
#include <visionaray/get_normal.h>
#include <visionaray/get_shading_normal.h>

using namespace visionaray;

int main()
{

#if defined GET_NORMAL_TRIANGLE_PERFACE_FLOAT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float4>, primitive<unsigned>> hr;
    vector<3, simd::float4> n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_FLOAT8

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float8>, primitive<unsigned>> hr;
    vector<3, simd::float8> n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_INT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::int4>, primitive<unsigned>> hr;
    auto n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERFACE_MASK4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::mask4>, primitive<unsigned>> hr;
    auto n = get_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_face_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_FLOAT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float4>, primitive<unsigned>> hr;
    vector<3, simd::float4> n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_FLOAT8

    vector<3, float>* normals;
    hit_record<basic_ray<simd::float8>, primitive<unsigned>> hr;
    vector<3, simd::float8> n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_INT4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::int4>, primitive<unsigned>> hr;
    auto n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#elif defined GET_NORMAL_TRIANGLE_PERVERTEX_MASK4

    vector<3, float>* normals;
    hit_record<basic_ray<simd::mask4>, primitive<unsigned>> hr;
    auto n = get_shading_normal(normals, hr, basic_triangle<3, float, unsigned>{}, normals_per_vertex_binding{});

#endif

    return 0;
}
<|endoftext|>


<commit_before>/* The goal of these tests is to verify the behavior of all blob commands given
 * the current state is updateStarted.  This state is achieved as an exit from
 * updatePending.
 */
#include "firmware_handler.hpp"
#include "firmware_unittest.hpp"
#include "status.hpp"
#include "util.hpp"

#include <cstdint>
#include <string>
#include <vector>

#include <gtest/gtest.h>

namespace ipmi_flash
{
namespace
{

using ::testing::Return;
using ::testing::UnorderedElementsAreArray;

/*
 * There are the following calls (parameters may vary):
 * canHandleBlob(blob)
 * getBlobIds
 * deleteBlob(blob)
 * stat(blob)
 * stat(session)
 * open(blob)
 * close(session)
 * writemeta(session)
 * write(session)
 * read(session)
 * commit(session)
 */

class FirmwareHandlerUpdateStartedTest : public IpmiOnlyFirmwareStaticTest
{
};

/*
 * open(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, AttemptToOpenFilesReturnsFailure)
{
    /* In state updateStarted a file is open, which means no others can be. */
    getToUpdateStarted();

    auto blobsToOpen = handler->getBlobIds();
    for (const auto& blob : blobsToOpen)
    {
        EXPECT_FALSE(handler->open(session + 1, flags, blob));
    }
}

/* canHandleBlob(blob)
 * getBlobIds
 */
TEST_F(FirmwareHandlerUpdateStartedTest, VerifyListOfBlobs)
{
    getToUpdateStarted();

    std::vector<std::string> expected = {updateBlobId, hashBlobId,
                                         activeImageBlobId, staticLayoutBlobId};
    EXPECT_THAT(handler->getBlobIds(), UnorderedElementsAreArray(expected));
}

/*
 * TODO: deleteBlob(blob)
 */

/*
 * stat(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, StatOnActiveImageReturnsFailure)
{
    getToUpdateStarted();

    ASSERT_TRUE(handler->canHandleBlob(activeImageBlobId));

    blobs::BlobMeta meta;
    EXPECT_FALSE(handler->stat(activeImageBlobId, &meta));
}

TEST_F(FirmwareHandlerUpdateStartedTest, StatOnUpdateBlobReturnsFailure)
{
    getToUpdateStarted();

    ASSERT_TRUE(handler->canHandleBlob(updateBlobId));

    blobs::BlobMeta meta;
    EXPECT_FALSE(handler->stat(updateBlobId, &meta));
}

TEST_F(FirmwareHandlerUpdateStartedTest, StatOnNormalBlobsReturnsSuccess)
{
    getToUpdateStarted();

    blobs::BlobMeta expected;
    expected.blobState = FirmwareBlobHandler::UpdateFlags::ipmi;
    expected.size = 0;

    std::vector<std::string> testBlobs = {staticLayoutBlobId, hashBlobId};
    for (const auto& blob : testBlobs)
    {
        ASSERT_TRUE(handler->canHandleBlob(blob));

        blobs::BlobMeta meta = {};
        EXPECT_TRUE(handler->stat(blob, &meta));
        EXPECT_EQ(expected, meta);
    }
}

/*
 * stat(session)
 */

/*
 * close(session)
 */

/*
 * writemeta(session)
 */

/*
 * write(session)
 */

/*
 * read(session)
 */

/*
 * commit(session)
 */

} // namespace
} // namespace ipmi_flash
<commit_msg>test: firmware updateStarted: writeMeta(session)<commit_after>/* The goal of these tests is to verify the behavior of all blob commands given
 * the current state is updateStarted.  This state is achieved as an exit from
 * updatePending.
 */
#include "firmware_handler.hpp"
#include "firmware_unittest.hpp"
#include "status.hpp"
#include "util.hpp"

#include <cstdint>
#include <string>
#include <vector>

#include <gtest/gtest.h>

namespace ipmi_flash
{
namespace
{

using ::testing::Return;
using ::testing::UnorderedElementsAreArray;

/*
 * There are the following calls (parameters may vary):
 * canHandleBlob(blob)
 * getBlobIds
 * deleteBlob(blob)
 * stat(blob)
 * stat(session)
 * open(blob)
 * close(session)
 * writemeta(session)
 * write(session)
 * read(session)
 * commit(session)
 */

class FirmwareHandlerUpdateStartedTest : public IpmiOnlyFirmwareStaticTest
{
};

/*
 * open(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, AttemptToOpenFilesReturnsFailure)
{
    /* In state updateStarted a file is open, which means no others can be. */
    getToUpdateStarted();

    auto blobsToOpen = handler->getBlobIds();
    for (const auto& blob : blobsToOpen)
    {
        EXPECT_FALSE(handler->open(session + 1, flags, blob));
    }
}

/* canHandleBlob(blob)
 * getBlobIds
 */
TEST_F(FirmwareHandlerUpdateStartedTest, VerifyListOfBlobs)
{
    getToUpdateStarted();

    std::vector<std::string> expected = {updateBlobId, hashBlobId,
                                         activeImageBlobId, staticLayoutBlobId};
    EXPECT_THAT(handler->getBlobIds(), UnorderedElementsAreArray(expected));
}

/*
 * TODO: deleteBlob(blob)
 */

/*
 * stat(blob)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, StatOnActiveImageReturnsFailure)
{
    getToUpdateStarted();

    ASSERT_TRUE(handler->canHandleBlob(activeImageBlobId));

    blobs::BlobMeta meta;
    EXPECT_FALSE(handler->stat(activeImageBlobId, &meta));
}

TEST_F(FirmwareHandlerUpdateStartedTest, StatOnUpdateBlobReturnsFailure)
{
    getToUpdateStarted();

    ASSERT_TRUE(handler->canHandleBlob(updateBlobId));

    blobs::BlobMeta meta;
    EXPECT_FALSE(handler->stat(updateBlobId, &meta));
}

TEST_F(FirmwareHandlerUpdateStartedTest, StatOnNormalBlobsReturnsSuccess)
{
    getToUpdateStarted();

    blobs::BlobMeta expected;
    expected.blobState = FirmwareBlobHandler::UpdateFlags::ipmi;
    expected.size = 0;

    std::vector<std::string> testBlobs = {staticLayoutBlobId, hashBlobId};
    for (const auto& blob : testBlobs)
    {
        ASSERT_TRUE(handler->canHandleBlob(blob));

        blobs::BlobMeta meta = {};
        EXPECT_TRUE(handler->stat(blob, &meta));
        EXPECT_EQ(expected, meta);
    }
}

/*
 * writemeta(session)
 */
TEST_F(FirmwareHandlerUpdateStartedTest, WriteMetaToUpdateBlobReturnsFailure)
{
    getToUpdateStarted();
    EXPECT_FALSE(handler->writeMeta(session, 0, {0x01}));
}

/*
 * write(session)
 */

/*
 * read(session)
 */

/*
 * stat(session) - this will trigger a check, and the state may change.
 */

/*
 * TODO: close(session) - this will abort.
 */

/*
 * commit(session)
 */

} // namespace
} // namespace ipmi_flash
<|endoftext|>


<commit_before>/*
 * libcpu: interface.cpp
 *
 * This is the interface to the client.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#include <memory>

#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Transforms/Utils/Cloning.h"

/* project global headers */
#include "libcpu.h"
#include "libcpu_llvm.h"
#include "tag.h"
#include "translate_all.h"
#include "translate_singlestep.h"
#include "translate_singlestep_bb.h"
#include "function.h"
#include "optimize.h"
#include "stat.h"

/* architecture descriptors */
extern arch_func_t arch_func_6502;
extern arch_func_t arch_func_m68k;
extern arch_func_t arch_func_mips;
extern arch_func_t arch_func_m88k;
extern arch_func_t arch_func_arm;
extern arch_func_t arch_func_8086;
extern arch_func_t arch_func_fapra;

#define IS_LITTLE_ENDIAN(cpu) (((cpu)->info.common_flags & CPU_FLAG_ENDIAN_MASK) == CPU_FLAG_ENDIAN_LITTLE)

static inline bool
is_valid_gpr_size(size_t size)
{
	switch (size) {
		case 0: case 1: case 8: case 16: case 32: case 64:
			return true;
		default:
			return false;
	}
}

static inline bool
is_valid_fpr_size(size_t size)
{
	switch (size) {
		case 0: case 32: case 64: case 80: case 128:
			return true;
		default:
			return false;
	}
}

static inline bool
is_valid_vr_size(size_t size)
{
	switch (size) {
		case 0: case 64: case 128:
			return true;
		default:
			return false;
	}
}

//////////////////////////////////////////////////////////////////////
// cpu_t
//////////////////////////////////////////////////////////////////////

cpu_t *
cpu_new(cpu_arch_t arch, uint32_t flags, uint32_t arch_flags)
{
	cpu_t *cpu;

	llvm::InitializeNativeTarget();

	cpu = new cpu_t;
	assert(cpu != NULL);
	memset(&cpu->info, 0, sizeof(cpu->info));
	memset(&cpu->rf, 0, sizeof(cpu->rf));

	cpu->ctx = new LLVMContext();
	cpu->info.type = arch;
	cpu->info.name = "noname";
	cpu->info.common_flags = flags;
	cpu->info.arch_flags = arch_flags;

	switch (arch) {
		case CPU_ARCH_6502:
			cpu->f = arch_func_6502;
			break;
		case CPU_ARCH_M68K:
			cpu->f = arch_func_m68k;
			break;
		case CPU_ARCH_MIPS:
			cpu->f = arch_func_mips;
			break;
		case CPU_ARCH_M88K:
			cpu->f = arch_func_m88k;
			break;
		case CPU_ARCH_ARM:
			cpu->f = arch_func_arm;
			break;
		case CPU_ARCH_8086:
			cpu->f = arch_func_8086;
			break;
		case CPU_ARCH_FAPRA:
			cpu->f = arch_func_fapra;
			break;
		default:
			printf("illegal arch: %d\n", arch);
			exit(1);
	}

	cpu->code_start = 0;
	cpu->code_end = 0;
	cpu->code_entry = 0;
	cpu->tag = NULL;

	uint32_t i;
	for (i = 0; i < sizeof(cpu->func)/sizeof(*cpu->func); i++)
		cpu->func[i] = NULL;
	for (i = 0; i < sizeof(cpu->fp)/sizeof(*cpu->fp); i++)
		cpu->fp[i] = NULL;
	cpu->functions = 0;

	cpu->flags_codegen = CPU_CODEGEN_OPTIMIZE;
	cpu->flags_debug = CPU_DEBUG_NONE;
	cpu->flags_hint = CPU_HINT_NONE;
	cpu->flags = 0;

	// init the frontend
	cpu->f.init(cpu, &cpu->info, &cpu->rf);

	assert(is_valid_gpr_size(cpu->info.register_size[CPU_REG_GPR]) &&
		"the specified GPR size is not guaranteed to work");
	assert(is_valid_fpr_size(cpu->info.register_size[CPU_REG_FPR]) &&
		"the specified FPR size is not guaranteed to work");
	assert(is_valid_vr_size(cpu->info.register_size[CPU_REG_VR]) &&
		"the specified VR size is not guaranteed to work");
	assert(is_valid_gpr_size(cpu->info.register_size[CPU_REG_XR]) &&
		"the specified XR size is not guaranteed to work");

	uint32_t count = cpu->info.register_count[CPU_REG_GPR];
	if (count != 0) {
		cpu->ptr_gpr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_gpr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_gpr = NULL;
		cpu->in_ptr_gpr = NULL;
	}

	count = cpu->info.register_count[CPU_REG_XR];
	if (count != 0) {
		cpu->ptr_xr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_xr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_xr = NULL;
		cpu->in_ptr_xr = NULL;
	}

	count = cpu->info.register_count[CPU_REG_FPR];
	if (count != 0) {
		cpu->ptr_fpr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_fpr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_fpr = NULL;
		cpu->in_ptr_fpr = NULL;
	}

	if (cpu->info.psr_size != 0) {
		cpu->ptr_FLAG = (Value **)calloc(cpu->info.flags_count,
				sizeof(Value*));
		assert(cpu->ptr_FLAG != NULL);
	}

	// init LLVM
	cpu->mod = new Module(cpu->info.name, _CTX());
	assert(cpu->mod != NULL);
	std::unique_ptr<llvm::Module> module_ptr(llvm::CloneModule(*cpu->mod));
	EngineBuilder builder{std::move(module_ptr)};
	builder.setEngineKind(EngineKind::Kind::JIT);
	cpu->exec_engine = builder.create();
	assert(cpu->exec_engine != NULL);

	// check if FP80 and FP128 are supported by this architecture.
	// XXX there is a better way to do this?
	std::string data_layout = cpu->exec_engine->getDataLayout().getStringRepresentation();
	if (data_layout.find("f80") != std::string::npos) {
		LOG("INFO: FP80 supported.\n");
		cpu->flags |= CPU_FLAG_FP80;
	}
	if (data_layout.find("f128") != std::string::npos) {
		LOG("INFO: FP128 supported.\n");
		cpu->flags |= CPU_FLAG_FP128;
	}

	// check if we need to swap guest memory.
	if (cpu->exec_engine->getDataLayout().isLittleEndian()
			^ IS_LITTLE_ENDIAN(cpu))
		cpu->flags |= CPU_FLAG_SWAPMEM;

	cpu->timer_total[TIMER_TAG] = 0;
	cpu->timer_total[TIMER_FE] = 0;
	cpu->timer_total[TIMER_BE] = 0;
	cpu->timer_total[TIMER_RUN] = 0;

	return cpu;
}

void
cpu_free(cpu_t *cpu)
{
	if (cpu->f.done != NULL)
		cpu->f.done(cpu);
	if (cpu->exec_engine != NULL) {
		if (cpu->cur_func != NULL) {
			cpu->cur_func->eraseFromParent();
		}
		delete cpu->exec_engine;
	}
	if (cpu->ctx != NULL)
		delete cpu->ctx;
	if (cpu->ptr_FLAG != NULL)
		free(cpu->ptr_FLAG);
	if (cpu->in_ptr_fpr != NULL)
		free(cpu->in_ptr_fpr);
	if (cpu->ptr_fpr != NULL)
		free(cpu->ptr_fpr);
	if (cpu->in_ptr_xr != NULL)
		free(cpu->in_ptr_xr);
	if (cpu->ptr_xr != NULL)
		free(cpu->ptr_xr);
	if (cpu->in_ptr_gpr != NULL)
		free(cpu->in_ptr_gpr);
	if (cpu->ptr_gpr != NULL)
		free(cpu->ptr_gpr);

	delete cpu;
}

void
cpu_set_ram(cpu_t*cpu, uint8_t *r)
{
	cpu->RAM = r;
}

void
cpu_set_flags_codegen(cpu_t *cpu, uint32_t f)
{
	cpu->flags_codegen = f;
}

void
cpu_set_flags_debug(cpu_t *cpu, uint32_t f)
{
	cpu->flags_debug = f;
}

void
cpu_set_flags_hint(cpu_t *cpu, uint32_t f)
{
	cpu->flags_hint = f;
}

void
cpu_tag(cpu_t *cpu, addr_t pc)
{
	update_timing(cpu, TIMER_TAG, true);
	tag_start(cpu, pc);
	update_timing(cpu, TIMER_TAG, false);
}

static void
cpu_translate_function(cpu_t *cpu)
{
	BasicBlock *bb_ret, *bb_trap, *label_entry, *bb_start;

	/* create function and fill it with std basic blocks */
	cpu->cur_func = cpu_create_function(cpu, "jitmain", &bb_ret, &bb_trap, &label_entry);
	cpu->func[cpu->functions] = cpu->cur_func;

	/* TRANSLATE! */
	update_timing(cpu, TIMER_FE, true);
	if (cpu->flags_debug & CPU_DEBUG_SINGLESTEP) {
		bb_start = cpu_translate_singlestep(cpu, bb_ret, bb_trap);
	} else if (cpu->flags_debug & CPU_DEBUG_SINGLESTEP_BB) {
		bb_start = cpu_translate_singlestep_bb(cpu, bb_ret, bb_trap);
	} else {
		bb_start = cpu_translate_all(cpu, bb_ret, bb_trap);
	}
	update_timing(cpu, TIMER_FE, false);

	/* finish entry basicblock */
	BranchInst::Create(bb_start, label_entry);

	/* make sure everything is OK */
	verifyFunction(*cpu->cur_func, &llvm::errs());

	if (cpu->flags_debug & CPU_DEBUG_PRINT_IR)
		cpu->mod->print(llvm::errs(), nullptr);

	if (cpu->flags_codegen & CPU_CODEGEN_OPTIMIZE) {
		LOG("*** Optimizing...");
		optimize(cpu);
		LOG("done.\n");
		if (cpu->flags_debug & CPU_DEBUG_PRINT_IR_OPTIMIZED)
			cpu->mod->print(llvm::errs(), nullptr);
	}

	LOG("*** Translating...");
	update_timing(cpu, TIMER_BE, true);
	cpu->fp[cpu->functions] = cpu->exec_engine->getPointerToFunction(cpu->cur_func);
	update_timing(cpu, TIMER_BE, false);
	LOG("done.\n");

	cpu->functions++;
}

/* forces ahead of time translation (e.g. for benchmarking the run) */
void
cpu_translate(cpu_t *cpu)
{
	/* on demand translation */
	if (cpu->tags_dirty)
		cpu_translate_function(cpu);

	cpu->tags_dirty = false;
}

typedef int (*fp_t)(uint8_t *RAM, void *grf, void *frf, debug_function_t fp);

#ifdef __GNUC__
void __attribute__((noinline))
breakpoint() {
asm("nop");
}
#else
void breakpoint() {}
#endif

int
cpu_run(cpu_t *cpu, debug_function_t debug_function)
{
	addr_t pc = 0, orig_pc = 0;
	uint32_t i;
	int ret;
	bool success;
	bool do_translate = true;

	/* try to find the entry in all functions */
	while(true) {
		if (do_translate) {
			cpu_translate(cpu);
			pc = cpu->f.get_pc(cpu, cpu->rf.grf);
		}

		orig_pc = pc;
		success = false;
		for (i = 0; i < cpu->functions; i++) {
			fp_t FP = (fp_t)cpu->fp[i];
			update_timing(cpu, TIMER_RUN, true);
			breakpoint();
			ret = FP(cpu->RAM, cpu->rf.grf, cpu->rf.frf, debug_function);
			update_timing(cpu, TIMER_RUN, false);
			pc = cpu->f.get_pc(cpu, cpu->rf.grf);
			if (ret != JIT_RETURN_FUNCNOTFOUND)
				return ret;
			if (!is_inside_code_area(cpu, pc))
				return ret;
			if (pc != orig_pc) {
				success = true;
				break;
			}
		}
		if (!success) {
			LOG("{%" PRIx64 "}", pc);
			cpu_tag(cpu, pc);
			do_translate = true;
		}
	}
}
//printf("%d\n", __LINE__);

void
cpu_flush(cpu_t *cpu)
{
	cpu->cur_func->eraseFromParent();

	cpu->functions = 0;

	// reset bb caching mapping
	cpu->func_bb.clear();

//	delete cpu->mod;
//	cpu->mod = NULL;
}

void
cpu_print_statistics(cpu_t *cpu)
{
	printf("tag = %8" PRId64 "\n", cpu->timer_total[TIMER_TAG]);
	printf("fe  = %8" PRId64 "\n", cpu->timer_total[TIMER_FE]);
	printf("be  = %8" PRId64 "\n", cpu->timer_total[TIMER_BE]);
	printf("run = %8" PRId64 "\n", cpu->timer_total[TIMER_RUN]);
}
//printf("%s:%d\n", __func__, __LINE__);
<commit_msg>llvm: Assert that getPointerToFunction() does not return NULL<commit_after>/*
 * libcpu: interface.cpp
 *
 * This is the interface to the client.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>

#include <memory>

#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Transforms/Utils/Cloning.h"

/* project global headers */
#include "libcpu.h"
#include "libcpu_llvm.h"
#include "tag.h"
#include "translate_all.h"
#include "translate_singlestep.h"
#include "translate_singlestep_bb.h"
#include "function.h"
#include "optimize.h"
#include "stat.h"

/* architecture descriptors */
extern arch_func_t arch_func_6502;
extern arch_func_t arch_func_m68k;
extern arch_func_t arch_func_mips;
extern arch_func_t arch_func_m88k;
extern arch_func_t arch_func_arm;
extern arch_func_t arch_func_8086;
extern arch_func_t arch_func_fapra;

#define IS_LITTLE_ENDIAN(cpu) (((cpu)->info.common_flags & CPU_FLAG_ENDIAN_MASK) == CPU_FLAG_ENDIAN_LITTLE)

static inline bool
is_valid_gpr_size(size_t size)
{
	switch (size) {
		case 0: case 1: case 8: case 16: case 32: case 64:
			return true;
		default:
			return false;
	}
}

static inline bool
is_valid_fpr_size(size_t size)
{
	switch (size) {
		case 0: case 32: case 64: case 80: case 128:
			return true;
		default:
			return false;
	}
}

static inline bool
is_valid_vr_size(size_t size)
{
	switch (size) {
		case 0: case 64: case 128:
			return true;
		default:
			return false;
	}
}

//////////////////////////////////////////////////////////////////////
// cpu_t
//////////////////////////////////////////////////////////////////////

cpu_t *
cpu_new(cpu_arch_t arch, uint32_t flags, uint32_t arch_flags)
{
	cpu_t *cpu;

	llvm::InitializeNativeTarget();

	cpu = new cpu_t;
	assert(cpu != NULL);
	memset(&cpu->info, 0, sizeof(cpu->info));
	memset(&cpu->rf, 0, sizeof(cpu->rf));

	cpu->ctx = new LLVMContext();
	cpu->info.type = arch;
	cpu->info.name = "noname";
	cpu->info.common_flags = flags;
	cpu->info.arch_flags = arch_flags;

	switch (arch) {
		case CPU_ARCH_6502:
			cpu->f = arch_func_6502;
			break;
		case CPU_ARCH_M68K:
			cpu->f = arch_func_m68k;
			break;
		case CPU_ARCH_MIPS:
			cpu->f = arch_func_mips;
			break;
		case CPU_ARCH_M88K:
			cpu->f = arch_func_m88k;
			break;
		case CPU_ARCH_ARM:
			cpu->f = arch_func_arm;
			break;
		case CPU_ARCH_8086:
			cpu->f = arch_func_8086;
			break;
		case CPU_ARCH_FAPRA:
			cpu->f = arch_func_fapra;
			break;
		default:
			printf("illegal arch: %d\n", arch);
			exit(1);
	}

	cpu->code_start = 0;
	cpu->code_end = 0;
	cpu->code_entry = 0;
	cpu->tag = NULL;

	uint32_t i;
	for (i = 0; i < sizeof(cpu->func)/sizeof(*cpu->func); i++)
		cpu->func[i] = NULL;
	for (i = 0; i < sizeof(cpu->fp)/sizeof(*cpu->fp); i++)
		cpu->fp[i] = NULL;
	cpu->functions = 0;

	cpu->flags_codegen = CPU_CODEGEN_OPTIMIZE;
	cpu->flags_debug = CPU_DEBUG_NONE;
	cpu->flags_hint = CPU_HINT_NONE;
	cpu->flags = 0;

	// init the frontend
	cpu->f.init(cpu, &cpu->info, &cpu->rf);

	assert(is_valid_gpr_size(cpu->info.register_size[CPU_REG_GPR]) &&
		"the specified GPR size is not guaranteed to work");
	assert(is_valid_fpr_size(cpu->info.register_size[CPU_REG_FPR]) &&
		"the specified FPR size is not guaranteed to work");
	assert(is_valid_vr_size(cpu->info.register_size[CPU_REG_VR]) &&
		"the specified VR size is not guaranteed to work");
	assert(is_valid_gpr_size(cpu->info.register_size[CPU_REG_XR]) &&
		"the specified XR size is not guaranteed to work");

	uint32_t count = cpu->info.register_count[CPU_REG_GPR];
	if (count != 0) {
		cpu->ptr_gpr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_gpr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_gpr = NULL;
		cpu->in_ptr_gpr = NULL;
	}

	count = cpu->info.register_count[CPU_REG_XR];
	if (count != 0) {
		cpu->ptr_xr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_xr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_xr = NULL;
		cpu->in_ptr_xr = NULL;
	}

	count = cpu->info.register_count[CPU_REG_FPR];
	if (count != 0) {
		cpu->ptr_fpr = (Value **)calloc(count, sizeof(Value *));
		cpu->in_ptr_fpr = (Value **)calloc(count, sizeof(Value *));
	} else {
		cpu->ptr_fpr = NULL;
		cpu->in_ptr_fpr = NULL;
	}

	if (cpu->info.psr_size != 0) {
		cpu->ptr_FLAG = (Value **)calloc(cpu->info.flags_count,
				sizeof(Value*));
		assert(cpu->ptr_FLAG != NULL);
	}

	// init LLVM
	cpu->mod = new Module(cpu->info.name, _CTX());
	assert(cpu->mod != NULL);
	std::unique_ptr<llvm::Module> module_ptr(llvm::CloneModule(*cpu->mod));
	EngineBuilder builder{std::move(module_ptr)};
	builder.setEngineKind(EngineKind::Kind::JIT);
	cpu->exec_engine = builder.create();
	assert(cpu->exec_engine != NULL);

	// check if FP80 and FP128 are supported by this architecture.
	// XXX there is a better way to do this?
	std::string data_layout = cpu->exec_engine->getDataLayout().getStringRepresentation();
	if (data_layout.find("f80") != std::string::npos) {
		LOG("INFO: FP80 supported.\n");
		cpu->flags |= CPU_FLAG_FP80;
	}
	if (data_layout.find("f128") != std::string::npos) {
		LOG("INFO: FP128 supported.\n");
		cpu->flags |= CPU_FLAG_FP128;
	}

	// check if we need to swap guest memory.
	if (cpu->exec_engine->getDataLayout().isLittleEndian()
			^ IS_LITTLE_ENDIAN(cpu))
		cpu->flags |= CPU_FLAG_SWAPMEM;

	cpu->timer_total[TIMER_TAG] = 0;
	cpu->timer_total[TIMER_FE] = 0;
	cpu->timer_total[TIMER_BE] = 0;
	cpu->timer_total[TIMER_RUN] = 0;

	return cpu;
}

void
cpu_free(cpu_t *cpu)
{
	if (cpu->f.done != NULL)
		cpu->f.done(cpu);
	if (cpu->exec_engine != NULL) {
		if (cpu->cur_func != NULL) {
			cpu->cur_func->eraseFromParent();
		}
		delete cpu->exec_engine;
	}
	if (cpu->ctx != NULL)
		delete cpu->ctx;
	if (cpu->ptr_FLAG != NULL)
		free(cpu->ptr_FLAG);
	if (cpu->in_ptr_fpr != NULL)
		free(cpu->in_ptr_fpr);
	if (cpu->ptr_fpr != NULL)
		free(cpu->ptr_fpr);
	if (cpu->in_ptr_xr != NULL)
		free(cpu->in_ptr_xr);
	if (cpu->ptr_xr != NULL)
		free(cpu->ptr_xr);
	if (cpu->in_ptr_gpr != NULL)
		free(cpu->in_ptr_gpr);
	if (cpu->ptr_gpr != NULL)
		free(cpu->ptr_gpr);

	delete cpu;
}

void
cpu_set_ram(cpu_t*cpu, uint8_t *r)
{
	cpu->RAM = r;
}

void
cpu_set_flags_codegen(cpu_t *cpu, uint32_t f)
{
	cpu->flags_codegen = f;
}

void
cpu_set_flags_debug(cpu_t *cpu, uint32_t f)
{
	cpu->flags_debug = f;
}

void
cpu_set_flags_hint(cpu_t *cpu, uint32_t f)
{
	cpu->flags_hint = f;
}

void
cpu_tag(cpu_t *cpu, addr_t pc)
{
	update_timing(cpu, TIMER_TAG, true);
	tag_start(cpu, pc);
	update_timing(cpu, TIMER_TAG, false);
}

static void
cpu_translate_function(cpu_t *cpu)
{
	BasicBlock *bb_ret, *bb_trap, *label_entry, *bb_start;

	/* create function and fill it with std basic blocks */
	cpu->cur_func = cpu_create_function(cpu, "jitmain", &bb_ret, &bb_trap, &label_entry);
	cpu->func[cpu->functions] = cpu->cur_func;

	/* TRANSLATE! */
	update_timing(cpu, TIMER_FE, true);
	if (cpu->flags_debug & CPU_DEBUG_SINGLESTEP) {
		bb_start = cpu_translate_singlestep(cpu, bb_ret, bb_trap);
	} else if (cpu->flags_debug & CPU_DEBUG_SINGLESTEP_BB) {
		bb_start = cpu_translate_singlestep_bb(cpu, bb_ret, bb_trap);
	} else {
		bb_start = cpu_translate_all(cpu, bb_ret, bb_trap);
	}
	update_timing(cpu, TIMER_FE, false);

	/* finish entry basicblock */
	BranchInst::Create(bb_start, label_entry);

	/* make sure everything is OK */
	verifyFunction(*cpu->cur_func, &llvm::errs());

	if (cpu->flags_debug & CPU_DEBUG_PRINT_IR)
		cpu->mod->print(llvm::errs(), nullptr);

	if (cpu->flags_codegen & CPU_CODEGEN_OPTIMIZE) {
		LOG("*** Optimizing...");
		optimize(cpu);
		LOG("done.\n");
		if (cpu->flags_debug & CPU_DEBUG_PRINT_IR_OPTIMIZED)
			cpu->mod->print(llvm::errs(), nullptr);
	}

	LOG("*** Translating...");
	update_timing(cpu, TIMER_BE, true);
	cpu->fp[cpu->functions] = cpu->exec_engine->getPointerToFunction(cpu->cur_func);
	assert(cpu->fp[cpu->functions] != NULL);
	update_timing(cpu, TIMER_BE, false);
	LOG("done.\n");

	cpu->functions++;
}

/* forces ahead of time translation (e.g. for benchmarking the run) */
void
cpu_translate(cpu_t *cpu)
{
	/* on demand translation */
	if (cpu->tags_dirty)
		cpu_translate_function(cpu);

	cpu->tags_dirty = false;
}

typedef int (*fp_t)(uint8_t *RAM, void *grf, void *frf, debug_function_t fp);

#ifdef __GNUC__
void __attribute__((noinline))
breakpoint() {
asm("nop");
}
#else
void breakpoint() {}
#endif

int
cpu_run(cpu_t *cpu, debug_function_t debug_function)
{
	addr_t pc = 0, orig_pc = 0;
	uint32_t i;
	int ret;
	bool success;
	bool do_translate = true;

	/* try to find the entry in all functions */
	while(true) {
		if (do_translate) {
			cpu_translate(cpu);
			pc = cpu->f.get_pc(cpu, cpu->rf.grf);
		}

		orig_pc = pc;
		success = false;
		for (i = 0; i < cpu->functions; i++) {
			fp_t FP = (fp_t)cpu->fp[i];
			update_timing(cpu, TIMER_RUN, true);
			breakpoint();
			ret = FP(cpu->RAM, cpu->rf.grf, cpu->rf.frf, debug_function);
			update_timing(cpu, TIMER_RUN, false);
			pc = cpu->f.get_pc(cpu, cpu->rf.grf);
			if (ret != JIT_RETURN_FUNCNOTFOUND)
				return ret;
			if (!is_inside_code_area(cpu, pc))
				return ret;
			if (pc != orig_pc) {
				success = true;
				break;
			}
		}
		if (!success) {
			LOG("{%" PRIx64 "}", pc);
			cpu_tag(cpu, pc);
			do_translate = true;
		}
	}
}
//printf("%d\n", __LINE__);

void
cpu_flush(cpu_t *cpu)
{
	cpu->cur_func->eraseFromParent();

	cpu->functions = 0;

	// reset bb caching mapping
	cpu->func_bb.clear();

//	delete cpu->mod;
//	cpu->mod = NULL;
}

void
cpu_print_statistics(cpu_t *cpu)
{
	printf("tag = %8" PRId64 "\n", cpu->timer_total[TIMER_TAG]);
	printf("fe  = %8" PRId64 "\n", cpu->timer_total[TIMER_FE]);
	printf("be  = %8" PRId64 "\n", cpu->timer_total[TIMER_BE]);
	printf("run = %8" PRId64 "\n", cpu->timer_total[TIMER_RUN]);
}
//printf("%s:%d\n", __func__, __LINE__);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 midnightBITS
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "pch.h"
#include <http.hpp>
#include <utils.hpp>
#include <string.h>

#if defined WIN32
#define PLATFORM L"Win32"
#elif defined _MACOSX_
#define PLATFORM L"Mac OSX"
#elif defined ANDROID
#define PLATFORM L"Android"
#elif defined __GNUC__
#define PLATFORM "UNIX"
#endif

#include <string>
#include "curl_http.hpp"
#include <expat.hpp>

namespace http
{
	namespace impl
	{
		struct ContentData
		{
			void* content;
			size_t content_length;
			ContentData()
				: content(nullptr)
				, content_length(0)
			{
			}
			~ContentData()
			{
				free(content);
			}

			bool append(const void* data, size_t len)
			{
				void* copy = nullptr;
				if (data && len)
				{
					void* copy = realloc(content, content_length + len);
					if (!copy) return false;
					content = copy;
					memcpy((char*)content + content_length, data, len);
					content_length += len;
				}
				return true;
			}

			void clear()
			{
				free(content);
				content = nullptr;
				content_length = 0;
			}
		};

		class XmlHttpRequest
			: public http::XmlHttpRequest
			, public http::HttpCallback
		{
			std::weak_ptr<HttpCallback> m_self;

			ONREADYSTATECHANGE handler;
			void* handler_data;

			HTTP_METHOD http_method;
			std::string url;
			bool async;
			Headers request_headers;
			READY_STATE ready_state;
			ContentData body;

			int http_status;
			std::string reason;
			Headers response_headers;
			ContentData response;
			dom::XmlDocumentPtr doc;

			bool send_flag, done_flag, debug;
			ConnectionCallback* m_connection;

			void onReadyStateChange()
			{
				if (handler)
					handler(this, handler_data);
			}
			bool rebuildDOM();
			bool parseXML(const std::string& encoding);

			void clear_response()
			{
				http_status = 0;
				reason.clear();
				response_headers.clear();
				response.clear();
			}
		public:

			XmlHttpRequest()
				: handler(nullptr)
				, handler_data(nullptr)
				, http_method(HTTP_GET)
				, async(true)
				, ready_state(UNSENT)
				, http_status(0)
				, send_flag(false)
				, done_flag(false)
				, debug(false)
				, m_connection(nullptr)
			{
			}

			~XmlHttpRequest()
			{
			}

			void setSelfRef(HttpCallbackPtr self) { m_self = self; }
			void onreadystatechange(ONREADYSTATECHANGE handler, void* userdata);
			READY_STATE getReadyState() const;

			void open(HTTP_METHOD method, const std::string& url, bool async = true);
			void setRequestHeader(const std::string& header, const std::string& value);

			void setBody(const void* body, size_t length);
			void send();
			void abort();

			int getStatus() const;
			std::string getStatusText() const;
			std::string getResponseHeader(const std::string& name) const;
			std::map<std::string, std::string> getResponseHeaders() const;
			size_t getResponseTextLength() const;
			const char* getResponseText() const;
			dom::XmlDocumentPtr getResponseXml();

			void setDebug(bool debug);

			void onStart(ConnectionCallback* cb);
			void onError();
			void onFinish();
			size_t onData(const void* data, size_t count);
			void onHeaders(const std::string& reason, int http_status, const Headers& headers);

			void appendHeaders();
			std::string getUrl();
			void* getContent(size_t& length);
			bool getDebug();
		};

		void XmlHttpRequest::onreadystatechange(ONREADYSTATECHANGE handler, void* userdata)
		{
			//Synchronize on(*this);
			this->handler = handler;
			this->handler_data = userdata;
		}

		http::XmlHttpRequest::READY_STATE XmlHttpRequest::getReadyState() const
		{
			return ready_state;
		}

		void XmlHttpRequest::open(HTTP_METHOD method, const std::string& url, bool async)
		{
			abort();

			//Synchronize on(*this);

			send_flag = false;
			clear_response();
			request_headers.empty();

			http_method = method;
			this->url = url;
			this->async = async;

			ready_state = OPENED;
			onReadyStateChange();
		}

		void XmlHttpRequest::setRequestHeader(const std::string& header, const std::string& value)
		{
			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			auto _h = std::tolower(header);
			Headers::iterator _it = request_headers.find(_h);
			if (_it == request_headers.end())
			{
				if (_h == "accept-charset") return;
				if (_h == "accept-encoding") return;
				if (_h == "connection") return;
				if (_h == "content-length") return;
				if (_h == "cookie") return;
				if (_h == "cookie2") return;
				if (_h == "content-transfer-encoding") return;
				if (_h == "date") return;
				if (_h == "expect") return;
				if (_h == "host") return;
				if (_h == "keep-alive") return;
				if (_h == "referer") return;
				if (_h == "te") return;
				if (_h == "trailer") return;
				if (_h == "transfer-encoding") return;
				if (_h == "upgrade") return;
				if (_h == "user-agent") return;
				if (_h == "via") return;
				if (_h.substr(0, 6) == "proxy-") return;
				if (_h.substr(0, 4) == "sec-") return;
				request_headers[_h] = header +": " + value;
			}
			else
			{
				_it->second += ", " + value;
			}
		}

		void XmlHttpRequest::setBody(const void* body, size_t length)
		{
			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			this->body.clear();

			if (http_method != HTTP_POST) return;

			this->body.append(body, length);
		}

		void XmlHttpRequest::send()
		{
			abort();

			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			send_flag = true;
			done_flag = false;
			http::Send(m_self.lock(), async);
		}

		void XmlHttpRequest::abort()
		{
			if (m_connection)
				m_connection->abort();
			m_connection = nullptr;
		}

		int XmlHttpRequest::getStatus() const
		{
			return http_status;
		}

		std::string XmlHttpRequest::getStatusText() const
		{
			return reason;
		}

		std::string XmlHttpRequest::getResponseHeader(const std::string& name) const
		{
			Headers::const_iterator _it = response_headers.find(std::tolower(name));
			if (_it == response_headers.end()) return std::string();
			return _it->second;
		}

		std::map<std::string, std::string> XmlHttpRequest::getResponseHeaders() const
		{
			return response_headers;
		}

		size_t XmlHttpRequest::getResponseTextLength() const
		{
			return response.content_length;
		}

		const char* XmlHttpRequest::getResponseText() const
		{
			return (const char*)response.content;
		}

		dom::XmlDocumentPtr XmlHttpRequest::getResponseXml()
		{
			// Synchronize on (*this);

			if (ready_state != DONE || done_flag) return false;

			if (!doc && response.content && response.content_length)
				if (!rebuildDOM())
					return dom::XmlDocumentPtr();

			return doc;
		}

		void XmlHttpRequest::setDebug(bool debug)
		{
			this->debug = debug;
		}

		void XmlHttpRequest::onStart(ConnectionCallback* connection)
		{
			m_connection = connection;

			if (!body.content || !body.content_length)
				http_method = HTTP_GET;

			onReadyStateChange();
		}

		void XmlHttpRequest::onError()
		{
			ready_state = DONE;
			done_flag = true;
			onReadyStateChange();
		}

		void XmlHttpRequest::onFinish()
		{
			ready_state = DONE;
			onReadyStateChange();
		}

		size_t XmlHttpRequest::onData(const void* data, size_t count)
		{
			//Synchronize on(*this);

			size_t ret = response.append(data, count) ? count : 0;
			if (ret)
			{
				if (ready_state == HEADERS_RECEIVED) ready_state = LOADING;
				if (ready_state == LOADING)          onReadyStateChange();
			}
			return ret;
		}

		void XmlHttpRequest::onHeaders(const std::string& reason, int http_status, const Headers& headers)
		{
			//Synchronize on(*this);
			this->http_status = http_status;
			this->reason = reason;
			this->response_headers = headers;

			ready_state = HEADERS_RECEIVED;
			onReadyStateChange();
		}

		void XmlHttpRequest::appendHeaders()
		{
			if (!m_connection)
				return;
			//Synchronize on(*this);
			for (Headers::const_iterator _cur = request_headers.begin(); _cur != request_headers.end(); ++_cur)
				m_connection->appendHeader(_cur->second);
		}

		std::string XmlHttpRequest::getUrl() { return url; }
		void* XmlHttpRequest::getContent(size_t& length) { if (http_method == HTTP_POST) { length = body.content_length; return body.content; } return nullptr; }
		bool XmlHttpRequest::getDebug() { return debug; }

		static void getMimeAndEncoding(std::string ct, std::string& mime, std::string& enc)
		{
			mime.empty();
			enc.empty();
			size_t pos = ct.find_first_of(';');
			mime = std::tolower(ct.substr(0, pos));
			if (pos == std::string::npos) return;
			ct = ct.substr(pos+1);

			while(1)
			{
				pos = ct.find_first_of('=');
				if (pos == std::string::npos) return;
				std::string cand = ct.substr(0, pos);
				ct = ct.substr(pos+1);
				size_t low = 0, hi = cand.length();
				while (cand[low] && isspace((unsigned char)cand[low])) ++low;
				while (hi > low && isspace((unsigned char)cand[hi-1])) --hi;

				if (std::tolower(cand.substr(low, hi - low)) == "charset")
				{
					pos = ct.find_first_of(';');
					enc = ct.substr(0, pos);
					low = 0; hi = enc.length();
					while (enc[low] && isspace((unsigned char)enc[low])) ++low;
					while (hi > low && isspace((unsigned char)enc[hi-1])) --hi;
					enc = std::toupper(enc.substr(low, hi-low));
					return;
				}
				pos = ct.find_first_of(';');
				if (pos == std::string::npos) return;
				ct = ct.substr(pos+1);
			};
		}

		bool XmlHttpRequest::rebuildDOM()
		{
			std::string mime;
			std::string enc;
			getMimeAndEncoding(getResponseHeader("Content-Type"), mime, enc);

			if (mime == "" ||
				mime == "text/xml" ||
				mime == "application/xml" ||
				(mime.length() > 4 && mime.substr(mime.length()-4) == "+xml"))
			{
				return parseXML(enc);
			}

			return true;
		}

		class XHRParser: public xml::ExpatBase<XHRParser>
		{
			dom::XmlElementPtr elem;
			std::string text;

			void addText()
			{
				if (text.empty()) return;
				if (elem)
					elem->appendChild(doc->createTextNode(text));
				text.clear();
			}
		public:

			dom::XmlDocumentPtr doc;

			bool create(const char* cp)
			{
				doc = dom::XmlDocument::create();
				if (!doc) return false;
				return xml::ExpatBase<XHRParser>::create(cp);
			}

			void onStartElement(const XML_Char *name, const XML_Char **attrs)
			{
				addText();
				auto current = doc->createElement(name);
				if (!current) return;
				for (; *attrs; attrs += 2)
				{
					auto attr = doc->createAttribute(attrs[0], attrs[1]);
					if (!attr) continue;
					current->setAttribute(attr);
				}
				if (elem)
					elem->appendChild(current);
				else
					doc->setDocumentElement(current);
				elem = current;
			}

			void onEndElement(const XML_Char *name)
			{
				addText();
				if (!elem) return;
				dom::XmlNodePtr node = elem->parentNode();
				elem = std::static_pointer_cast<dom::XmlElement>(node);
			}

			void onCharacterData(const XML_Char *pszData, int nLength)
			{
				text += std::string(pszData, nLength);
			}
		};

		bool XmlHttpRequest::parseXML(const std::string& encoding)
		{
			const char* cp = NULL;
			if (encoding != "") cp = encoding.c_str();
			XHRParser parser;
			if (!parser.create(cp)) return false;
			parser.enableElementHandler();
			parser.enableCharacterDataHandler();
			if (!parser.parse((const char*)response.content, response.content_length))
				return false;
			doc = parser.doc;
			return true;
		}
	} // http::impl

	XmlHttpRequestPtr XmlHttpRequest::Create()
	{
		try {
		auto xhr = std::make_shared<impl::XmlHttpRequest>();
		HttpCallbackPtr self(xhr);
		xhr->setSelfRef(self);
		return xhr;
		} catch(std::bad_alloc) { return nullptr; }
	}
}
<commit_msg>Relaxing the header requirements<commit_after>/*
 * Copyright (C) 2013 midnightBITS
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "pch.h"
#include <http.hpp>
#include <utils.hpp>
#include <string.h>

#if defined WIN32
#define PLATFORM L"Win32"
#elif defined _MACOSX_
#define PLATFORM L"Mac OSX"
#elif defined ANDROID
#define PLATFORM L"Android"
#elif defined __GNUC__
#define PLATFORM "UNIX"
#endif

#include <string>
#include "curl_http.hpp"
#include <expat.hpp>

namespace http
{
	namespace impl
	{
		struct ContentData
		{
			void* content;
			size_t content_length;
			ContentData()
				: content(nullptr)
				, content_length(0)
			{
			}
			~ContentData()
			{
				free(content);
			}

			bool append(const void* data, size_t len)
			{
				void* copy = nullptr;
				if (data && len)
				{
					void* copy = realloc(content, content_length + len);
					if (!copy) return false;
					content = copy;
					memcpy((char*)content + content_length, data, len);
					content_length += len;
				}
				return true;
			}

			void clear()
			{
				free(content);
				content = nullptr;
				content_length = 0;
			}
		};

		class XmlHttpRequest
			: public http::XmlHttpRequest
			, public http::HttpCallback
		{
			std::weak_ptr<HttpCallback> m_self;

			ONREADYSTATECHANGE handler;
			void* handler_data;

			HTTP_METHOD http_method;
			std::string url;
			bool async;
			Headers request_headers;
			READY_STATE ready_state;
			ContentData body;

			int http_status;
			std::string reason;
			Headers response_headers;
			ContentData response;
			dom::XmlDocumentPtr doc;

			bool send_flag, done_flag, debug;
			ConnectionCallback* m_connection;

			void onReadyStateChange()
			{
				if (handler)
					handler(this, handler_data);
			}
			bool rebuildDOM(const char* forcedType = nullptr);
			bool parseXML(const std::string& encoding);

			void clear_response()
			{
				http_status = 0;
				reason.clear();
				response_headers.clear();
				response.clear();
			}
		public:

			XmlHttpRequest()
				: handler(nullptr)
				, handler_data(nullptr)
				, http_method(HTTP_GET)
				, async(true)
				, ready_state(UNSENT)
				, http_status(0)
				, send_flag(false)
				, done_flag(false)
				, debug(false)
				, m_connection(nullptr)
			{
			}

			~XmlHttpRequest()
			{
			}

			void setSelfRef(HttpCallbackPtr self) { m_self = self; }
			void onreadystatechange(ONREADYSTATECHANGE handler, void* userdata);
			READY_STATE getReadyState() const;

			void open(HTTP_METHOD method, const std::string& url, bool async = true);
			void setRequestHeader(const std::string& header, const std::string& value);

			void setBody(const void* body, size_t length);
			void send();
			void abort();

			int getStatus() const;
			std::string getStatusText() const;
			std::string getResponseHeader(const std::string& name) const;
			std::map<std::string, std::string> getResponseHeaders() const;
			size_t getResponseTextLength() const;
			const char* getResponseText() const;
			dom::XmlDocumentPtr getResponseXml();

			void setDebug(bool debug);

			void onStart(ConnectionCallback* cb);
			void onError();
			void onFinish();
			size_t onData(const void* data, size_t count);
			void onHeaders(const std::string& reason, int http_status, const Headers& headers);

			void appendHeaders();
			std::string getUrl();
			void* getContent(size_t& length);
			bool getDebug();
		};

		void XmlHttpRequest::onreadystatechange(ONREADYSTATECHANGE handler, void* userdata)
		{
			//Synchronize on(*this);
			this->handler = handler;
			this->handler_data = userdata;
		}

		http::XmlHttpRequest::READY_STATE XmlHttpRequest::getReadyState() const
		{
			return ready_state;
		}

		void XmlHttpRequest::open(HTTP_METHOD method, const std::string& url, bool async)
		{
			abort();

			//Synchronize on(*this);

			send_flag = false;
			clear_response();
			request_headers.empty();

			http_method = method;
			this->url = url;
			this->async = async;

			ready_state = OPENED;
			onReadyStateChange();
		}

		void XmlHttpRequest::setRequestHeader(const std::string& header, const std::string& value)
		{
			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			auto _h = std::tolower(header);
			Headers::iterator _it = request_headers.find(_h);
			if (_it == request_headers.end())
			{
				if (_h == "accept-charset") return;
				if (_h == "accept-encoding") return;
				if (_h == "connection") return;
				if (_h == "content-length") return;
				if (_h == "cookie") return;
				if (_h == "cookie2") return;
				if (_h == "content-transfer-encoding") return;
				if (_h == "date") return;
				if (_h == "expect") return;
				if (_h == "host") return;
				if (_h == "keep-alive") return;
				if (_h == "referer") return;
				if (_h == "te") return;
				if (_h == "trailer") return;
				if (_h == "transfer-encoding") return;
				if (_h == "upgrade") return;
				if (_h == "user-agent") return;
				if (_h == "via") return;
				if (_h.substr(0, 6) == "proxy-") return;
				if (_h.substr(0, 4) == "sec-") return;
				request_headers[_h] = header +": " + value;
			}
			else
			{
				_it->second += ", " + value;
			}
		}

		void XmlHttpRequest::setBody(const void* body, size_t length)
		{
			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			this->body.clear();

			if (http_method != HTTP_POST) return;

			this->body.append(body, length);
		}

		void XmlHttpRequest::send()
		{
			abort();

			//Synchronize on(*this);

			if (ready_state != OPENED || send_flag) return;

			send_flag = true;
			done_flag = false;
			http::Send(m_self.lock(), async);
		}

		void XmlHttpRequest::abort()
		{
			if (m_connection)
				m_connection->abort();
			m_connection = nullptr;
		}

		int XmlHttpRequest::getStatus() const
		{
			return http_status;
		}

		std::string XmlHttpRequest::getStatusText() const
		{
			return reason;
		}

		std::string XmlHttpRequest::getResponseHeader(const std::string& name) const
		{
			Headers::const_iterator _it = response_headers.find(std::tolower(name));
			if (_it == response_headers.end()) return std::string();
			return _it->second;
		}

		std::map<std::string, std::string> XmlHttpRequest::getResponseHeaders() const
		{
			return response_headers;
		}

		size_t XmlHttpRequest::getResponseTextLength() const
		{
			return response.content_length;
		}

		const char* XmlHttpRequest::getResponseText() const
		{
			return (const char*)response.content;
		}

		dom::XmlDocumentPtr XmlHttpRequest::getResponseXml()
		{
			// Synchronize on (*this);

			if (ready_state != DONE || done_flag) return false;

			if (!doc && response.content && response.content_length)
				if (!rebuildDOM("text/xml"))
					return dom::XmlDocumentPtr();

			return doc;
		}

		void XmlHttpRequest::setDebug(bool debug)
		{
			this->debug = debug;
		}

		void XmlHttpRequest::onStart(ConnectionCallback* connection)
		{
			m_connection = connection;

			if (!body.content || !body.content_length)
				http_method = HTTP_GET;

			onReadyStateChange();
		}

		void XmlHttpRequest::onError()
		{
			ready_state = DONE;
			done_flag = true;
			onReadyStateChange();
		}

		void XmlHttpRequest::onFinish()
		{
			ready_state = DONE;
			onReadyStateChange();
		}

		size_t XmlHttpRequest::onData(const void* data, size_t count)
		{
			//Synchronize on(*this);

			size_t ret = response.append(data, count) ? count : 0;
			if (ret)
			{
				if (ready_state == HEADERS_RECEIVED) ready_state = LOADING;
				if (ready_state == LOADING)          onReadyStateChange();
			}
			return ret;
		}

		void XmlHttpRequest::onHeaders(const std::string& reason, int http_status, const Headers& headers)
		{
			//Synchronize on(*this);
			this->http_status = http_status;
			this->reason = reason;
			this->response_headers = headers;

			ready_state = HEADERS_RECEIVED;
			onReadyStateChange();
		}

		void XmlHttpRequest::appendHeaders()
		{
			if (!m_connection)
				return;
			//Synchronize on(*this);
			for (Headers::const_iterator _cur = request_headers.begin(); _cur != request_headers.end(); ++_cur)
				m_connection->appendHeader(_cur->second);
		}

		std::string XmlHttpRequest::getUrl() { return url; }
		void* XmlHttpRequest::getContent(size_t& length) { if (http_method == HTTP_POST) { length = body.content_length; return body.content; } return nullptr; }
		bool XmlHttpRequest::getDebug() { return debug; }

		static void getMimeAndEncoding(std::string ct, std::string& mime, std::string& enc)
		{
			mime.empty();
			enc.empty();
			size_t pos = ct.find_first_of(';');
			mime = std::tolower(ct.substr(0, pos));
			if (pos == std::string::npos) return;
			ct = ct.substr(pos+1);

			while(1)
			{
				pos = ct.find_first_of('=');
				if (pos == std::string::npos) return;
				std::string cand = ct.substr(0, pos);
				ct = ct.substr(pos+1);
				size_t low = 0, hi = cand.length();
				while (cand[low] && isspace((unsigned char)cand[low])) ++low;
				while (hi > low && isspace((unsigned char)cand[hi-1])) --hi;

				if (std::tolower(cand.substr(low, hi - low)) == "charset")
				{
					pos = ct.find_first_of(';');
					enc = ct.substr(0, pos);
					low = 0; hi = enc.length();
					while (enc[low] && isspace((unsigned char)enc[low])) ++low;
					while (hi > low && isspace((unsigned char)enc[hi-1])) --hi;
					enc = std::toupper(enc.substr(low, hi-low));
					return;
				}
				pos = ct.find_first_of(';');
				if (pos == std::string::npos) return;
				ct = ct.substr(pos+1);
			};
		}

		bool XmlHttpRequest::rebuildDOM(const char* forcedType)
		{
			std::string forced = forcedType ? forcedType : std::string();
			std::string mime;
			std::string enc;
			getMimeAndEncoding(getResponseHeader("Content-Type"), mime, enc);

			if (mime == "" ||
				mime == "text/xml" ||
				mime == "application/xml" ||
				(mime.length() > 4 && mime.substr(mime.length()-4) == "+xml") ||
				forcedType != nullptr && (
					forced == "text/xml" ||
					forced == "application/xml" ||
					(forced.length() > 4 && forced.substr(mime.length()-4) == "+xml")
				))
			{
				return parseXML(enc);
			}

			return true;
		}

		class XHRParser: public xml::ExpatBase<XHRParser>
		{
			dom::XmlElementPtr elem;
			std::string text;

			void addText()
			{
				if (text.empty()) return;
				if (elem)
					elem->appendChild(doc->createTextNode(text));
				text.clear();
			}
		public:

			dom::XmlDocumentPtr doc;

			bool create(const char* cp)
			{
				doc = dom::XmlDocument::create();
				if (!doc) return false;
				return xml::ExpatBase<XHRParser>::create(cp);
			}

			void onStartElement(const XML_Char *name, const XML_Char **attrs)
			{
				addText();
				auto current = doc->createElement(name);
				if (!current) return;
				for (; *attrs; attrs += 2)
				{
					auto attr = doc->createAttribute(attrs[0], attrs[1]);
					if (!attr) continue;
					current->setAttribute(attr);
				}
				if (elem)
					elem->appendChild(current);
				else
					doc->setDocumentElement(current);
				elem = current;
			}

			void onEndElement(const XML_Char *name)
			{
				addText();
				if (!elem) return;
				dom::XmlNodePtr node = elem->parentNode();
				elem = std::static_pointer_cast<dom::XmlElement>(node);
			}

			void onCharacterData(const XML_Char *pszData, int nLength)
			{
				text += std::string(pszData, nLength);
			}
		};

		bool XmlHttpRequest::parseXML(const std::string& encoding)
		{
			const char* cp = NULL;
			if (encoding != "") cp = encoding.c_str();
			XHRParser parser;
			if (!parser.create(cp)) return false;
			parser.enableElementHandler();
			parser.enableCharacterDataHandler();
			if (!parser.parse((const char*)response.content, response.content_length))
				return false;
			doc = parser.doc;
			return true;
		}
	} // http::impl

	XmlHttpRequestPtr XmlHttpRequest::Create()
	{
		try {
		auto xhr = std::make_shared<impl::XmlHttpRequest>();
		HttpCallbackPtr self(xhr);
		xhr->setSelfRef(self);
		return xhr;
		} catch(std::bad_alloc) { return nullptr; }
	}
}
<|endoftext|>


<commit_before>#include <cstdio>

typedef unsigned int u32;
#define BUF 65536
class Reader {
    char buf[BUF]; char b; int bi, bz;
public:
    Reader() { bi=bz=0; read(); }
    void read() {
        if (bi==bz) { bi=0; bz = fread(buf, 1, BUF, stdin); }
        b = bz ? buf[bi++] : 0; }
    void skip() { while (b > 0 && b <= 32) read(); }
    u32 nextU32() {
        u32 v = 0; for (skip(); b > 32; read()) v = v*10 + b-48; return v; }
};

u32 n, k, i, d;

int main(void)
{
    Reader _r;
    n = _r.nextU32();
    k = _r.nextU32();
    d = 0;
    while (n--) {
        i = _r.nextU32();
        if (i % k == 0) d++;
    }
    printf ("%d\n", d);

    return 0;
}
<commit_msg>Simplified reader code<commit_after>#include <cstdio>

typedef unsigned int u32;

struct Reader {
    char b;
    Reader() { read(); }
    void read() { int r = fgetc_unlocked(stdin); b = r == EOF ? 0 : r; }
    void skip() { while (b > 0 && b <= 32) read(); }
    u32 next_u32() {
        u32 v = 0; for (skip(); b > 32; read()) v = v*10 + b-48; return v; }
};

u32 n, k, i, d;

int main(void)
{
    Reader rr;

    n = rr.next_u32();
    k = rr.next_u32();
    d = 0;
    while (n--) {
        i = rr.next_u32();
        if (i % k == 0) d++;
    }
    printf ("%d\n", d);

    return 0;
}
<|endoftext|>