<commit_before>/***********************************************************************
	filename: 	CEGUIWindowManager.cpp
	created:	21/2/2004
	author:		Paul D Turner

	purpose:	Implements the WindowManager class
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   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 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 "CEGUI/WindowManager.h"
#include "CEGUI/WindowFactoryManager.h"
#include "CEGUI/WindowFactory.h"
#include "CEGUI/Window.h"
#include "CEGUI/Exceptions.h"
#include "CEGUI/GUILayout_xmlHandler.h"
#include "CEGUI/XMLParser.h"
#include "CEGUI/RenderEffectManager.h"
#include "CEGUI/RenderingWindow.h"
#include <iostream>
#include <sstream>
#include <algorithm>

// Start of CEGUI namespace section
namespace CEGUI
{
/*************************************************************************
	Static Data Definitions
*************************************************************************/
// singleton instance pointer
template<> WindowManager* Singleton<WindowManager>::ms_Singleton	= 0;
// default resource group
String WindowManager::d_defaultResourceGroup;

/*************************************************************************
	Definition of constant data for WindowManager
*************************************************************************/
// Declared in WindowManager
const String WindowManager::GUILayoutSchemaName("GUILayout.xsd");
const String WindowManager::GeneratedWindowNameBase("__cewin_uid_");
const String WindowManager::EventNamespace("WindowManager");
const String WindowManager::EventWindowCreated("WindowCreated");
const String WindowManager::EventWindowDestroyed("WindowDestroyed");

/*************************************************************************
    Constructor
*************************************************************************/
WindowManager::WindowManager(void) :
    d_uid_counter(0),
    d_lockCount(0)
{
    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(this));
    Logger::getSingleton().logEvent(
        "CEGUI::WindowManager singleton created " + String(addr_buff));
}

/*************************************************************************
	Destructor
*************************************************************************/
WindowManager::~WindowManager(void)
{
	destroyAllWindows();
    cleanDeadPool();

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(this));
    Logger::getSingleton().logEvent(
        "CEGUI::WindowManager singleton destroyed " + String(addr_buff));
}

/*************************************************************************
	Create a new window of the specified type
*************************************************************************/
Window* WindowManager::createWindow(const String& type, const String& name)
{
    // only allow creation of Window objects if we are in unlocked state
    if (isLocked())
        CEGUI_THROW(InvalidRequestException("WindowManager::createWindow - "
            "WindowManager is in the locked state."));

    String finalName(name.empty() ? generateUniqueWindowName() : name);

    WindowFactoryManager& wfMgr = WindowFactoryManager::getSingleton();
    WindowFactory* factory = wfMgr.getFactory(type);

    Window* newWindow = factory->createWindow(finalName);

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(newWindow));
    Logger::getSingleton().logEvent("Window '" + finalName +"' of type '" +
        type + "' has been created. " + addr_buff, Informative);

    // see if we need to assign a look to this window
    if (wfMgr.isFalagardMappedType(type))
    {
        const WindowFactoryManager::FalagardWindowMapping& fwm = wfMgr.getFalagardMappingForType(type);
        // this was a mapped type, so assign a look to the window so it can finalise
        // its initialisation
        newWindow->d_falagardType = type;
        newWindow->setWindowRenderer(fwm.d_rendererType);
        newWindow->setLookNFeel(fwm.d_lookName);

        initialiseRenderEffect(newWindow, fwm.d_effectName);
    }

	d_windowRegistry.push_back(newWindow);

    // fire event to notify interested parites about the new window.
    WindowEventArgs args(newWindow);
    fireEvent(EventWindowCreated, args, EventNamespace);

	return newWindow;
}

//---------------------------------------------------------------------------//
void WindowManager::initialiseRenderEffect(
        Window* wnd, const String& effect) const
{
    Logger& logger(Logger::getSingleton());

    // nothing to do if effect is empty string
    if (effect.empty())
        return;

    // if requested RenderEffect is not available, log that and continue
    if (!RenderEffectManager::getSingleton().isEffectAvailable(effect))
    {
        logger.logEvent("Missing RenderEffect '" + effect + "' requested for "
            "window '" + wnd->getName() + "' - continuing without effect...",
            Errors);

       return;
    }

    // If we do not have a RenderingSurface, enable AutoRenderingSurface to
    // try and create one
    if (!wnd->getRenderingSurface())
    {
        logger.logEvent("Enabling AutoRenderingSurface on '" +
            wnd->getName() + "' for RenderEffect support.");

        wnd->setUsingAutoRenderingSurface(true);
    }

    // If we have a RenderingSurface and it's a RenderingWindow
    if (wnd->getRenderingSurface() &&
        wnd->getRenderingSurface()->isRenderingWindow())
    {
        // Set an instance of the requested RenderEffect
        static_cast<RenderingWindow*>(wnd->getRenderingSurface())->
                setRenderEffect(&RenderEffectManager::getSingleton().
                        create(effect));
    }
    // log fact that we could not get a usable RenderingSurface
    else
    {
        logger.logEvent("Unable to set effect for window '" +
            wnd->getName() + "' since RenderingSurface is either missing "
            "or of wrong type (i.e. not a RenderingWindow).",
            Errors);
    }
}

/*************************************************************************
	Destroy the given window by pointer
*************************************************************************/
void WindowManager::destroyWindow(Window* window)
{
	WindowVector::iterator iter =
        std::find(d_windowRegistry.begin(),
                  d_windowRegistry.end(),
                  window);

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(&window));

	if (iter == d_windowRegistry.end())
    {
        Logger::getSingleton().logEvent("[WindowManager] Attempt to delete "
            "Window that does not exist!  Address was: " + String(addr_buff) +
            ". WARNING: This could indicate a double-deletion issue!!",
            Errors);
        return;
    }

    d_windowRegistry.erase(iter);

    Logger::getSingleton().logEvent("Window at '" + window->getNamePath() +
        "' will be added to dead pool. " + addr_buff, Informative);

    // do 'safe' part of cleanup
    window->destroy();

    // add window to dead pool
    d_deathrow.push_back(window);

    // fire event to notify interested parites about window destruction.
    // TODO: Perhaps this should fire first, so window is still usable?
    WindowEventArgs args(window);
    fireEvent(EventWindowDestroyed, args, EventNamespace);
}

/*************************************************************************
	Destroy all Window objects
*************************************************************************/
void WindowManager::destroyAllWindows(void)
{
	while (!d_windowRegistry.empty())
		destroyWindow(*d_windowRegistry.begin());
}

//----------------------------------------------------------------------------//
bool WindowManager::isAlive(const Window* window) const
{
	WindowVector::const_iterator iter =
        std::find(d_windowRegistry.begin(),
                  d_windowRegistry.end(),
                  window);

	return iter != d_windowRegistry.end();
}

Window* WindowManager::loadLayoutFromContainer(const RawDataContainer& source, PropertyCallback* callback, void* userdata)
{
    // log the fact we are about to load a layout
    Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from a RawDataContainer ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

    // do parse (which uses handler to create actual data)
    CEGUI_TRY
    {
        System::getSingleton().getXMLParser()->parseXML(handler, source, GUILayoutSchemaName);
    }
    CEGUI_CATCH(...)
    {
        Logger::getSingleton().logEvent("WindowManager::loadWindowLayout - loading of layout from a RawDataContainer failed.", Errors);
        CEGUI_RETHROW;
    }

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from a RawDataContainer ----", Standard);

    return handler.getLayoutRootWindow();
}

Window* WindowManager::loadLayoutFromFile(const String& filename, const String& resourceGroup, PropertyCallback* callback, void* userdata)
{
	if (filename.empty())
	{
		CEGUI_THROW(InvalidRequestException("WindowManager::loadWindowLayout - Filename supplied for gui-layout loading must be valid."));
	}

	// log the fact we are about to load a layout
	Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from '" + filename + "' ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

	// do parse (which uses handler to create actual data)
	CEGUI_TRY
	{
        System::getSingleton().getXMLParser()->parseXMLFile(handler,
            filename, GUILayoutSchemaName, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup);
	}
	CEGUI_CATCH(...)
	{
        Logger::getSingleton().logEvent("WindowManager::loadLayoutFromFile - loading of layout from file '" + filename +"' failed.", Errors);
        CEGUI_RETHROW;
	}

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from '" + filename + "' ----", Standard);

	return handler.getLayoutRootWindow();
}

Window* WindowManager::loadLayoutFromString(const String& source, PropertyCallback* callback, void* userdata)
{
    // log the fact we are about to load a layout
    Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from string ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

    // do parse (which uses handler to create actual data)
    CEGUI_TRY
    {
        System::getSingleton().getXMLParser()->parseXMLString(handler, source, GUILayoutSchemaName);
    }
    CEGUI_CATCH(...)
    {
        Logger::getSingleton().logEvent("WindowManager::loadLayoutFromString - loading of layout from string failed.", Errors);
        CEGUI_RETHROW;
    }

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from string ----", Standard);

    return handler.getLayoutRootWindow();
}

bool WindowManager::isDeadPoolEmpty(void) const
{
    return d_deathrow.empty();
}

void WindowManager::cleanDeadPool(void)
{
    WindowVector::reverse_iterator curr = d_deathrow.rbegin();
    for (; curr != d_deathrow.rend(); ++curr)
    {
// in debug mode, log what gets cleaned from the dead pool (insane level)
#if defined(DEBUG) || defined (_DEBUG)
        CEGUI_LOGINSANE("Window '" + (*curr)->getName() + "' about to be finally destroyed from dead pool.");
#endif

        WindowFactory* factory = WindowFactoryManager::getSingleton().getFactory((*curr)->getType());
        factory->destroyWindow(*curr);
    }

    // all done here, so clear all pointers from dead pool
    d_deathrow.clear();
}

void WindowManager::writeLayoutToStream(const Window& window, OutStream& out_stream) const
{

    XMLSerializer xml(out_stream);
    // output GUILayout start element
    xml.openTag(GUILayout_xmlHandler::GUILayoutElement);
    xml.attribute(GUILayout_xmlHandler::GUILayoutVersionAttribute,
                  GUILayout_xmlHandler::NativeVersion);

    // write windows
    window.writeXMLToStream(xml);
    // write closing GUILayout element
    xml.closeTag();
}

String WindowManager::getLayoutAsString(const Window& window) const
{
    std::ostringstream str;
    writeLayoutToStream(window, str);

    return String(str.str());
}

//----------------------------------------------------------------------------//
void WindowManager::saveLayoutToFile(const Window& window,
                                     const String& filename) const
{
    std::ofstream stream(filename.c_str());

    if (!stream.good())
        CEGUI_THROW(FileIOException("WindowManager::saveWindowLayout: "
            "failed to create stream for writing."));

    writeLayoutToStream(window, stream);
}

String WindowManager::generateUniqueWindowName()
{
    // build name
    std::ostringstream uidname;
    uidname << GeneratedWindowNameBase.c_str() << d_uid_counter;

    // update counter for next time
    unsigned long old_uid = d_uid_counter;
    ++d_uid_counter;

    // log if we ever wrap-around (which should be pretty unlikely)
    if (d_uid_counter < old_uid)
        Logger::getSingleton().logEvent("UID counter for generated window names has wrapped around - the fun shall now commence!");

    // return generated name as a CEGUI::String.
    return String(uidname.str().c_str());
}

/*************************************************************************
	Return a WindowManager::WindowIterator object to iterate over the
	currently defined Windows.
*************************************************************************/
WindowManager::WindowIterator WindowManager::getIterator(void) const
{
	return WindowIterator(d_windowRegistry.begin(), d_windowRegistry.end());
}

/*************************************************************************
    Outputs the names of ALL existing windows to log (DEBUG function).
*************************************************************************/
void WindowManager::DEBUG_dumpWindowNames(String zone) const
{
    Logger::getSingleton().logEvent("WINDOW NAMES DUMP (" + zone + ")");
    Logger::getSingleton().logEvent("-----------------");

    for (WindowVector::const_iterator i = d_windowRegistry.begin();
         i != d_windowRegistry.end();
         ++i)
    {
        Logger::getSingleton().logEvent("Window : " + (*i)->getNamePath());
    }
    Logger::getSingleton().logEvent("-----------------");
}

//----------------------------------------------------------------------------//
void WindowManager::lock()
{
    ++d_lockCount;
}

//----------------------------------------------------------------------------//
void WindowManager::unlock()
{
    if (d_lockCount > 0)
        --d_lockCount;
}

//----------------------------------------------------------------------------//
bool WindowManager::isLocked() const
{
    return d_lockCount != 0;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<commit_msg>WindowManager::getLayoutAsString doesn't mangle utf8 content any more<commit_after>/***********************************************************************
	filename: 	CEGUIWindowManager.cpp
	created:	21/2/2004
	author:		Paul D Turner

	purpose:	Implements the WindowManager class
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2006 Paul D Turner & The CEGUI Development Team
 *
 *   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 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 "CEGUI/WindowManager.h"
#include "CEGUI/WindowFactoryManager.h"
#include "CEGUI/WindowFactory.h"
#include "CEGUI/Window.h"
#include "CEGUI/Exceptions.h"
#include "CEGUI/GUILayout_xmlHandler.h"
#include "CEGUI/XMLParser.h"
#include "CEGUI/RenderEffectManager.h"
#include "CEGUI/RenderingWindow.h"
#include <iostream>
#include <sstream>
#include <algorithm>

// Start of CEGUI namespace section
namespace CEGUI
{
/*************************************************************************
	Static Data Definitions
*************************************************************************/
// singleton instance pointer
template<> WindowManager* Singleton<WindowManager>::ms_Singleton	= 0;
// default resource group
String WindowManager::d_defaultResourceGroup;

/*************************************************************************
	Definition of constant data for WindowManager
*************************************************************************/
// Declared in WindowManager
const String WindowManager::GUILayoutSchemaName("GUILayout.xsd");
const String WindowManager::GeneratedWindowNameBase("__cewin_uid_");
const String WindowManager::EventNamespace("WindowManager");
const String WindowManager::EventWindowCreated("WindowCreated");
const String WindowManager::EventWindowDestroyed("WindowDestroyed");

/*************************************************************************
    Constructor
*************************************************************************/
WindowManager::WindowManager(void) :
    d_uid_counter(0),
    d_lockCount(0)
{
    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(this));
    Logger::getSingleton().logEvent(
        "CEGUI::WindowManager singleton created " + String(addr_buff));
}

/*************************************************************************
	Destructor
*************************************************************************/
WindowManager::~WindowManager(void)
{
	destroyAllWindows();
    cleanDeadPool();

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(this));
    Logger::getSingleton().logEvent(
        "CEGUI::WindowManager singleton destroyed " + String(addr_buff));
}

/*************************************************************************
	Create a new window of the specified type
*************************************************************************/
Window* WindowManager::createWindow(const String& type, const String& name)
{
    // only allow creation of Window objects if we are in unlocked state
    if (isLocked())
        CEGUI_THROW(InvalidRequestException("WindowManager::createWindow - "
            "WindowManager is in the locked state."));

    String finalName(name.empty() ? generateUniqueWindowName() : name);

    WindowFactoryManager& wfMgr = WindowFactoryManager::getSingleton();
    WindowFactory* factory = wfMgr.getFactory(type);

    Window* newWindow = factory->createWindow(finalName);

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(newWindow));
    Logger::getSingleton().logEvent("Window '" + finalName +"' of type '" +
        type + "' has been created. " + addr_buff, Informative);

    // see if we need to assign a look to this window
    if (wfMgr.isFalagardMappedType(type))
    {
        const WindowFactoryManager::FalagardWindowMapping& fwm = wfMgr.getFalagardMappingForType(type);
        // this was a mapped type, so assign a look to the window so it can finalise
        // its initialisation
        newWindow->d_falagardType = type;
        newWindow->setWindowRenderer(fwm.d_rendererType);
        newWindow->setLookNFeel(fwm.d_lookName);

        initialiseRenderEffect(newWindow, fwm.d_effectName);
    }

	d_windowRegistry.push_back(newWindow);

    // fire event to notify interested parites about the new window.
    WindowEventArgs args(newWindow);
    fireEvent(EventWindowCreated, args, EventNamespace);

	return newWindow;
}

//---------------------------------------------------------------------------//
void WindowManager::initialiseRenderEffect(
        Window* wnd, const String& effect) const
{
    Logger& logger(Logger::getSingleton());

    // nothing to do if effect is empty string
    if (effect.empty())
        return;

    // if requested RenderEffect is not available, log that and continue
    if (!RenderEffectManager::getSingleton().isEffectAvailable(effect))
    {
        logger.logEvent("Missing RenderEffect '" + effect + "' requested for "
            "window '" + wnd->getName() + "' - continuing without effect...",
            Errors);

       return;
    }

    // If we do not have a RenderingSurface, enable AutoRenderingSurface to
    // try and create one
    if (!wnd->getRenderingSurface())
    {
        logger.logEvent("Enabling AutoRenderingSurface on '" +
            wnd->getName() + "' for RenderEffect support.");

        wnd->setUsingAutoRenderingSurface(true);
    }

    // If we have a RenderingSurface and it's a RenderingWindow
    if (wnd->getRenderingSurface() &&
        wnd->getRenderingSurface()->isRenderingWindow())
    {
        // Set an instance of the requested RenderEffect
        static_cast<RenderingWindow*>(wnd->getRenderingSurface())->
                setRenderEffect(&RenderEffectManager::getSingleton().
                        create(effect));
    }
    // log fact that we could not get a usable RenderingSurface
    else
    {
        logger.logEvent("Unable to set effect for window '" +
            wnd->getName() + "' since RenderingSurface is either missing "
            "or of wrong type (i.e. not a RenderingWindow).",
            Errors);
    }
}

/*************************************************************************
	Destroy the given window by pointer
*************************************************************************/
void WindowManager::destroyWindow(Window* window)
{
	WindowVector::iterator iter =
        std::find(d_windowRegistry.begin(),
                  d_windowRegistry.end(),
                  window);

    char addr_buff[32];
    sprintf(addr_buff, "(%p)", static_cast<void*>(&window));

	if (iter == d_windowRegistry.end())
    {
        Logger::getSingleton().logEvent("[WindowManager] Attempt to delete "
            "Window that does not exist!  Address was: " + String(addr_buff) +
            ". WARNING: This could indicate a double-deletion issue!!",
            Errors);
        return;
    }

    d_windowRegistry.erase(iter);

    Logger::getSingleton().logEvent("Window at '" + window->getNamePath() +
        "' will be added to dead pool. " + addr_buff, Informative);

    // do 'safe' part of cleanup
    window->destroy();

    // add window to dead pool
    d_deathrow.push_back(window);

    // fire event to notify interested parites about window destruction.
    // TODO: Perhaps this should fire first, so window is still usable?
    WindowEventArgs args(window);
    fireEvent(EventWindowDestroyed, args, EventNamespace);
}

/*************************************************************************
	Destroy all Window objects
*************************************************************************/
void WindowManager::destroyAllWindows(void)
{
	while (!d_windowRegistry.empty())
		destroyWindow(*d_windowRegistry.begin());
}

//----------------------------------------------------------------------------//
bool WindowManager::isAlive(const Window* window) const
{
	WindowVector::const_iterator iter =
        std::find(d_windowRegistry.begin(),
                  d_windowRegistry.end(),
                  window);

	return iter != d_windowRegistry.end();
}

Window* WindowManager::loadLayoutFromContainer(const RawDataContainer& source, PropertyCallback* callback, void* userdata)
{
    // log the fact we are about to load a layout
    Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from a RawDataContainer ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

    // do parse (which uses handler to create actual data)
    CEGUI_TRY
    {
        System::getSingleton().getXMLParser()->parseXML(handler, source, GUILayoutSchemaName);
    }
    CEGUI_CATCH(...)
    {
        Logger::getSingleton().logEvent("WindowManager::loadWindowLayout - loading of layout from a RawDataContainer failed.", Errors);
        CEGUI_RETHROW;
    }

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from a RawDataContainer ----", Standard);

    return handler.getLayoutRootWindow();
}

Window* WindowManager::loadLayoutFromFile(const String& filename, const String& resourceGroup, PropertyCallback* callback, void* userdata)
{
	if (filename.empty())
	{
		CEGUI_THROW(InvalidRequestException("WindowManager::loadWindowLayout - Filename supplied for gui-layout loading must be valid."));
	}

	// log the fact we are about to load a layout
	Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from '" + filename + "' ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

	// do parse (which uses handler to create actual data)
	CEGUI_TRY
	{
        System::getSingleton().getXMLParser()->parseXMLFile(handler,
            filename, GUILayoutSchemaName, resourceGroup.empty() ? d_defaultResourceGroup : resourceGroup);
	}
	CEGUI_CATCH(...)
	{
        Logger::getSingleton().logEvent("WindowManager::loadLayoutFromFile - loading of layout from file '" + filename +"' failed.", Errors);
        CEGUI_RETHROW;
	}

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from '" + filename + "' ----", Standard);

	return handler.getLayoutRootWindow();
}

Window* WindowManager::loadLayoutFromString(const String& source, PropertyCallback* callback, void* userdata)
{
    // log the fact we are about to load a layout
    Logger::getSingleton().logEvent("---- Beginning loading of GUI layout from string ----", Informative);

    // create handler object
    GUILayout_xmlHandler handler(callback, userdata);

    // do parse (which uses handler to create actual data)
    CEGUI_TRY
    {
        System::getSingleton().getXMLParser()->parseXMLString(handler, source, GUILayoutSchemaName);
    }
    CEGUI_CATCH(...)
    {
        Logger::getSingleton().logEvent("WindowManager::loadLayoutFromString - loading of layout from string failed.", Errors);
        CEGUI_RETHROW;
    }

    // log the completion of loading
    Logger::getSingleton().logEvent("---- Successfully completed loading of GUI layout from string ----", Standard);

    return handler.getLayoutRootWindow();
}

bool WindowManager::isDeadPoolEmpty(void) const
{
    return d_deathrow.empty();
}

void WindowManager::cleanDeadPool(void)
{
    WindowVector::reverse_iterator curr = d_deathrow.rbegin();
    for (; curr != d_deathrow.rend(); ++curr)
    {
// in debug mode, log what gets cleaned from the dead pool (insane level)
#if defined(DEBUG) || defined (_DEBUG)
        CEGUI_LOGINSANE("Window '" + (*curr)->getName() + "' about to be finally destroyed from dead pool.");
#endif

        WindowFactory* factory = WindowFactoryManager::getSingleton().getFactory((*curr)->getType());
        factory->destroyWindow(*curr);
    }

    // all done here, so clear all pointers from dead pool
    d_deathrow.clear();
}

void WindowManager::writeLayoutToStream(const Window& window, OutStream& out_stream) const
{

    XMLSerializer xml(out_stream);
    // output GUILayout start element
    xml.openTag(GUILayout_xmlHandler::GUILayoutElement);
    xml.attribute(GUILayout_xmlHandler::GUILayoutVersionAttribute,
                  GUILayout_xmlHandler::NativeVersion);

    // write windows
    window.writeXMLToStream(xml);
    // write closing GUILayout element
    xml.closeTag();
}

String WindowManager::getLayoutAsString(const Window& window) const
{
    std::ostringstream str;
    writeLayoutToStream(window, str);

#if CEGUI_STRING_CLASS == CEGUI_STRING_CLASS_UNICODE
    // we are holding utf8 string in std::string which is something CEGUI::String doesn't expect,
    // therefore we are forcing it to treat it as utf8 by this cast
    return String(reinterpret_cast<const utf8*>(str.str().c_str()));
#else
    return String(str.str());
#endif
}

//----------------------------------------------------------------------------//
void WindowManager::saveLayoutToFile(const Window& window,
                                     const String& filename) const
{
    std::ofstream stream(filename.c_str());

    if (!stream.good())
        CEGUI_THROW(FileIOException("WindowManager::saveWindowLayout: "
            "failed to create stream for writing."));

    writeLayoutToStream(window, stream);
}

String WindowManager::generateUniqueWindowName()
{
    // build name
    std::ostringstream uidname;
    uidname << GeneratedWindowNameBase.c_str() << d_uid_counter;

    // update counter for next time
    unsigned long old_uid = d_uid_counter;
    ++d_uid_counter;

    // log if we ever wrap-around (which should be pretty unlikely)
    if (d_uid_counter < old_uid)
        Logger::getSingleton().logEvent("UID counter for generated window names has wrapped around - the fun shall now commence!");

    // return generated name as a CEGUI::String.
    return String(uidname.str().c_str());
}

/*************************************************************************
	Return a WindowManager::WindowIterator object to iterate over the
	currently defined Windows.
*************************************************************************/
WindowManager::WindowIterator WindowManager::getIterator(void) const
{
	return WindowIterator(d_windowRegistry.begin(), d_windowRegistry.end());
}

/*************************************************************************
    Outputs the names of ALL existing windows to log (DEBUG function).
*************************************************************************/
void WindowManager::DEBUG_dumpWindowNames(String zone) const
{
    Logger::getSingleton().logEvent("WINDOW NAMES DUMP (" + zone + ")");
    Logger::getSingleton().logEvent("-----------------");

    for (WindowVector::const_iterator i = d_windowRegistry.begin();
         i != d_windowRegistry.end();
         ++i)
    {
        Logger::getSingleton().logEvent("Window : " + (*i)->getNamePath());
    }
    Logger::getSingleton().logEvent("-----------------");
}

//----------------------------------------------------------------------------//
void WindowManager::lock()
{
    ++d_lockCount;
}

//----------------------------------------------------------------------------//
void WindowManager::unlock()
{
    if (d_lockCount > 0)
        --d_lockCount;
}

//----------------------------------------------------------------------------//
bool WindowManager::isLocked() const
{
    return d_lockCount != 0;
}

//----------------------------------------------------------------------------//

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>#include "SwiftOutputRewrite.h"
#include "swift/extractor/SwiftExtractorConfiguration.h"

#include <llvm/ADT/SmallString.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <swift/Basic/OutputFileMap.h>
#include <swift/Basic/Platform.h>
#include <unistd.h>
#include <unordered_set>
#include <optional>
#include <iostream>

// Creates a copy of the output file map and updates remapping table in place
// It does not change the original map file as it is depended upon by the original compiler
// Returns path to the newly created output file map on success, or None in a case of failure
static std::optional<std::string> rewriteOutputFileMap(
    const codeql::SwiftExtractorConfiguration& config,
    const std::string& outputFileMapPath,
    const std::vector<std::string>& inputs,
    std::unordered_map<std::string, std::string>& remapping) {
  auto newPath = config.tempArtifactDir + '/' + outputFileMapPath;

  // TODO: do not assume absolute path for the second parameter
  auto outputMapOrError = swift::OutputFileMap::loadFromPath(outputFileMapPath, "");
  if (!outputMapOrError) {
    return std::nullopt;
  }
  auto oldOutputMap = outputMapOrError.get();
  swift::OutputFileMap newOutputMap;
  std::vector<llvm::StringRef> keys;
  for (auto& key : inputs) {
    auto oldMap = oldOutputMap.getOutputMapForInput(key);
    if (!oldMap) {
      continue;
    }
    keys.push_back(key);
    auto& newMap = newOutputMap.getOrCreateOutputMapForInput(key);
    newMap.copyFrom(*oldMap);
    for (auto& entry : newMap) {
      auto oldPath = entry.getSecond();
      auto newPath = config.tempArtifactDir + '/' + oldPath;
      entry.getSecond() = newPath;
      remapping[oldPath] = newPath;
    }
  }
  std::error_code ec;
  llvm::SmallString<PATH_MAX> filepath(newPath);
  llvm::StringRef parent = llvm::sys::path::parent_path(filepath);
  if (std::error_code ec = llvm::sys::fs::create_directories(parent)) {
    std::cerr << "Cannot create relocated output map dir: '" << parent.str()
              << "': " << ec.message() << "\n";
    return std::nullopt;
  }

  llvm::raw_fd_ostream fd(newPath, ec, llvm::sys::fs::OF_None);
  newOutputMap.write(fd, keys);
  return newPath;
}

// This is an Xcode-specific workaround to produce alias names for an existing .swiftmodule file.
// In the case of Xcode, it calls the Swift compiler and asks it to produce a Swift module.
// Once it's done, Xcode moves the .swiftmodule file in another location, and the location is
// rather arbitrary. Here are examples of such locations:
// Original file produced by the frontend:
//  DerivedData/<Project>/Build/Intermediates.noindex/<Project>.build/<BuiltType>-<Target>/<Project>.build/Objects-normal/<Arch>/<ModuleName>.swiftmodule
// where:
//  Project: name of a project, target, or scheme
//  BuildType: Debug, Release, etc.
//  Target: macOS, iphoneos, appletvsimulator, etc.
//  Arch: arm64, x86_64, etc.
//
// So far we observed that Xcode can move the module into different locations, and it's not
// entirely clear how to deduce the destination from the context available for the extractor.
// 1. First case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// 2. Second case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// 2. Third case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.framework/Modules/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.framework/Modules/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// The <Triple> here is a normalized target triple (e.g. arm64-apple-iphoneos15.4 ->
// arm64-apple-iphoneos).
//
// This method constructs those aliases for a module only if it comes from Xcode, which is detected
// by the presence of an `Intermediates.noindex` directory in the module path.
//
// In the case of the Swift Package Manager (`swift build`) this is not needed.
static std::vector<std::string> computeModuleAliases(llvm::StringRef modulePath,
                                                     const std::string& targetTriple) {
  if (modulePath.empty()) {
    return {};
  }
  if (!modulePath.endswith(".swiftmodule")) {
    return {};
  }
  // Deconstructing the Xcode generated path
  //
  // clang-format off
  //                          intermediatesDirIndex               destinationDir (2)                              arch(5)
  // DerivedData/FooBar/Build/Intermediates.noindex/FooBar.build/Debug-iphonesimulator/FooBar.build/Objects-normal/x86_64/FooBar.swiftmodule
  // clang-format on
  llvm::SmallVector<llvm::StringRef> chunks;
  modulePath.split(chunks, '/');
  size_t intermediatesDirIndex = 0;
  for (size_t i = 0; i < chunks.size(); i++) {
    if (chunks[i] == "Intermediates.noindex") {
      intermediatesDirIndex = i;
      break;
    }
  }
  // Not built by Xcode, skipping
  if (intermediatesDirIndex == 0) {
    return {};
  }
  size_t destinationDirIndex = intermediatesDirIndex + 2;
  size_t archIndex = intermediatesDirIndex + 5;
  if (archIndex >= chunks.size()) {
    return {};
  }
  // e.g. Debug-iphoneos, Release-iphonesimulator, etc.
  auto destinationDir = chunks[destinationDirIndex].str();
  auto arch = chunks[archIndex].str();
  auto moduleNameWithExt = chunks.back();
  auto moduleName = moduleNameWithExt.substr(0, moduleNameWithExt.find_last_of('.'));
  std::string relocatedModulePath = chunks[0].str();
  for (size_t i = 1; i < intermediatesDirIndex; i++) {
    relocatedModulePath += '/' + chunks[i].str();
  }
  relocatedModulePath += "/Products/";
  relocatedModulePath += destinationDir + '/';

  std::vector<std::string> moduleLocations;

  std::string firstCase = relocatedModulePath;
  firstCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(firstCase);

  std::string secondCase = relocatedModulePath;
  secondCase += moduleName.str() + '/';
  secondCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(secondCase);

  std::string thirdCase = relocatedModulePath;
  thirdCase += moduleName.str() + '/';
  thirdCase += moduleName.str() + ".framework/Modules/";
  thirdCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(thirdCase);

  std::vector<std::string> aliases;
  for (auto& location : moduleLocations) {
    aliases.push_back(location + arch + ".swiftmodule");
    if (!targetTriple.empty()) {
      llvm::Triple triple(targetTriple);
      auto moduleTriple = swift::getTargetSpecificModuleTriple(triple);
      aliases.push_back(location + moduleTriple.normalize() + ".swiftmodule");
    }
  }

  return aliases;
}

namespace codeql {

std::unordered_map<std::string, std::string> rewriteOutputsInPlace(
    SwiftExtractorConfiguration& config,
    std::vector<std::string>& CLIArgs) {
  std::unordered_map<std::string, std::string> remapping;

  // TODO: handle filelists?
  const std::unordered_set<std::string> pathRewriteOptions({
      "-emit-dependencies-path",
      "-emit-module-path",
      "-emit-module-doc-path",
      "-emit-module-source-info-path",
      "-emit-objc-header-path",
      "-emit-reference-dependencies-path",
      "-index-store-path",
      "-module-cache-path",
      "-o",
      "-pch-output-dir",
      "-serialize-diagnostics-path",
  });

  std::unordered_set<std::string> outputFileMaps(
      {"-supplementary-output-file-map", "-output-file-map"});

  std::vector<size_t> outputFileMapIndexes;
  std::vector<std::string> maybeInput;
  std::string targetTriple;

  std::vector<std::string> newLocations;
  for (size_t i = 0; i < CLIArgs.size(); i++) {
    if (pathRewriteOptions.count(CLIArgs[i])) {
      auto oldPath = CLIArgs[i + 1];
      auto newPath = config.tempArtifactDir + '/' + oldPath;
      CLIArgs[++i] = newPath;
      newLocations.push_back(newPath);

      remapping[oldPath] = newPath;
    } else if (outputFileMaps.count(CLIArgs[i])) {
      // collect output map indexes for further rewriting and skip the following argument
      // We don't patch the map in place as we need to collect all the input files first
      outputFileMapIndexes.push_back(++i);
    } else if (CLIArgs[i] == "-target") {
      targetTriple = CLIArgs[++i];
    } else if (CLIArgs[i][0] != '-') {
      // TODO: add support for input file lists?
      // We need to collect input file names to later use them to extract information from the
      // output file maps.
      maybeInput.push_back(CLIArgs[i]);
    }
  }

  for (auto index : outputFileMapIndexes) {
    auto oldPath = CLIArgs[index];
    auto maybeNewPath = rewriteOutputFileMap(config, oldPath, maybeInput, remapping);
    if (maybeNewPath) {
      auto newPath = maybeNewPath.value();
      CLIArgs[index] = newPath;
      remapping[oldPath] = newPath;
    }
  }

  // This doesn't really belong here, but we've got Xcode...
  for (const auto& [oldPath, newPath] : remapping) {
    llvm::StringRef path(oldPath);
    auto aliases = computeModuleAliases(path, targetTriple);
    for (auto& alias : aliases) {
      remapping[alias] = newPath;
    }
  }

  return remapping;
}

void ensureDirectoriesForNewPathsExist(
    const std::unordered_map<std::string, std::string>& remapping) {
  for (auto& [_, newPath] : remapping) {
    llvm::SmallString<PATH_MAX> filepath(newPath);
    llvm::StringRef parent = llvm::sys::path::parent_path(filepath);
    if (std::error_code ec = llvm::sys::fs::create_directories(parent)) {
      std::cerr << "Cannot create redirected directory: " << ec.message() << "\n";
    }
  }
}

void storeRemappingForVFS(const SwiftExtractorConfiguration& config,
                          const std::unordered_map<std::string, std::string>& remapping) {
  // Only create remapping for the .swiftmodule files
  std::unordered_map<std::string, std::string> modules;
  for (auto& [oldPath, newPath] : remapping) {
    if (llvm::StringRef(oldPath).endswith(".swiftmodule")) {
      modules[oldPath] = newPath;
    }
  }

  if (modules.empty()) {
    return;
  }

  if (std::error_code ec = llvm::sys::fs::create_directories(config.tempVFSDir)) {
    std::cerr << "Cannot create temp VFS directory: " << ec.message() << "\n";
    return;
  }

  if (std::error_code ec = llvm::sys::fs::create_directories(config.VFSDir)) {
    std::cerr << "Cannot create VFS directory: " << ec.message() << "\n";
    return;
  }

  // Constructing the VFS yaml file in a temp folder so that the other process doesn't read it
  // while it is not complete
  // TODO: Pick a more robust way to not collide with files from other processes
  auto tempVfsPath = config.tempVFSDir + '/' + std::to_string(getpid()) + "-vfs.yaml";
  std::error_code ec;
  llvm::raw_fd_ostream fd(tempVfsPath, ec, llvm::sys::fs::OF_None);
  if (ec) {
    std::cerr << "Cannot create temp VFS file: '" << tempVfsPath << "': " << ec.message() << "\n";
    return;
  }
  // TODO: there must be a better API than this
  // LLVM expects the version to be 0
  fd << "{ version: 0,\n";
  // This tells the FS not to fallback to the physical file system in case the remapped file is not
  // present
  fd << "  fallthrough: false,\n";
  fd << "  roots: [\n";
  for (auto& [oldPath, newPath] : modules) {
    fd << "  {\n";
    fd << "    type: 'file',\n";
    fd << "    name: '" << oldPath << "\',\n";
    fd << "    external-contents: '" << newPath << "\'\n";
    fd << "  },\n";
  }
  fd << "  ]\n";
  fd << "}\n";

  fd.flush();
  auto vfsPath = config.VFSDir + '/' + std::to_string(getpid()) + "-vfs.yaml";
  if (std::error_code ec = llvm::sys::fs::rename(tempVfsPath, vfsPath)) {
    std::cerr << "Cannot move temp VFS file '" << tempVfsPath << "' -> '" << vfsPath
              << "': " << ec.message() << "\n";
    return;
  }
}

std::vector<std::string> collectVFSFiles(const SwiftExtractorConfiguration& config) {
  auto vfsDir = config.VFSDir + '/';
  if (!llvm::sys::fs::exists(vfsDir)) {
    return {};
  }
  std::vector<std::string> overlays;
  std::error_code ec;
  llvm::sys::fs::directory_iterator it(vfsDir, ec);
  while (!ec && it != llvm::sys::fs::directory_iterator()) {
    llvm::StringRef path(it->path());
    if (path.endswith("vfs.yaml")) {
      overlays.push_back(path.str());
    }
    it.increment(ec);
  }

  return overlays;
}

}  // namespace codeql
<commit_msg>Apply suggestions from code review<commit_after>#include "SwiftOutputRewrite.h"
#include "swift/extractor/SwiftExtractorConfiguration.h"

#include <llvm/ADT/SmallString.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/Path.h>
#include <swift/Basic/OutputFileMap.h>
#include <swift/Basic/Platform.h>
#include <unistd.h>
#include <unordered_set>
#include <optional>
#include <iostream>

// Creates a copy of the output file map and updates remapping table in place
// It does not change the original map file as it is depended upon by the original compiler
// Returns path to the newly created output file map on success, or None in a case of failure
static std::optional<std::string> rewriteOutputFileMap(
    const codeql::SwiftExtractorConfiguration& config,
    const std::string& outputFileMapPath,
    const std::vector<std::string>& inputs,
    std::unordered_map<std::string, std::string>& remapping) {
  auto newPath = config.tempArtifactDir + '/' + outputFileMapPath;

  // TODO: do not assume absolute path for the second parameter
  auto outputMapOrError = swift::OutputFileMap::loadFromPath(outputFileMapPath, "");
  if (!outputMapOrError) {
    return std::nullopt;
  }
  auto oldOutputMap = outputMapOrError.get();
  swift::OutputFileMap newOutputMap;
  std::vector<llvm::StringRef> keys;
  for (auto& key : inputs) {
    auto oldMap = oldOutputMap.getOutputMapForInput(key);
    if (!oldMap) {
      continue;
    }
    keys.push_back(key);
    auto& newMap = newOutputMap.getOrCreateOutputMapForInput(key);
    newMap.copyFrom(*oldMap);
    for (auto& entry : newMap) {
      auto oldPath = entry.getSecond();
      auto newPath = config.tempArtifactDir + '/' + oldPath;
      entry.getSecond() = newPath;
      remapping[oldPath] = newPath;
    }
  }
  std::error_code ec;
  llvm::SmallString<PATH_MAX> filepath(newPath);
  llvm::StringRef parent = llvm::sys::path::parent_path(filepath);
  if (std::error_code ec = llvm::sys::fs::create_directories(parent)) {
    std::cerr << "Cannot create relocated output map dir: '" << parent.str()
              << "': " << ec.message() << "\n";
    return std::nullopt;
  }

  llvm::raw_fd_ostream fd(newPath, ec, llvm::sys::fs::OF_None);
  newOutputMap.write(fd, keys);
  return newPath;
}

// This is an Xcode-specific workaround to produce alias names for an existing .swiftmodule file.
// In the case of Xcode, it calls the Swift compiler and asks it to produce a Swift module.
// Once it's done, Xcode moves the .swiftmodule file in another location, and the location is
// rather arbitrary. Here are examples of such locations:
// Original file produced by the frontend:
//  DerivedData/<Project>/Build/Intermediates.noindex/<Project>.build/<BuiltType>-<Target>/<Project>.build/Objects-normal/<Arch>/<ModuleName>.swiftmodule
// where:
//  Project: name of a project, target, or scheme
//  BuildType: Debug, Release, etc.
//  Target: macOS, iphoneos, appletvsimulator, etc.
//  Arch: arm64, x86_64, etc.
//
// So far we observed that Xcode can move the module into different locations, and it's not
// entirely clear how to deduce the destination from the context available for the extractor.
// 1. First case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// 2. Second case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// 2. Third case:
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.framework/Modules/<ModuleName>.swiftmodule/<Arch>.swiftmodule
//  DerivedData/<Project>/Build/Products/<BuiltType>-<Target>/<ModuleName>/<ModuleName>.framework/Modules/<ModuleName>.swiftmodule/<Triple>.swiftmodule
// The <Triple> here is a normalized target triple (e.g. arm64-apple-iphoneos15.4 ->
// arm64-apple-iphoneos).
//
// This method constructs those aliases for a module only if it comes from Xcode, which is detected
// by the presence of an `Intermediates.noindex` directory in the module path.
//
// In the case of the Swift Package Manager (`swift build`) this is not needed.
static std::vector<std::string> computeModuleAliases(llvm::StringRef modulePath,
                                                     const std::string& targetTriple) {
  if (modulePath.empty()) {
    return {};
  }
  if (!modulePath.endswith(".swiftmodule")) {
    return {};
  }
  // Deconstructing the Xcode generated path
  //
  // clang-format off
  //                          intermediatesDirIndex               destinationDir (2)                              arch(5)
  // DerivedData/FooBar/Build/Intermediates.noindex/FooBar.build/Debug-iphonesimulator/FooBar.build/Objects-normal/x86_64/FooBar.swiftmodule
  // clang-format on
  llvm::SmallVector<llvm::StringRef> chunks;
  modulePath.split(chunks, '/');
  size_t intermediatesDirIndex = 0;
  for (size_t i = 0; i < chunks.size(); i++) {
    if (chunks[i] == "Intermediates.noindex") {
      intermediatesDirIndex = i;
      break;
    }
  }
  // Not built by Xcode, skipping
  if (intermediatesDirIndex == 0) {
    return {};
  }
  size_t destinationDirIndex = intermediatesDirIndex + 2;
  size_t archIndex = intermediatesDirIndex + 5;
  if (archIndex >= chunks.size()) {
    return {};
  }
  // e.g. Debug-iphoneos, Release-iphonesimulator, etc.
  auto destinationDir = chunks[destinationDirIndex].str();
  auto arch = chunks[archIndex].str();
  auto moduleNameWithExt = chunks.back();
  auto moduleName = moduleNameWithExt.substr(0, moduleNameWithExt.find_last_of('.'));
  std::string relocatedModulePath = chunks[0].str();
  for (size_t i = 1; i < intermediatesDirIndex; i++) {
    relocatedModulePath += '/' + chunks[i].str();
  }
  relocatedModulePath += "/Products/";
  relocatedModulePath += destinationDir + '/';

  std::vector<std::string> moduleLocations;

  std::string firstCase = relocatedModulePath;
  firstCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(firstCase);

  std::string secondCase = relocatedModulePath;
  secondCase += moduleName.str() + '/';
  secondCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(secondCase);

  std::string thirdCase = relocatedModulePath;
  thirdCase += moduleName.str() + '/';
  thirdCase += moduleName.str() + ".framework/Modules/";
  thirdCase += moduleNameWithExt.str() + '/';
  moduleLocations.push_back(thirdCase);

  std::vector<std::string> aliases;
  for (auto& location : moduleLocations) {
    aliases.push_back(location + arch + ".swiftmodule");
    if (!targetTriple.empty()) {
      llvm::Triple triple(targetTriple);
      auto moduleTriple = swift::getTargetSpecificModuleTriple(triple);
      aliases.push_back(location + moduleTriple.normalize() + ".swiftmodule");
    }
  }

  return aliases;
}

namespace codeql {

std::unordered_map<std::string, std::string> rewriteOutputsInPlace(
    SwiftExtractorConfiguration& config,
    std::vector<std::string>& CLIArgs) {
  std::unordered_map<std::string, std::string> remapping;

  // TODO: handle filelists?
  const std::unordered_set<std::string> pathRewriteOptions({
      "-emit-dependencies-path",
      "-emit-module-path",
      "-emit-module-doc-path",
      "-emit-module-source-info-path",
      "-emit-objc-header-path",
      "-emit-reference-dependencies-path",
      "-index-store-path",
      "-module-cache-path",
      "-o",
      "-pch-output-dir",
      "-serialize-diagnostics-path",
  });

  std::unordered_set<std::string> outputFileMaps(
      {"-supplementary-output-file-map", "-output-file-map"});

  std::vector<size_t> outputFileMapIndexes;
  std::vector<std::string> maybeInput;
  std::string targetTriple;

  std::vector<std::string> newLocations;
  for (size_t i = 0; i < CLIArgs.size(); i++) {
    if (pathRewriteOptions.count(CLIArgs[i])) {
      auto oldPath = CLIArgs[i + 1];
      auto newPath = config.tempArtifactDir + '/' + oldPath;
      CLIArgs[++i] = newPath;
      newLocations.push_back(newPath);

      remapping[oldPath] = newPath;
    } else if (outputFileMaps.count(CLIArgs[i])) {
      // collect output map indexes for further rewriting and skip the following argument
      // We don't patch the map in place as we need to collect all the input files first
      outputFileMapIndexes.push_back(++i);
    } else if (CLIArgs[i] == "-target") {
      targetTriple = CLIArgs[++i];
    } else if (CLIArgs[i][0] != '-') {
      // TODO: add support for input file lists?
      // We need to collect input file names to later use them to extract information from the
      // output file maps.
      maybeInput.push_back(CLIArgs[i]);
    }
  }

  for (auto index : outputFileMapIndexes) {
    auto oldPath = CLIArgs[index];
    auto maybeNewPath = rewriteOutputFileMap(config, oldPath, maybeInput, remapping);
    if (maybeNewPath) {
      auto newPath = maybeNewPath.value();
      CLIArgs[index] = newPath;
      remapping[oldPath] = newPath;
    }
  }

  // This doesn't really belong here, but we've got Xcode...
  for (const auto& [oldPath, newPath] : remapping) {
    llvm::StringRef path(oldPath);
    auto aliases = computeModuleAliases(path, targetTriple);
    for (auto& alias : aliases) {
      remapping[alias] = newPath;
    }
  }

  return remapping;
}

void ensureDirectoriesForNewPathsExist(
    const std::unordered_map<std::string, std::string>& remapping) {
  for (auto& [_, newPath] : remapping) {
    llvm::SmallString<PATH_MAX> filepath(newPath);
    llvm::StringRef parent = llvm::sys::path::parent_path(filepath);
    if (std::error_code ec = llvm::sys::fs::create_directories(parent)) {
      std::cerr << "Cannot create redirected directory: " << ec.message() << "\n";
    }
  }
}

void storeRemappingForVFS(const SwiftExtractorConfiguration& config,
                          const std::unordered_map<std::string, std::string>& remapping) {
  // Only create remapping for the .swiftmodule files
  std::unordered_map<std::string, std::string> modules;
  for (const auto& [oldPath, newPath] : remapping) {
    if (llvm::StringRef(oldPath).endswith(".swiftmodule")) {
      modules.emplace(oldPath, newPath);
    }
  }

  if (modules.empty()) {
    return;
  }

  if (std::error_code ec = llvm::sys::fs::create_directories(config.tempVFSDir)) {
    std::cerr << "Cannot create temp VFS directory: " << ec.message() << "\n";
    return;
  }

  if (std::error_code ec = llvm::sys::fs::create_directories(config.VFSDir)) {
    std::cerr << "Cannot create VFS directory: " << ec.message() << "\n";
    return;
  }

  // Constructing the VFS yaml file in a temp folder so that the other process doesn't read it
  // while it is not complete
  // TODO: Pick a more robust way to not collide with files from other processes
  auto tempVfsPath = config.tempVFSDir + '/' + std::to_string(getpid()) + "-vfs.yaml";
  std::error_code ec;
  llvm::raw_fd_ostream fd(tempVfsPath, ec, llvm::sys::fs::OF_None);
  if (ec) {
    std::cerr << "Cannot create temp VFS file: '" << tempVfsPath << "': " << ec.message() << "\n";
    return;
  }
  // TODO: there must be a better API than this
  // LLVM expects the version to be 0
  fd << "{ version: 0,\n";
  // This tells the FS not to fallback to the physical file system in case the remapped file is not
  // present
  fd << "  fallthrough: false,\n";
  fd << "  roots: [\n";
  for (auto& [oldPath, newPath] : modules) {
    fd << "  {\n";
    fd << "    type: 'file',\n";
    fd << "    name: '" << oldPath << "\',\n";
    fd << "    external-contents: '" << newPath << "\'\n";
    fd << "  },\n";
  }
  fd << "  ]\n";
  fd << "}\n";

  fd.flush();
  auto vfsPath = config.VFSDir + '/' + std::to_string(getpid()) + "-vfs.yaml";
  if (std::error_code ec = llvm::sys::fs::rename(tempVfsPath, vfsPath)) {
    std::cerr << "Cannot move temp VFS file '" << tempVfsPath << "' -> '" << vfsPath
              << "': " << ec.message() << "\n";
    return;
  }
}

std::vector<std::string> collectVFSFiles(const SwiftExtractorConfiguration& config) {
  auto vfsDir = config.VFSDir + '/';
  if (!llvm::sys::fs::exists(vfsDir)) {
    return {};
  }
  std::vector<std::string> overlays;
  std::error_code ec;
  llvm::sys::fs::directory_iterator it(vfsDir, ec);
  while (!ec && it != llvm::sys::fs::directory_iterator()) {
    llvm::StringRef path(it->path());
    if (path.endswith("vfs.yaml")) {
      overlays.push_back(path.str());
    }
    it.increment(ec);
  }

  return overlays;
}

}  // namespace codeql
<|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

#include "../general/okina.hpp"

#include <bitset>
#include <cassert>

namespace mfem
{

// *****************************************************************************
// * Tests if ptr is a known address
// *****************************************************************************
static bool Known(const mm::ledger &maps, const void *ptr)
{
   const mm::memory_map::const_iterator found = maps.memories.find(ptr);
   const bool known = found != maps.memories.end();
   if (known) { return true; }
   return false;
}

// *****************************************************************************
bool mm::Known(const void *ptr)
{
   return mfem::Known(maps,ptr);
}

// *****************************************************************************
// * Looks if ptr is an alias of one memory
// *****************************************************************************
static const void* IsAlias(const mm::ledger &maps, const void *ptr)
{
   MFEM_ASSERT(!Known(maps, ptr), "Ptr is an already known address!");
   for (mm::memory_map::const_iterator mem = maps.memories.begin();
        mem != maps.memories.end(); mem++)
   {
      const void *b_ptr = mem->first;
      if (b_ptr > ptr) { continue; }
      const void *end = static_cast<const char*>(b_ptr) + mem->second.bytes;
      if (ptr < end) { return b_ptr; }
   }
   return nullptr;
}

// *****************************************************************************
static const void* InsertAlias(mm::ledger &maps,
                               const void *base,
                               const void *ptr)
{
   mm::memory &mem = maps.memories.at(base);
   const long offset = static_cast<const char*>(ptr) - static_cast<const char*>
                       (base);
   const mm::alias *alias = new mm::alias{&mem, offset};
   maps.aliases.emplace(ptr, alias);
#ifdef MFEM_DEBUG_MM
   {
      mem.aliases.sort();
      for (const mm::alias *a : mem.aliases)
      {
         if (a->mem == &mem )
         {
            assert(a->offset != offset);
         }
      }
   }
#endif // MFEM_DEBUG
   mem.aliases.push_back(alias);
   return ptr;
}

// *****************************************************************************
// * Tests if ptr is an alias address
// *****************************************************************************
static bool Alias(mm::ledger &maps, const void *ptr)
{
   const mm::alias_map::const_iterator found = maps.aliases.find(ptr);
   const bool alias = found != maps.aliases.end();
   if (alias) { return true; }
   const void *base = IsAlias(maps, ptr);
   if (!base) { return false; }
   InsertAlias(maps, base, ptr);
   return true;
}

// *****************************************************************************
bool mm::Alias(const void *ptr)
{
   return mfem::Alias(maps,ptr);
}

// *****************************************************************************
static void DumpMode(void)
{
   static bool env_ini = false;
   static bool env_dbg = false;
   if (!env_ini) { env_dbg = getenv("DBG"); env_ini = true; }
   if (!env_dbg) { return; }
   static std::bitset<8+1> mode;
   std::bitset<8+1> cfg;
   cfg.set(config::UsingMM()?8:0);
   cfg.set(config::DeviceHasBeenEnabled()?7:0);
   cfg.set(config::DeviceEnabled()?6:0);
   cfg.set(config::DeviceDisabled()?5:0);
   cfg.set(config::UsingHost()?4:0);
   cfg.set(config::UsingDevice()?3:0);
   cfg.set(config::UsingCuda()?2:0);
   cfg.set(config::UsingOcca()?1:0);
   cfg>>=1;
   if (cfg==mode) { return; }
   mode=cfg;
   printf("\033[1K\r[0x%lx] %sMM %sHasBeenEnabled %sEnabled %sDisabled "
          "%sHOST %sDEVICE %sCUDA %sOCCA\033[m", mode.to_ulong(),
          config::UsingMM()?"\033[32m":"\033[31m",
          config::DeviceHasBeenEnabled()?"\033[32m":"\033[31m",
          config::DeviceEnabled()?"\033[32m":"\033[31m",
          config::DeviceDisabled()?"\033[32m":"\033[31m",
          config::UsingHost()?"\033[32m":"\033[31m",
          config::UsingDevice()?"\033[32m":"\033[31m",
          config::UsingCuda()?"\033[32m":"\033[31m",
          config::UsingOcca()?"\033[32m":"\033[31m");
}

// *****************************************************************************
static inline bool MmFilter(void)
{
   if (!config::UsingMM()) { return true; }
   return false;
}

// *****************************************************************************
static inline bool MmDeviceIniFilter(void)
{
   if (MmFilter()) { return true; }
   if (config::DeviceDisabled()) { return true; }
   if (!config::DeviceHasBeenEnabled()) { return true; }
   assert(!config::UsingOcca());
   return false;
}

// *****************************************************************************
// * Adds an address
// *****************************************************************************
void* mm::Insert(void *ptr, const size_t bytes)
{
   if (MmFilter()) { return ptr; }
   const bool known = Known(ptr);
   if (known)
   {
      MFEM_ASSERT(!known, "Trying to add an already present address!");
      mfem_error("Trying to add an already present address!");
   }
   DumpMode();
   maps.memories.emplace(ptr, memory(ptr, bytes));
   return ptr;
}

// *****************************************************************************
// * Remove the address from the map, as well as all the address' aliases
// *****************************************************************************
void *mm::Erase(void *ptr)
{
   if (MmFilter()) { return ptr; }
   const bool known = Known(ptr);
   if (!known)
   {
      mfem_error("Trying to erase an unknown pointer!");
   }
   MFEM_ASSERT(known, "Trying to erase an unknown pointer!");
   memory &mem = maps.memories.at(ptr);
   for (const alias* const alias : mem.aliases)
   {
      maps.aliases.erase(alias);
   }
   mem.aliases.clear();
   maps.memories.erase(ptr);
   return ptr;
}

// *****************************************************************************
// * Turn a known address to the right host or device one
// * Alloc, Push or Pull it if required
// *****************************************************************************
static void *PtrKnown(mm::ledger &maps, void *ptr)
{
   mm::memory &base = maps.memories.at(ptr);
   const bool host = base.host;
   const bool device = !host;
   const size_t bytes = base.bytes;
   const bool gpu = config::UsingDevice();
   if (host && !gpu) { return ptr; }
   if (!base.d_ptr) { cuMemAlloc(&base.d_ptr, bytes); }
   assert(base.d_ptr);
   if (device &&  gpu) { return base.d_ptr; }
   if (device && !gpu) // Pull
   {
      cuMemcpyDtoH(ptr, base.d_ptr, bytes);
      base.host = true;
      return ptr;
   }
   // Push
   assert(host && gpu);
   cuMemcpyHtoD(base.d_ptr, ptr, bytes);
   base.host = false;
   return base.d_ptr;
}

// *****************************************************************************
// * Turn an alias to the right host or device one
// * Alloc, Push or Pull it if required
// *****************************************************************************
static void *PtrAlias(mm::ledger &maps, void *ptr)
{
   const bool gpu = config::UsingDevice();
   const mm::alias *alias = maps.aliases.at(ptr);
   const mm::memory *base = alias->mem;
   const bool host = base->host;
   const bool device = !base->host;
   const size_t bytes = base->bytes;
   if (host && !gpu) { return ptr; }
   if (!base->d_ptr) { cuMemAlloc(&(alias->mem->d_ptr), bytes); }
   assert(base->d_ptr);
   void *a_ptr = static_cast<char*>(base->d_ptr) + alias->offset;
   if (device && gpu) { return a_ptr; }
   if (device && !gpu) // Pull
   {
      cuMemcpyDtoH(base->h_ptr, base->d_ptr, bytes);
      alias->mem->host = true;
      return ptr;
   }
   // Push
   assert(host && gpu);
   cuMemcpyHtoD(base->d_ptr, base->h_ptr, bytes);
   alias->mem->host = false;
   return a_ptr;
}

// *****************************************************************************
// * Turn an address to the right host or device one
// * If the pointer is NULL the companion pointer
// * will be too.
// *****************************************************************************
void *mm::Ptr(void *ptr)
{
   if (MmDeviceIniFilter()) { return ptr; }
   if (Known(ptr)) { return PtrKnown(maps, ptr); }
   if (Alias(ptr)) { return PtrAlias(maps, ptr); }
   if (config::UsingDevice() && ptr==NULL)
   {
     return NULL;
   }
   else
   {
     mfem_error("Trying to use unknown pointer on the DEVICE!");
   }
   return ptr;
}

// *****************************************************************************
const void *mm::Ptr(const void *ptr)
{
   return static_cast<const void*>(Ptr(const_cast<void*>(ptr)));
}

// *****************************************************************************
static OccaMemory occaMemory(mm::ledger &maps, const void *ptr)
{
   OccaDevice occaDevice = config::GetOccaDevice();
   if (!config::UsingMM())
   {
      OccaMemory o_ptr = occaWrapMemory(occaDevice, const_cast<void*>(ptr), 0);
      return o_ptr;
   }
   const bool known = mm::known(ptr);
   if (!known) { mfem_error("occaMemory"); }
   MFEM_ASSERT(known, "Unknown address!");
   mm::memory &base = maps.memories.at(ptr);
   const size_t bytes = base.bytes;
   const bool gpu = config::UsingDevice();
   MFEM_ASSERT(config::UsingOcca(), "Using OCCA memory without OCCA mode!");
   if (!base.d_ptr)
   {
      base.host = false; // This address is no more on the host
      if (gpu)
      {
         cuMemAlloc(&base.d_ptr, bytes);
         void *stream = config::Stream();
         cuMemcpyHtoDAsync(base.d_ptr, base.h_ptr, bytes, stream);
      }
      else
      {
         base.o_ptr = occaDeviceMalloc(occaDevice, bytes);
         base.d_ptr = occaMemoryPtr(base.o_ptr);
         occaCopyFrom(base.o_ptr, base.h_ptr);
      }
   }
   if (gpu)
   {
      return occaWrapMemory(occaDevice, base.d_ptr, bytes);
   }
   return base.o_ptr;
}

// *****************************************************************************
OccaMemory mm::Memory(const void *ptr) { return occaMemory(maps, ptr); }

// *****************************************************************************
static void PushKnown(mm::ledger &maps, const void *ptr, const size_t bytes)
{
   mm::memory &base = maps.memories.at(ptr);
   if (!base.d_ptr) { cuMemAlloc(&base.d_ptr, base.bytes); }
   cuMemcpyHtoD(base.d_ptr, ptr, bytes == 0 ? base.bytes : bytes);
}

// *****************************************************************************
static void PushAlias(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::alias *alias = maps.aliases.at(ptr);
   cuMemcpyHtoD(static_cast<char*>(alias->mem->d_ptr) + alias->offset, ptr, bytes);
}

// *****************************************************************************
void mm::Push(const void *ptr, const size_t bytes)
{
   if (MmDeviceIniFilter()) { return; }
   if (Known(ptr)) { return PushKnown(maps, ptr, bytes); }
   if (Alias(ptr)) { return PushAlias(maps, ptr, bytes); }
   if (config::UsingDevice())
   {
      mfem_error("Unknown pointer to push to!");
   }
}

// *****************************************************************************
static void PullKnown(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::memory &base = maps.memories.at(ptr);
   const bool host = base.host;
   if (host) { return; }
   cuMemcpyDtoH(base.h_ptr, base.d_ptr, bytes == 0 ? base.bytes : bytes);
}

// *****************************************************************************
static void PullAlias(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::alias *alias = maps.aliases.at(ptr);
   const bool host = alias->mem->host;
   if (host) { return; }
   cuMemcpyDtoH(const_cast<void*>(ptr),
                static_cast<char*>(alias->mem->d_ptr) + alias->offset, bytes);
}

// *****************************************************************************
void mm::Pull(const void *ptr, const size_t bytes)
{
   if (MmDeviceIniFilter()) { return; }
   if (Known(ptr)) { return PullKnown(maps, ptr, bytes); }
   if (Alias(ptr)) { return PullAlias(maps, ptr, bytes); }
   if (config::UsingDevice())
   {
      mfem_error("Unknown pointer to pull from!");
   }
}

// *****************************************************************************
// * Data will be pushed/pulled before the copy happens on the H or the D
// *****************************************************************************
static void* d2d(void *dst, const void *src, const size_t bytes,
                 const bool async)
{
   const bool host = config::UsingHost();
   if (host) { return std::memcpy(dst, src, bytes); }
   const void *d_src = mm::ptr(src);
   void *d_dst = mm::ptr(dst);
   if (!async) { return cuMemcpyDtoD(d_dst, const_cast<void*>(d_src), bytes); }
   return cuMemcpyDtoDAsync(d_dst, const_cast<void*>(d_src),
                            bytes, config::Stream());
}

// *****************************************************************************
void* mm::memcpy(void *dst, const void *src, const size_t bytes,
                 const bool async)
{
   if (bytes == 0)
   {
      return dst;
   }
   else
   {
      return d2d(dst, src, bytes, async);
   }
}

void RegisterCheck(void *ptr)
{
  if(ptr != NULL){
    if(!mm::known(ptr))
      {
        assert(0 && "Pointer is not registerd!");
        mfem_error("Trying use an unregistered pointer!");
      }
  }
}

} // namespace mfem
<commit_msg>Return null pointer if null pointer is given<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License (as published by the Free
// Software Foundation) version 2.1 dated February 1999.

#include "../general/okina.hpp"

#include <bitset>
#include <cassert>

namespace mfem
{

// *****************************************************************************
// * Tests if ptr is a known address
// *****************************************************************************
static bool Known(const mm::ledger &maps, const void *ptr)
{
   const mm::memory_map::const_iterator found = maps.memories.find(ptr);
   const bool known = found != maps.memories.end();
   if (known) { return true; }
   return false;
}

// *****************************************************************************
bool mm::Known(const void *ptr)
{
   return mfem::Known(maps,ptr);
}

// *****************************************************************************
// * Looks if ptr is an alias of one memory
// *****************************************************************************
static const void* IsAlias(const mm::ledger &maps, const void *ptr)
{
   MFEM_ASSERT(!Known(maps, ptr), "Ptr is an already known address!");
   for (mm::memory_map::const_iterator mem = maps.memories.begin();
        mem != maps.memories.end(); mem++)
   {
      const void *b_ptr = mem->first;
      if (b_ptr > ptr) { continue; }
      const void *end = static_cast<const char*>(b_ptr) + mem->second.bytes;
      if (ptr < end) { return b_ptr; }
   }
   return nullptr;
}

// *****************************************************************************
static const void* InsertAlias(mm::ledger &maps,
                               const void *base,
                               const void *ptr)
{
   mm::memory &mem = maps.memories.at(base);
   const long offset = static_cast<const char*>(ptr) - static_cast<const char*>
                       (base);
   const mm::alias *alias = new mm::alias{&mem, offset};
   maps.aliases.emplace(ptr, alias);
#ifdef MFEM_DEBUG_MM
   {
      mem.aliases.sort();
      for (const mm::alias *a : mem.aliases)
      {
         if (a->mem == &mem )
         {
            assert(a->offset != offset);
         }
      }
   }
#endif // MFEM_DEBUG
   mem.aliases.push_back(alias);
   return ptr;
}

// *****************************************************************************
// * Tests if ptr is an alias address
// *****************************************************************************
static bool Alias(mm::ledger &maps, const void *ptr)
{
   const mm::alias_map::const_iterator found = maps.aliases.find(ptr);
   const bool alias = found != maps.aliases.end();
   if (alias) { return true; }
   const void *base = IsAlias(maps, ptr);
   if (!base) { return false; }
   InsertAlias(maps, base, ptr);
   return true;
}

// *****************************************************************************
bool mm::Alias(const void *ptr)
{
   return mfem::Alias(maps,ptr);
}

// *****************************************************************************
static void DumpMode(void)
{
   static bool env_ini = false;
   static bool env_dbg = false;
   if (!env_ini) { env_dbg = getenv("DBG"); env_ini = true; }
   if (!env_dbg) { return; }
   static std::bitset<8+1> mode;
   std::bitset<8+1> cfg;
   cfg.set(config::UsingMM()?8:0);
   cfg.set(config::DeviceHasBeenEnabled()?7:0);
   cfg.set(config::DeviceEnabled()?6:0);
   cfg.set(config::DeviceDisabled()?5:0);
   cfg.set(config::UsingHost()?4:0);
   cfg.set(config::UsingDevice()?3:0);
   cfg.set(config::UsingCuda()?2:0);
   cfg.set(config::UsingOcca()?1:0);
   cfg>>=1;
   if (cfg==mode) { return; }
   mode=cfg;
   printf("\033[1K\r[0x%lx] %sMM %sHasBeenEnabled %sEnabled %sDisabled "
          "%sHOST %sDEVICE %sCUDA %sOCCA\033[m", mode.to_ulong(),
          config::UsingMM()?"\033[32m":"\033[31m",
          config::DeviceHasBeenEnabled()?"\033[32m":"\033[31m",
          config::DeviceEnabled()?"\033[32m":"\033[31m",
          config::DeviceDisabled()?"\033[32m":"\033[31m",
          config::UsingHost()?"\033[32m":"\033[31m",
          config::UsingDevice()?"\033[32m":"\033[31m",
          config::UsingCuda()?"\033[32m":"\033[31m",
          config::UsingOcca()?"\033[32m":"\033[31m");
}

// *****************************************************************************
static inline bool MmFilter(void)
{
   if (!config::UsingMM()) { return true; }
   return false;
}

// *****************************************************************************
static inline bool MmDeviceIniFilter(void)
{
   if (MmFilter()) { return true; }
   if (config::DeviceDisabled()) { return true; }
   if (!config::DeviceHasBeenEnabled()) { return true; }
   assert(!config::UsingOcca());
   return false;
}

// *****************************************************************************
// * Adds an address
// *****************************************************************************
void* mm::Insert(void *ptr, const size_t bytes)
{
   if (MmFilter()) { return ptr; }
   const bool known = Known(ptr);
   if (known)
   {
      MFEM_ASSERT(!known, "Trying to add an already present address!");
      mfem_error("Trying to add an already present address!");
   }
   DumpMode();
   maps.memories.emplace(ptr, memory(ptr, bytes));
   return ptr;
}

// *****************************************************************************
// * Remove the address from the map, as well as all the address' aliases
// *****************************************************************************
void *mm::Erase(void *ptr)
{
   if (MmFilter()) { return ptr; }
   const bool known = Known(ptr);
   if (!known)
   {
      mfem_error("Trying to erase an unknown pointer!");
   }
   MFEM_ASSERT(known, "Trying to erase an unknown pointer!");
   memory &mem = maps.memories.at(ptr);
   for (const alias* const alias : mem.aliases)
   {
      maps.aliases.erase(alias);
   }
   mem.aliases.clear();
   maps.memories.erase(ptr);
   return ptr;
}

// *****************************************************************************
// * Turn a known address to the right host or device one
// * Alloc, Push or Pull it if required
// *****************************************************************************
static void *PtrKnown(mm::ledger &maps, void *ptr)
{
   mm::memory &base = maps.memories.at(ptr);
   const bool host = base.host;
   const bool device = !host;
   const size_t bytes = base.bytes;
   const bool gpu = config::UsingDevice();
   if (host && !gpu) { return ptr; }
   if (!base.d_ptr) { cuMemAlloc(&base.d_ptr, bytes); }
   assert(base.d_ptr);
   if (device &&  gpu) { return base.d_ptr; }
   if (device && !gpu) // Pull
   {
      cuMemcpyDtoH(ptr, base.d_ptr, bytes);
      base.host = true;
      return ptr;
   }
   // Push
   assert(host && gpu);
   cuMemcpyHtoD(base.d_ptr, ptr, bytes);
   base.host = false;
   return base.d_ptr;
}

// *****************************************************************************
// * Turn an alias to the right host or device one
// * Alloc, Push or Pull it if required
// *****************************************************************************
static void *PtrAlias(mm::ledger &maps, void *ptr)
{
   const bool gpu = config::UsingDevice();
   const mm::alias *alias = maps.aliases.at(ptr);
   const mm::memory *base = alias->mem;
   const bool host = base->host;
   const bool device = !base->host;
   const size_t bytes = base->bytes;
   if (host && !gpu) { return ptr; }
   if (!base->d_ptr) { cuMemAlloc(&(alias->mem->d_ptr), bytes); }
   assert(base->d_ptr);
   void *a_ptr = static_cast<char*>(base->d_ptr) + alias->offset;
   if (device && gpu) { return a_ptr; }
   if (device && !gpu) // Pull
   {
      cuMemcpyDtoH(base->h_ptr, base->d_ptr, bytes);
      alias->mem->host = true;
      return ptr;
   }
   // Push
   assert(host && gpu);
   cuMemcpyHtoD(base->d_ptr, base->h_ptr, bytes);
   alias->mem->host = false;
   return a_ptr;
}

// *****************************************************************************
// * Turn an address to the right host or device one
// * If the pointer is NULL the companion pointer
// * will be too.
// *****************************************************************************
void *mm::Ptr(void *ptr)
{
   if (MmDeviceIniFilter()) { return ptr; }
   if (Known(ptr)) { return PtrKnown(maps, ptr); }
   if (Alias(ptr)) { return PtrAlias(maps, ptr); }
   if (ptr==NULL)
   {
     return NULL;
   }
   else
   {
     mfem_error("Trying to use unknown pointer on the DEVICE!");
   }
   return ptr;
}

// *****************************************************************************
const void *mm::Ptr(const void *ptr)
{
   return static_cast<const void*>(Ptr(const_cast<void*>(ptr)));
}

// *****************************************************************************
static OccaMemory occaMemory(mm::ledger &maps, const void *ptr)
{
   OccaDevice occaDevice = config::GetOccaDevice();
   if (!config::UsingMM())
   {
      OccaMemory o_ptr = occaWrapMemory(occaDevice, const_cast<void*>(ptr), 0);
      return o_ptr;
   }
   const bool known = mm::known(ptr);
   if (!known) { mfem_error("occaMemory"); }
   MFEM_ASSERT(known, "Unknown address!");
   mm::memory &base = maps.memories.at(ptr);
   const size_t bytes = base.bytes;
   const bool gpu = config::UsingDevice();
   MFEM_ASSERT(config::UsingOcca(), "Using OCCA memory without OCCA mode!");
   if (!base.d_ptr)
   {
      base.host = false; // This address is no more on the host
      if (gpu)
      {
         cuMemAlloc(&base.d_ptr, bytes);
         void *stream = config::Stream();
         cuMemcpyHtoDAsync(base.d_ptr, base.h_ptr, bytes, stream);
      }
      else
      {
         base.o_ptr = occaDeviceMalloc(occaDevice, bytes);
         base.d_ptr = occaMemoryPtr(base.o_ptr);
         occaCopyFrom(base.o_ptr, base.h_ptr);
      }
   }
   if (gpu)
   {
      return occaWrapMemory(occaDevice, base.d_ptr, bytes);
   }
   return base.o_ptr;
}

// *****************************************************************************
OccaMemory mm::Memory(const void *ptr) { return occaMemory(maps, ptr); }

// *****************************************************************************
static void PushKnown(mm::ledger &maps, const void *ptr, const size_t bytes)
{
   mm::memory &base = maps.memories.at(ptr);
   if (!base.d_ptr) { cuMemAlloc(&base.d_ptr, base.bytes); }
   cuMemcpyHtoD(base.d_ptr, ptr, bytes == 0 ? base.bytes : bytes);
}

// *****************************************************************************
static void PushAlias(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::alias *alias = maps.aliases.at(ptr);
   cuMemcpyHtoD(static_cast<char*>(alias->mem->d_ptr) + alias->offset, ptr, bytes);
}

// *****************************************************************************
void mm::Push(const void *ptr, const size_t bytes)
{
   if (MmDeviceIniFilter()) { return; }
   if (Known(ptr)) { return PushKnown(maps, ptr, bytes); }
   if (Alias(ptr)) { return PushAlias(maps, ptr, bytes); }
   if (config::UsingDevice())
   {
      mfem_error("Unknown pointer to push to!");
   }
}

// *****************************************************************************
static void PullKnown(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::memory &base = maps.memories.at(ptr);
   const bool host = base.host;
   if (host) { return; }
   cuMemcpyDtoH(base.h_ptr, base.d_ptr, bytes == 0 ? base.bytes : bytes);
}

// *****************************************************************************
static void PullAlias(const mm::ledger &maps, const void *ptr,
                      const size_t bytes)
{
   const mm::alias *alias = maps.aliases.at(ptr);
   const bool host = alias->mem->host;
   if (host) { return; }
   cuMemcpyDtoH(const_cast<void*>(ptr),
                static_cast<char*>(alias->mem->d_ptr) + alias->offset, bytes);
}

// *****************************************************************************
void mm::Pull(const void *ptr, const size_t bytes)
{
   if (MmDeviceIniFilter()) { return; }
   if (Known(ptr)) { return PullKnown(maps, ptr, bytes); }
   if (Alias(ptr)) { return PullAlias(maps, ptr, bytes); }
   if (config::UsingDevice())
   {
      mfem_error("Unknown pointer to pull from!");
   }
}

// *****************************************************************************
// * Data will be pushed/pulled before the copy happens on the H or the D
// *****************************************************************************
static void* d2d(void *dst, const void *src, const size_t bytes,
                 const bool async)
{
   const bool host = config::UsingHost();
   if (host) { return std::memcpy(dst, src, bytes); }
   const void *d_src = mm::ptr(src);
   void *d_dst = mm::ptr(dst);
   if (!async) { return cuMemcpyDtoD(d_dst, const_cast<void*>(d_src), bytes); }
   return cuMemcpyDtoDAsync(d_dst, const_cast<void*>(d_src),
                            bytes, config::Stream());
}

// *****************************************************************************
void* mm::memcpy(void *dst, const void *src, const size_t bytes,
                 const bool async)
{
   if (bytes == 0)
   {
      return dst;
   }
   else
   {
      return d2d(dst, src, bytes, async);
   }
}

void RegisterCheck(void *ptr)
{
  if(ptr != NULL){
    if(!mm::known(ptr))
      {
        assert(0 && "Pointer is not registerd!");
        mfem_error("Trying use an unregistered pointer!");
      }
  }
}

} // namespace mfem
<|endoftext|>


<commit_before>//
//  OtherAutomatonTest.cpp
//  jdemon
//
//  Created by Mark Larus on 3/22/13.
//  Copyright (c) 2013 Kenyon College. All rights reserved.
//

#include <vector>
#include <set>
#include <map>
#include <iterator>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include "OtherAutomaton.h"

struct OATestFixture {
    OtherAutomaton::Grid grid;
    OATestFixture() : grid(6) {}
};

typedef std::set<OtherAutomaton::Cell *> CellSet;

static inline CellSet testOverlap(OtherAutomaton::Block left, OtherAutomaton::Block right) {
    using namespace OtherAutomaton;
    CellSet cellSet;
    BOOST_FOREACH(Cell *leftCell, left) {
        BOOST_FOREACH(Cell *rightCell, right) {
            if (leftCell==rightCell) {
                cellSet.insert(leftCell);
            }
        }
    }
    return cellSet;
}

BOOST_FIXTURE_TEST_SUITE(OtherAutomatonTests, OATestFixture)

BOOST_AUTO_TEST_CASE( testBlockOverlap ) {
    // Each even block should be disjoint from each other even block. Same is true for odds.

    typedef OtherAutomaton::BlockList::const_iterator iterator;
    typedef std::pair<const OtherAutomaton::Block *, CellSet> pair;

    for (iterator odd=grid.oddBlocks.begin(); odd!=grid.oddBlocks.end(); ++odd) {
        std::map<const OtherAutomaton::Block *,CellSet> overlap;
        for (iterator even=grid.evenBlocks.begin(); even!=grid.evenBlocks.end(); ++even) {
            CellSet sharedCells = testOverlap(*even, *odd);
            if (sharedCells.size()>0) {
                overlap.insert(std::make_pair(&*even, sharedCells));
            }
        }
        if (overlap.size()) {
            BOOST_CHECK_EQUAL(overlap.size(), 2);
            BOOST_FOREACH(pair leftItem, overlap) {
                BOOST_CHECK_EQUAL(leftItem.second.size(),2);
            }
        }
    }

    typedef OtherAutomaton::BlockList::const_iterator iterator;
    for (iterator even=grid.evenBlocks.begin(); even!=grid.evenBlocks.end(); ++even) {
        std::map<const OtherAutomaton::Block *,CellSet> overlap;
        for (iterator odd=grid.oddBlocks.begin(); odd!=grid.oddBlocks.end(); ++odd) {
            CellSet sharedCells = testOverlap(*odd, *even);
            if (sharedCells.size()>0) {
                overlap.insert(std::make_pair(&*odd, sharedCells));
            }
        }
        if (overlap.size()) {
            BOOST_CHECK_EQUAL(overlap.size(), 2);
            BOOST_FOREACH(pair leftItem, overlap) {
                BOOST_CHECK_EQUAL(leftItem.second.size(),2);
            }
        }
    }
}

BOOST_AUTO_TEST_CASE( testDisjoint ) {
    int leftCount = 0;
    int rightCount = 0;
    BOOST_FOREACH(auto left, grid.oddBlocks) {

        BOOST_FOREACH(auto right, grid.oddBlocks) {
            if (left != right) {
                CellSet shouldBeEmpty = testOverlap(left,right);
                BOOST_CHECK(shouldBeEmpty.empty());
            }
            ++rightCount;
        }
        ++leftCount;
    }
}

BOOST_AUTO_TEST_CASE( testNotEmpty ) {
    BOOST_CHECK_GT(grid.size(),0);
    BOOST_CHECK_GT(grid.evenBlocks.size(), 0);
    BOOST_CHECK_GT(grid.oddBlocks.size(), 0);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(OtherAutomatonCell)

BOOST_AUTO_TEST_CASE( defaultValue ) {
    OtherAutomaton::Cell cell;
    BOOST_REQUIRE(!cell.isOccupied());
}

BOOST_AUTO_TEST_SUITE_END()<commit_msg>Cleanup a couple more uses of<commit_after>//
//  OtherAutomatonTest.cpp
//  jdemon
//
//  Created by Mark Larus on 3/22/13.
//  Copyright (c) 2013 Kenyon College. All rights reserved.
//

#include <vector>
#include <set>
#include <map>
#include <iterator>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
#include "OtherAutomaton.h"

struct OATestFixture {
    OtherAutomaton::Grid grid;
    OATestFixture() : grid(6) {}
};

typedef std::set<OtherAutomaton::Cell *> CellSet;

static inline CellSet testOverlap(OtherAutomaton::Block left, OtherAutomaton::Block right) {
    using namespace OtherAutomaton;
    CellSet cellSet;
    BOOST_FOREACH(Cell *leftCell, left) {
        BOOST_FOREACH(Cell *rightCell, right) {
            if (leftCell==rightCell) {
                cellSet.insert(leftCell);
            }
        }
    }
    return cellSet;
}

BOOST_FIXTURE_TEST_SUITE(OtherAutomatonTests, OATestFixture)

BOOST_AUTO_TEST_CASE( testBlockOverlap ) {
    // Each even block should be disjoint from each other even block. Same is true for odds.

    typedef OtherAutomaton::BlockList::const_iterator iterator;
    typedef std::pair<const OtherAutomaton::Block *, CellSet> pair;

    for (iterator odd=grid.oddBlocks.begin(); odd!=grid.oddBlocks.end(); ++odd) {
        std::map<const OtherAutomaton::Block *,CellSet> overlap;
        for (iterator even=grid.evenBlocks.begin(); even!=grid.evenBlocks.end(); ++even) {
            CellSet sharedCells = testOverlap(*even, *odd);
            if (sharedCells.size()>0) {
                overlap.insert(std::make_pair(&*even, sharedCells));
            }
        }
        if (overlap.size()) {
            BOOST_CHECK_EQUAL(overlap.size(), 2);
            BOOST_FOREACH(pair leftItem, overlap) {
                BOOST_CHECK_EQUAL(leftItem.second.size(),2);
            }
        }
    }

    typedef OtherAutomaton::BlockList::const_iterator iterator;
    for (iterator even=grid.evenBlocks.begin(); even!=grid.evenBlocks.end(); ++even) {
        std::map<const OtherAutomaton::Block *,CellSet> overlap;
        for (iterator odd=grid.oddBlocks.begin(); odd!=grid.oddBlocks.end(); ++odd) {
            CellSet sharedCells = testOverlap(*odd, *even);
            if (sharedCells.size()>0) {
                overlap.insert(std::make_pair(&*odd, sharedCells));
            }
        }
        if (overlap.size()) {
            BOOST_CHECK_EQUAL(overlap.size(), 2);
            BOOST_FOREACH(pair leftItem, overlap) {
                BOOST_CHECK_EQUAL(leftItem.second.size(),2);
            }
        }
    }
}

BOOST_AUTO_TEST_CASE( testDisjoint ) {
    int leftCount = 0;
    int rightCount = 0;
    BOOST_FOREACH(const OtherAutomaton::Block &left, grid.oddBlocks) {
        BOOST_FOREACH(const OtherAutomaton::Block &right, grid.oddBlocks) {
            if (left != right) {
                CellSet shouldBeEmpty = testOverlap(left,right);
                BOOST_CHECK(shouldBeEmpty.empty());
            }
            ++rightCount;
        }
        ++leftCount;
    }
}

BOOST_AUTO_TEST_CASE( testNotEmpty ) {
    BOOST_CHECK_GT(grid.size(),0);
    BOOST_CHECK_GT(grid.evenBlocks.size(), 0);
    BOOST_CHECK_GT(grid.oddBlocks.size(), 0);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(OtherAutomatonCell)

BOOST_AUTO_TEST_CASE( defaultValue ) {
    OtherAutomaton::Cell cell;
    BOOST_REQUIRE(!cell.isOccupied());
}

BOOST_AUTO_TEST_SUITE_END()<|endoftext|>