<commit_before>/*******************************************************************************
* ALMA - Atacama Large Millimiter Array
* (c) Associated Universities Inc., 2005
*
* This library 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*
* "@(#) $Id: testMacros.cpp,v 1.15 2010/03/26 23:24:15 javarias Exp $"
*
* who       when      what
* --------  --------  ----------------------------------------------
* dfugate  2005-04-04  created
*/

/************************************************************************
*   NAME
*
*
*   SYNOPSIS
*
*
*   DESCRIPTION
*
*   FILES
*
*   ENVIRONMENT
*
*   COMMANDS
*
*   RETURN VALUES
*
*   CAUTIONS
*
*   EXAMPLES
*
*   SEE ALSO
*
*   BUGS
*
*------------------------------------------------------------------------
*/

#include "logging.h"
#include <acsutilTimeStamp.h>

#include "loggingACSLogger.h"
#include "loggingHandler.h"
#include "loggingLogTrace.h"
#include "logging.h"

namespace Logging {

    class TestHandler : public virtual Handler
    {
      public:
	TestHandler()
	    {setLevel(LM_TRACE);}

	~TestHandler()
	    {}

	virtual void
	log(const LogRecord& lr)
	    {
		std::string niceTime = getStringifiedUTC(lr.timeStamp).c_str();
		std::cout << lr.priority << " " << lr.message << " " << lr.file << " " << lr.line << " " << lr.method << " " << niceTime << std::endl;
	    }

	virtual std::string
	getName() const
	    {return "TestHandler";}
    };
};

void testAutoTraceFunc()
{
	 AUTO_TRACE("testAutoTraceFunc");
}

static void testStaticLoggingWithAudience()
{
    STATIC_LOG(Logging::BaseLog::LM_INFO, __PRETTY_FUNCTION__,
            "Testing Static Log");
    STATIC_LOG_TO_DEVELOPER(LM_INFO, "STATIC_LOG_TO_DEVELOPER");
    STATIC_LOG_TO_OPERATOR(LM_INFO, "STATIC_LOG_TO_OPERATOR");
    STATIC_LOG_TO_SCIENCE(LM_INFO, "STATIC_LOG_TO_SCIENCE");
    STATIC_LOG_TO_SCILOG(LM_INFO, "STATIC_LOG_TO_SCILOG");
}

int main(int argc, char *argv[])
{
    char *tooLong_p = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";

    LoggingProxy m_logger(0, 0, 31, 0);
    LoggingProxy::init (&m_logger);

    ACS_CHECK_LOGGER;
    AUTO_TRACE("someFunc");

    testAutoTraceFunc();

    ACS_SHORT_LOG((LM_INFO, "%s a %s b %s c %s d %s e %s f %s g %s h %s i %s j %s k Should never see this...\n",
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p));

    //--------------------------------------------------------------------
    //Test ACS_LOG
    ACS_LOG(LM_RUNTIME_CONTEXT, "main",
	    (LM_INFO, "Test of formatted log 1 - %s",
	     "This is a string parameter"));

    ACS_LOG( LM_SOURCE_INFO, "main",
	    (LM_INFO, "Test of formatted log 2 - %s",
	     "This is a string parameter"));

    ACS_LOG( LM_FULL_INFO, "main",
	    (LM_INFO, "Test of formatted log 3 - %s",
	     "This is a string parameter"));

    //--------------------------------------------------------------------
    //Test ACS_LOG_TIME
    Logging::Logger::LoggerSmartPtr myLoggerSmartPtr = getLogger();
    Logging::Handler::HandlerSmartPtr localHandler(new Logging::TestHandler());
    myLoggerSmartPtr->addHandler(localHandler);
    ACS_LOG_TIME(0, getTimeStamp(), "someRoutineName",
		 (LM_ERROR, "%s", "The actual time..."));

    ACS_LOG_TIME(0, 132922080005000000ULL, "someRoutineName",
		 (LM_ERROR, "%s", "Should be January 1st 2004..."));
    myLoggerSmartPtr->removeHandler(localHandler->getName());
    //--------------------------------------------------------------------

    //--------------------------------------------------------------------
    //Test Add Data

    char *tooLongAddData_p =
         "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789LAST01234567890123456789012345678906789012345678901234567890123456789012345678901234567890";

    LoggingProxy::AddData("testTooLongValue", tooLongAddData_p);
    ACS_SHORT_LOG((LM_ERROR, "add data for this log message should be too long (max is 1024 characters)"));

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

  //Test Levels
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_TRACE, "Test of LM_TRACE log"));

    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_ERROR, "Test of LM_ERROR log"));
    ACS_LOG( LM_FULL_INFO, "main",
         (03, "Test of LM_DELOUSE log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_DEBUG, "Test of LM_DEBUG log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_INFO, "Test of LM_INFO log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_NOTICE, "Test of LM_NOTICE log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_WARNING, "Test of LM_WARNING log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_CRITICAL, "Test of LM_CRITICAL log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_ALERT, "Test of LM_ALERT log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_EMERGENCY, "Test of LM_EMERGENCY log"));

    //Test audience macros
    LOG_TO_AUDIENCE_WITH_LOGGER(LM_INFO,
            "Test of LOG_TO_AUDIENCE_WITH_LOGGER log",
            log_audience::OPERATOR, myLoggerSmartPtr);
    LOG_TO_DEVELOPER( LM_INFO, "Test of LOG_TO_DEVELOPER log");
    LOG_TO_DEVELOPER_WITH_LOGGER(LM_INFO,
            "Test of LOG_TO_DEVELOPER_WITH_LOGGER",
            myLoggerSmartPtr);
    LOG_TO_OPERATOR( LM_INFO, "Test of LOG_TO_OPERATOR log");
    LOG_TO_OPERATOR_WITH_LOGGER(LM_INFO, "Test of LOG_TO_OPERATOR_WITH_LOGGER",
            myLoggerSmartPtr);
    LOG_TO_SCIENCE( LM_INFO, "Test of LOG_TO_SCIENCE log");
    LOG_TO_SCILOG( LM_INFO, "Test of LOG_TO_SCILOG log");

    testStaticLoggingWithAudience();

    return 0;
}
<commit_msg>Changed the way to debug a DELOUSE log (using LM_DELOUSE)<commit_after>/*******************************************************************************
* ALMA - Atacama Large Millimiter Array
* (c) Associated Universities Inc., 2005
*
* This library 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
*
* "@(#) $Id: testMacros.cpp,v 1.16 2010/03/31 20:15:37 javarias Exp $"
*
* who       when      what
* --------  --------  ----------------------------------------------
* dfugate  2005-04-04  created
*/

/************************************************************************
*   NAME
*
*
*   SYNOPSIS
*
*
*   DESCRIPTION
*
*   FILES
*
*   ENVIRONMENT
*
*   COMMANDS
*
*   RETURN VALUES
*
*   CAUTIONS
*
*   EXAMPLES
*
*   SEE ALSO
*
*   BUGS
*
*------------------------------------------------------------------------
*/

#include "logging.h"
#include <acsutilTimeStamp.h>

#include "loggingACSLogger.h"
#include "loggingHandler.h"
#include "loggingLogTrace.h"
#include "logging.h"

namespace Logging {

    class TestHandler : public virtual Handler
    {
      public:
	TestHandler()
	    {setLevel(LM_TRACE);}

	~TestHandler()
	    {}

	virtual void
	log(const LogRecord& lr)
	    {
		std::string niceTime = getStringifiedUTC(lr.timeStamp).c_str();
		std::cout << lr.priority << " " << lr.message << " " << lr.file << " " << lr.line << " " << lr.method << " " << niceTime << std::endl;
	    }

	virtual std::string
	getName() const
	    {return "TestHandler";}
    };
};

void testAutoTraceFunc()
{
	 AUTO_TRACE("testAutoTraceFunc");
}

static void testStaticLoggingWithAudience()
{
    STATIC_LOG(Logging::BaseLog::LM_INFO, __PRETTY_FUNCTION__,
            "Testing Static Log");
    STATIC_LOG_TO_DEVELOPER(LM_INFO, "STATIC_LOG_TO_DEVELOPER");
    STATIC_LOG_TO_OPERATOR(LM_INFO, "STATIC_LOG_TO_OPERATOR");
    STATIC_LOG_TO_SCIENCE(LM_INFO, "STATIC_LOG_TO_SCIENCE");
    STATIC_LOG_TO_SCILOG(LM_INFO, "STATIC_LOG_TO_SCILOG");
}

int main(int argc, char *argv[])
{
    char *tooLong_p = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";

    LoggingProxy m_logger(0, 0, 31, 0);
    LoggingProxy::init (&m_logger);

    ACS_CHECK_LOGGER;
    AUTO_TRACE("someFunc");

    testAutoTraceFunc();

    ACS_SHORT_LOG((LM_INFO, "%s a %s b %s c %s d %s e %s f %s g %s h %s i %s j %s k Should never see this...\n",
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p,
		   tooLong_p));

    //--------------------------------------------------------------------
    //Test ACS_LOG
    ACS_LOG(LM_RUNTIME_CONTEXT, "main",
	    (LM_INFO, "Test of formatted log 1 - %s",
	     "This is a string parameter"));

    ACS_LOG( LM_SOURCE_INFO, "main",
	    (LM_INFO, "Test of formatted log 2 - %s",
	     "This is a string parameter"));

    ACS_LOG( LM_FULL_INFO, "main",
	    (LM_INFO, "Test of formatted log 3 - %s",
	     "This is a string parameter"));

    //--------------------------------------------------------------------
    //Test ACS_LOG_TIME
    Logging::Logger::LoggerSmartPtr myLoggerSmartPtr = getLogger();
    Logging::Handler::HandlerSmartPtr localHandler(new Logging::TestHandler());
    myLoggerSmartPtr->addHandler(localHandler);
    ACS_LOG_TIME(0, getTimeStamp(), "someRoutineName",
		 (LM_ERROR, "%s", "The actual time..."));

    ACS_LOG_TIME(0, 132922080005000000ULL, "someRoutineName",
		 (LM_ERROR, "%s", "Should be January 1st 2004..."));
    myLoggerSmartPtr->removeHandler(localHandler->getName());
    //--------------------------------------------------------------------

    //--------------------------------------------------------------------
    //Test Add Data

    char *tooLongAddData_p =
         "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\
1234567890123456789LAST01234567890123456789012345678906789012345678901234567890123456789012345678901234567890";

    LoggingProxy::AddData("testTooLongValue", tooLongAddData_p);
    ACS_SHORT_LOG((LM_ERROR, "add data for this log message should be too long (max is 1024 characters)"));

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

  //Test Levels
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_TRACE, "Test of LM_TRACE log"));

    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_ERROR, "Test of LM_ERROR log"));
    ACS_LOG( LM_FULL_INFO, "main",
         (LM_DELOUSE, "Test of LM_DELOUSE log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_DEBUG, "Test of LM_DEBUG log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_INFO, "Test of LM_INFO log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_NOTICE, "Test of LM_NOTICE log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_WARNING, "Test of LM_WARNING log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_CRITICAL, "Test of LM_CRITICAL log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_ALERT, "Test of LM_ALERT log"));
    ACS_LOG( LM_FULL_INFO, "main",
	     (LM_EMERGENCY, "Test of LM_EMERGENCY log"));

    //Test audience macros
    LOG_TO_AUDIENCE_WITH_LOGGER(LM_INFO,
            "Test of LOG_TO_AUDIENCE_WITH_LOGGER log",
            log_audience::OPERATOR, myLoggerSmartPtr);
    LOG_TO_DEVELOPER( LM_INFO, "Test of LOG_TO_DEVELOPER log");
    LOG_TO_DEVELOPER_WITH_LOGGER(LM_INFO,
            "Test of LOG_TO_DEVELOPER_WITH_LOGGER",
            myLoggerSmartPtr);
    LOG_TO_OPERATOR( LM_INFO, "Test of LOG_TO_OPERATOR log");
    LOG_TO_OPERATOR_WITH_LOGGER(LM_INFO, "Test of LOG_TO_OPERATOR_WITH_LOGGER",
            myLoggerSmartPtr);
    LOG_TO_SCIENCE( LM_INFO, "Test of LOG_TO_SCIENCE log");
    LOG_TO_SCILOG( LM_INFO, "Test of LOG_TO_SCILOG log");

    testStaticLoggingWithAudience();

    return 0;
}
<|endoftext|>


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

#ifndef __MDDS_GRID_MAP_TYPES_HPP__
#define __MDDS_GRID_MAP_TYPES_HPP__

#include <vector>

namespace mdds { namespace gridmap {

typedef int cell_t;

const cell_t celltype_numeric = 0;
const cell_t celltype_string  = 1;
const cell_t celltype_index   = 2;
const cell_t celltype_boolean = 3;

const cell_t celltype_user_start = 50;

struct base_cell_block;
cell_t get_block_type(const base_cell_block&);

struct base_cell_block
{
    friend cell_t get_block_type(const base_cell_block&);
protected:
    cell_t type;
    base_cell_block(cell_t _t) : type(_t) {}
};

template<typename _Self, cell_t _TypeId, typename _Data>
class cell_block : public base_cell_block
{
    struct print_block_array
    {
        void operator() (const _Data& val) const
        {
            std::cout << val << " ";
        }
    };

protected:
    typedef std::vector<_Data> store_type;
    store_type m_array;

    cell_block() : base_cell_block(_TypeId) {}
    cell_block(size_t n) : base_cell_block(_TypeId), m_array(n) {}

public:
    bool operator== (const _Self& r) const
    {
        return m_array == r.m_array;
    }

    bool operator!= (const _Self& r) const
    {
        return !operator==(r);
    }

    static _Self& get(base_cell_block& block)
    {
        if (get_block_type(block) != _TypeId)
            throw general_error("incorrect block type.");

        return static_cast<_Self&>(block);
    }

    static const _Self& get(const base_cell_block& block)
    {
        if (get_block_type(block) != _TypeId)
            throw general_error("incorrect block type.");

        return static_cast<const _Self&>(block);
    }

    static void set_value(base_cell_block& blk, size_t pos, const _Data& val)
    {
        get(blk).m_array[pos] = val;
    }

    static void get_value(const base_cell_block& blk, size_t pos, _Data& val)
    {
        val = get(blk).m_array[pos];
    }

    static void append_value(base_cell_block& blk, const _Data& val)
    {
        get(blk).m_array.push_back(val);
    }

    static void prepend_value(base_cell_block& blk, const _Data& val)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.insert(blk2.begin(), val);
    }

    static _Self* create_block(size_t init_size)
    {
        return new _Self(init_size);
    }

    static _Self* clone_block(const base_cell_block& blk)
    {
        return new _Self(get(blk));
    }

    static void delete_block(const base_cell_block* p)
    {
        delete static_cast<const _Self*>(p);
    }

    static void resize_block(base_cell_block& blk, size_t new_size)
    {
        static_cast<_Self&>(blk).m_array.resize(new_size);
    }

    static void print_block(const base_cell_block& blk)
    {
        const store_type& blk2 = get(blk).m_array;
        std::for_each(blk2.begin(), blk2.end(), print_block_array());
        std::cout << std::endl;
    }

    static void erase_block(base_cell_block& blk, size_t pos)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.erase(blk2.begin()+pos);
    }

    static void erase_block(base_cell_block& blk, size_t pos, size_t size)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.erase(blk2.begin()+pos, blk2.begin()+pos+size);
    }

    static void append_values_from_block(base_cell_block& dest, const base_cell_block& src)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        d.insert(d.end(), s.begin(), s.end());
    }

    static void append_values_from_block(
        base_cell_block& dest, const base_cell_block& src, size_t begin_pos, size_t len)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        typename store_type::const_iterator it = s.begin();
        std::advance(it, begin_pos);
        typename store_type::const_iterator it_end = it;
        std::advance(it_end, len);
        d.reserve(d.size() + len);
        std::copy(it, it_end, std::back_inserter(d));
    }

    static void assign_values_from_block(
        base_cell_block& dest, const base_cell_block& src, size_t begin_pos, size_t len)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        typename store_type::const_iterator it = s.begin();
        std::advance(it, begin_pos);
        typename store_type::const_iterator it_end = it;
        std::advance(it_end, len);
        d.assign(it, it_end);
    }

    template<typename _Iter>
    static void set_values(
        base_cell_block& block, size_t pos, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        for (_Iter it = it_begin; it != it_end; ++it, ++pos)
            d[pos] = *it;
    }

    template<typename _Iter>
    static void append_values(base_cell_block& block, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        typename store_type::iterator it = d.end();
        d.insert(it, it_begin, it_end);
    }

    template<typename _Iter>
    static void prepend_values(base_cell_block& block, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        d.insert(d.begin(), it_begin, it_end);
    }

    template<typename _Iter>
    static void assign_values(base_cell_block& dest, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(dest).m_array;
        d.assign(it_begin, it_end);
    }

    template<typename _Iter>
    static void insert_values(
        base_cell_block& block, size_t pos, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& blk = get(block).m_array;
        blk.insert(blk.begin()+pos, it_begin, it_end);
    }
};

/**
 * Get the numerical block type ID from a given cell block instance.
 *
 * @param blk cell block instance
 *
 * @return numerical value representing the ID of a cell block.
 */
inline cell_t get_block_type(const base_cell_block& blk)
{
    return blk.type;
}

/**
 * Template for default, unmanaged cell block for use in grid_map.
 */
template<cell_t _TypeId, typename _Data>
struct default_cell_block : public cell_block<default_cell_block<_TypeId,_Data>, _TypeId, _Data>
{
    typedef cell_block<default_cell_block, _TypeId, _Data> base_type;

    default_cell_block() : base_type() {}
    default_cell_block(size_t n) : base_type(n) {}

    static void overwrite_cells(base_cell_block&, size_t, size_t)
    {
        // Do nothing.
    }
};

/**
 * Template for cell block that stores pointers to objects whose life cycles
 * are managed by the block.
 */
template<cell_t _TypeId, typename _Data>
struct managed_cell_block : public cell_block<managed_cell_block<_TypeId,_Data>, _TypeId, _Data*>
{
    typedef cell_block<managed_cell_block<_TypeId,_Data>, _TypeId, _Data*> base_type;

    using base_type::get;
    using base_type::m_array;

    managed_cell_block() : base_type() {}
    managed_cell_block(size_t n) : base_type(n) {}
    managed_cell_block(const managed_cell_block& r)
    {
        m_array.reserve(r.m_array.size());
        typename managed_cell_block::store_type::const_iterator it = r.m_array.begin(), it_end = r.m_array.end();
        for (; it != it_end; ++it)
            m_array.push_back(new _Data(**it));
    }

    ~managed_cell_block()
    {
        std::for_each(m_array.begin(), m_array.end(), default_deleter<_Data>());
    }

    static void overwrite_cells(base_cell_block& block, size_t pos, size_t len)
    {
        managed_cell_block& blk = get(block);
        typename managed_cell_block::store_type::iterator it = blk.m_array.begin() + pos;
        typename managed_cell_block::store_type::iterator it_end = it + len;
        std::for_each(it, it_end, default_deleter<_Data>());
    }
};

}}

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

#ifndef __MDDS_GRID_MAP_TYPES_HPP__
#define __MDDS_GRID_MAP_TYPES_HPP__

#include "mdds/default_deleter.hpp"

#include <vector>

namespace mdds { namespace gridmap {

typedef int cell_t;

const cell_t celltype_numeric = 0;
const cell_t celltype_string  = 1;
const cell_t celltype_index   = 2;
const cell_t celltype_boolean = 3;

const cell_t celltype_user_start = 50;

struct base_cell_block;
cell_t get_block_type(const base_cell_block&);

struct base_cell_block
{
    friend cell_t get_block_type(const base_cell_block&);
protected:
    cell_t type;
    base_cell_block(cell_t _t) : type(_t) {}
};

template<typename _Self, cell_t _TypeId, typename _Data>
class cell_block : public base_cell_block
{
    struct print_block_array
    {
        void operator() (const _Data& val) const
        {
            std::cout << val << " ";
        }
    };

protected:
    typedef std::vector<_Data> store_type;
    store_type m_array;

    cell_block() : base_cell_block(_TypeId) {}
    cell_block(size_t n) : base_cell_block(_TypeId), m_array(n) {}

public:
    bool operator== (const _Self& r) const
    {
        return m_array == r.m_array;
    }

    bool operator!= (const _Self& r) const
    {
        return !operator==(r);
    }

    static _Self& get(base_cell_block& block)
    {
        if (get_block_type(block) != _TypeId)
            throw general_error("incorrect block type.");

        return static_cast<_Self&>(block);
    }

    static const _Self& get(const base_cell_block& block)
    {
        if (get_block_type(block) != _TypeId)
            throw general_error("incorrect block type.");

        return static_cast<const _Self&>(block);
    }

    static void set_value(base_cell_block& blk, size_t pos, const _Data& val)
    {
        get(blk).m_array[pos] = val;
    }

    static void get_value(const base_cell_block& blk, size_t pos, _Data& val)
    {
        val = get(blk).m_array[pos];
    }

    static void append_value(base_cell_block& blk, const _Data& val)
    {
        get(blk).m_array.push_back(val);
    }

    static void prepend_value(base_cell_block& blk, const _Data& val)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.insert(blk2.begin(), val);
    }

    static _Self* create_block(size_t init_size)
    {
        return new _Self(init_size);
    }

    static _Self* clone_block(const base_cell_block& blk)
    {
        return new _Self(get(blk));
    }

    static void delete_block(const base_cell_block* p)
    {
        delete static_cast<const _Self*>(p);
    }

    static void resize_block(base_cell_block& blk, size_t new_size)
    {
        static_cast<_Self&>(blk).m_array.resize(new_size);
    }

    static void print_block(const base_cell_block& blk)
    {
        const store_type& blk2 = get(blk).m_array;
        std::for_each(blk2.begin(), blk2.end(), print_block_array());
        std::cout << std::endl;
    }

    static void erase_block(base_cell_block& blk, size_t pos)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.erase(blk2.begin()+pos);
    }

    static void erase_block(base_cell_block& blk, size_t pos, size_t size)
    {
        store_type& blk2 = get(blk).m_array;
        blk2.erase(blk2.begin()+pos, blk2.begin()+pos+size);
    }

    static void append_values_from_block(base_cell_block& dest, const base_cell_block& src)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        d.insert(d.end(), s.begin(), s.end());
    }

    static void append_values_from_block(
        base_cell_block& dest, const base_cell_block& src, size_t begin_pos, size_t len)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        typename store_type::const_iterator it = s.begin();
        std::advance(it, begin_pos);
        typename store_type::const_iterator it_end = it;
        std::advance(it_end, len);
        d.reserve(d.size() + len);
        std::copy(it, it_end, std::back_inserter(d));
    }

    static void assign_values_from_block(
        base_cell_block& dest, const base_cell_block& src, size_t begin_pos, size_t len)
    {
        store_type& d = get(dest).m_array;
        const store_type& s = get(src).m_array;
        typename store_type::const_iterator it = s.begin();
        std::advance(it, begin_pos);
        typename store_type::const_iterator it_end = it;
        std::advance(it_end, len);
        d.assign(it, it_end);
    }

    template<typename _Iter>
    static void set_values(
        base_cell_block& block, size_t pos, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        for (_Iter it = it_begin; it != it_end; ++it, ++pos)
            d[pos] = *it;
    }

    template<typename _Iter>
    static void append_values(base_cell_block& block, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        typename store_type::iterator it = d.end();
        d.insert(it, it_begin, it_end);
    }

    template<typename _Iter>
    static void prepend_values(base_cell_block& block, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(block).m_array;
        d.insert(d.begin(), it_begin, it_end);
    }

    template<typename _Iter>
    static void assign_values(base_cell_block& dest, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& d = get(dest).m_array;
        d.assign(it_begin, it_end);
    }

    template<typename _Iter>
    static void insert_values(
        base_cell_block& block, size_t pos, const _Iter& it_begin, const _Iter& it_end)
    {
        store_type& blk = get(block).m_array;
        blk.insert(blk.begin()+pos, it_begin, it_end);
    }
};

/**
 * Get the numerical block type ID from a given cell block instance.
 *
 * @param blk cell block instance
 *
 * @return numerical value representing the ID of a cell block.
 */
inline cell_t get_block_type(const base_cell_block& blk)
{
    return blk.type;
}

/**
 * Template for default, unmanaged cell block for use in grid_map.
 */
template<cell_t _TypeId, typename _Data>
struct default_cell_block : public cell_block<default_cell_block<_TypeId,_Data>, _TypeId, _Data>
{
    typedef cell_block<default_cell_block, _TypeId, _Data> base_type;

    default_cell_block() : base_type() {}
    default_cell_block(size_t n) : base_type(n) {}

    static void overwrite_cells(base_cell_block&, size_t, size_t)
    {
        // Do nothing.
    }
};

/**
 * Template for cell block that stores pointers to objects whose life cycles
 * are managed by the block.
 */
template<cell_t _TypeId, typename _Data>
struct managed_cell_block : public cell_block<managed_cell_block<_TypeId,_Data>, _TypeId, _Data*>
{
    typedef cell_block<managed_cell_block<_TypeId,_Data>, _TypeId, _Data*> base_type;

    using base_type::get;
    using base_type::m_array;

    managed_cell_block() : base_type() {}
    managed_cell_block(size_t n) : base_type(n) {}
    managed_cell_block(const managed_cell_block& r)
    {
        m_array.reserve(r.m_array.size());
        typename managed_cell_block::store_type::const_iterator it = r.m_array.begin(), it_end = r.m_array.end();
        for (; it != it_end; ++it)
            m_array.push_back(new _Data(**it));
    }

    ~managed_cell_block()
    {
        std::for_each(m_array.begin(), m_array.end(), mdds::default_deleter<_Data>());
    }

    static void overwrite_cells(base_cell_block& block, size_t pos, size_t len)
    {
        managed_cell_block& blk = get(block);
        typename managed_cell_block::store_type::iterator it = blk.m_array.begin() + pos;
        typename managed_cell_block::store_type::iterator it_end = it + len;
        std::for_each(it, it_end, mdds::default_deleter<_Data>());
    }
};

}}

#endif
<|endoftext|>


<commit_before>/*=============================================================================

  Library: CTK

  Copyright (c) German Cancer Research Center,
    Division of Medical and Biological Informatics

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=============================================================================*/

#include "ctkPluginFrameworkContext_p.h"
#include "ctkPluginFrameworkUtil_p.h"
#include "ctkPluginFramework_p.h"
#include "ctkPluginArchive_p.h"
#include "ctkPluginStorageSQL_p.h"
#include "ctkPluginConstants.h"
#include "ctkServices_p.h"
#include "ctkUtils.h"

//----------------------------------------------------------------------------
QMutex ctkPluginFrameworkContext::globalFwLock;
int ctkPluginFrameworkContext::globalId = 1;

//----------------------------------------------------------------------------
ctkPluginFrameworkContext::ctkPluginFrameworkContext(
    const ctkProperties& initProps)
  : plugins(0), listeners(this), services(0), systemPlugin(new ctkPluginFramework()),
    storage(0), firstInit(true), props(initProps), debug(props),
    initialized(false)
{

  {
    QMutexLocker lock(&globalFwLock);
    id = globalId++;
    systemPlugin->ctkPlugin::init(new ctkPluginFrameworkPrivate(systemPlugin, this));
  }

  initProperties();
  log() << "created";
}

//----------------------------------------------------------------------------
ctkPluginFrameworkContext::~ctkPluginFrameworkContext()
{
  if (initialized)
  {
    this->uninit();
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::initProperties()
{
  props[ctkPluginConstants::FRAMEWORK_VERSION] = "0.9";
  props[ctkPluginConstants::FRAMEWORK_VENDOR] = "CommonTK";
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::init()
{
  log() << "initializing";

  if (firstInit && ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT
      == props[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN])
  {
    deleteFWDir();
    firstInit = false;
  }

  ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  systemPluginPrivate->initSystemPlugin();

  storage = new ctkPluginStorageSQL(this);
  dataStorage = ctkPluginFrameworkUtil::getFileStorage(this, "data");
  services = new ctkServices(this);
  plugins = new ctkPlugins(this);

  // Pre-load libraries
  // This may speed up installing new plug-ins if they have dependencies on
  // one of these libraries. It prevents repeated loading and unloading of the
  // pre-loaded libraries during caching of the plug-in meta-data.
  if (props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].isValid())
  {
    QStringList preloadLibs = props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].toStringList();
    QLibrary::LoadHints loadHints;
    QVariant loadHintsVariant = props[ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS];
    if (loadHintsVariant.isValid())
    {
      loadHints = loadHintsVariant.value<QLibrary::LoadHints>();
    }

    foreach(QString preloadLib, preloadLibs)
    {
      QLibrary lib;
      QStringList nameAndVersion = preloadLib.split(":");

      QString libraryName;
      if (nameAndVersion.count() == 1)
      {
        libraryName = nameAndVersion.front();
        lib.setFileName(nameAndVersion.front());
      }
      else if (nameAndVersion.count() == 2)
      {
        libraryName = nameAndVersion.front() + "." + nameAndVersion.back();
        lib.setFileNameAndVersion(nameAndVersion.front(), nameAndVersion.back());
      }
      else
      {
        qWarning() << "Wrong syntax in" << preloadLib << ". Use <lib-name>[:version]. Skipping.";
        continue;
      }

      lib.setLoadHints(loadHints);
      log() << "Pre-loading library" << libraryName << "with hints [" << static_cast<int>(loadHints) << "]";
      if (!lib.load())
      {
        qWarning() << "Pre-loading library" << libraryName << "failed. Check your library search paths.";
      }
    }
  }

  plugins->load();

  log() << "inited";
  initialized = true;

  log() << "Installed plugins:";
  // Use the ordering in the plugin storage to get a sorted list of plugins.
  QList<QSharedPointer<ctkPluginArchive> > allPAs = storage->getAllPluginArchives();
  foreach (QSharedPointer<ctkPluginArchive> pa, allPAs)
  {
    QSharedPointer<ctkPlugin> plugin = plugins->getPlugin(pa->getPluginLocation().toString());
    log() << " #" << plugin->getPluginId() << " " << plugin->getSymbolicName() << ":"
        << plugin->getVersion() << " location:" << plugin->getLocation();
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::uninit()
{
  if (!initialized) return;

  log() << "uninit";

  ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  systemPluginPrivate->uninitSystemPlugin();

  plugins->clear();
  delete plugins;
  plugins = 0;

  delete storage; // calls storage->close()
  storage = 0;

  delete services;
  services = 0;

  initialized = false;
}

//----------------------------------------------------------------------------
int ctkPluginFrameworkContext::getId() const
{
  return id;
}

//----------------------------------------------------------------------------
QFileInfo ctkPluginFrameworkContext::getDataStorage(long id)
{
  return QFileInfo(dataStorage.absolutePath() + '/' + QString::number(id) + '/');
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::checkOurPlugin(ctkPlugin* plugin) const
{
  ctkPluginPrivate* pp = plugin->d_func();
  if (this != pp->fwCtx)
  {
    throw ctkInvalidArgumentException("ctkPlugin does not belong to this framework: " + plugin->getSymbolicName());
  }
}

//----------------------------------------------------------------------------
QDebug ctkPluginFrameworkContext::log() const
{
  static QString nirvana;
  nirvana.clear();
  if (debug.framework)
    return qDebug() << "Framework instance " << getId() << ": ";
  else
    return QDebug(&nirvana);
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::resolvePlugin(ctkPluginPrivate* plugin)
{
  if (debug.resolve)
  {
    qDebug() << "resolve:" << plugin->symbolicName << "[" << plugin->id << "]";
  }

  // If we enter with tempResolved set, it means that we already have
  // resolved plugins. Check that it is true!
  if (tempResolved.size() > 0 && !tempResolved.contains(plugin))
  {
    ctkPluginException pe("resolve: InternalError1!", ctkPluginException::RESOLVE_ERROR);
    listeners.frameworkError(plugin->q_func(), pe);
    throw pe;
  }

  tempResolved.clear();
  tempResolved.insert(plugin);

  checkRequirePlugin(plugin);

  tempResolved.clear();

  if (debug.resolve)
  {
    qDebug() << "resolve: Done for" << plugin->symbolicName << "[" << plugin->id << "]";
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::checkRequirePlugin(ctkPluginPrivate *plugin)
{
  if (!plugin->require.isEmpty())
  {
    if (debug.resolve)
    {
      qDebug() << "checkRequirePlugin: check requiring plugin" << plugin->id;
    }

    QListIterator<ctkRequirePlugin*> i(plugin->require);
    while (i.hasNext())
    {
      ctkRequirePlugin* pr = i.next();
      QList<ctkPlugin*> pl = plugins->getPlugins(pr->name, pr->pluginRange);
      ctkPluginPrivate* ok = 0;
      for (QListIterator<ctkPlugin*> pci(pl); pci.hasNext() && ok == 0; )
      {
        ctkPluginPrivate* p2 = pci.next()->d_func();
        if (tempResolved.contains(p2))
        {
          ok = p2;
        }
        else if (ctkPluginPrivate::RESOLVED_FLAGS & p2->state)
        {
          ok = p2;
        }
        else if (p2->state == ctkPlugin::INSTALLED) {
          QSet<ctkPluginPrivate*> oldTempResolved = tempResolved;
          tempResolved.insert(p2);

          // TODO check if operation locking is correct in case of
          // multi-threaded plug-in start up. Maybe refactor out the dependency
          // checking (use the "package" lock)
          ctkPluginPrivate::Locker sync(&p2->operationLock);
          p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::RESOLVING);
          checkRequirePlugin(p2);
          tempResolved = oldTempResolved;
          p2->state = ctkPlugin::RESOLVED;
          listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, p2->q_func()));
          p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::IDLE);
          ok = p2;
        }
      }

      if (!ok && pr->resolution == ctkPluginConstants::RESOLUTION_MANDATORY)
      {
        tempResolved.clear();
        if (debug.resolve)
        {
          qDebug() << "checkRequirePlugin: failed to satisfy:" << pr->name;
        }
        throw ctkPluginException(QString("Failed to resolve required plugin: %1").arg(pr->name));
      }
    }
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::deleteFWDir()
{
  QString d = ctkPluginFrameworkUtil::getFrameworkDir(this);

  QFileInfo fwDirInfo(d);
  if (fwDirInfo.exists())
  {
    if(fwDirInfo.isDir())
    {
      log() << "deleting old framework directory.";
      bool bOK = ctk::removeDirRecursively(fwDirInfo.absoluteFilePath());
      if(!bOK)
      {
        qDebug() << "Failed to remove existing fwdir" << fwDirInfo.absoluteFilePath();
      }
    }
  }
}
<commit_msg>Moved library pre-loading before framework initialization. Fixes #324.<commit_after>/*=============================================================================

  Library: CTK

  Copyright (c) German Cancer Research Center,
    Division of Medical and Biological Informatics

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

=============================================================================*/

#include "ctkPluginFrameworkContext_p.h"
#include "ctkPluginFrameworkUtil_p.h"
#include "ctkPluginFramework_p.h"
#include "ctkPluginArchive_p.h"
#include "ctkPluginStorageSQL_p.h"
#include "ctkPluginConstants.h"
#include "ctkServices_p.h"
#include "ctkUtils.h"

//----------------------------------------------------------------------------
QMutex ctkPluginFrameworkContext::globalFwLock;
int ctkPluginFrameworkContext::globalId = 1;

//----------------------------------------------------------------------------
ctkPluginFrameworkContext::ctkPluginFrameworkContext(
    const ctkProperties& initProps)
  : plugins(0), listeners(this), services(0), systemPlugin(new ctkPluginFramework()),
    storage(0), firstInit(true), props(initProps), debug(props),
    initialized(false)
{

  {
    QMutexLocker lock(&globalFwLock);
    id = globalId++;
    systemPlugin->ctkPlugin::init(new ctkPluginFrameworkPrivate(systemPlugin, this));
  }

  initProperties();
  log() << "created";
}

//----------------------------------------------------------------------------
ctkPluginFrameworkContext::~ctkPluginFrameworkContext()
{
  if (initialized)
  {
    this->uninit();
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::initProperties()
{
  props[ctkPluginConstants::FRAMEWORK_VERSION] = "0.9";
  props[ctkPluginConstants::FRAMEWORK_VENDOR] = "CommonTK";
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::init()
{
  log() << "initializing";

  if (firstInit && ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT
      == props[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN])
  {
    deleteFWDir();
    firstInit = false;
  }

  // Pre-load libraries
  // This may speed up installing new plug-ins if they have dependencies on
  // one of these libraries. It prevents repeated loading and unloading of the
  // pre-loaded libraries during caching of the plug-in meta-data.
  if (props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].isValid())
  {
    QStringList preloadLibs = props[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES].toStringList();
    QLibrary::LoadHints loadHints;
    QVariant loadHintsVariant = props[ctkPluginConstants::FRAMEWORK_PLUGIN_LOAD_HINTS];
    if (loadHintsVariant.isValid())
    {
      loadHints = loadHintsVariant.value<QLibrary::LoadHints>();
    }

    foreach(QString preloadLib, preloadLibs)
    {
      QLibrary lib;
      QStringList nameAndVersion = preloadLib.split(":");

      QString libraryName;
      if (nameAndVersion.count() == 1)
      {
        libraryName = nameAndVersion.front();
        lib.setFileName(nameAndVersion.front());
      }
      else if (nameAndVersion.count() == 2)
      {
        libraryName = nameAndVersion.front() + "." + nameAndVersion.back();
        lib.setFileNameAndVersion(nameAndVersion.front(), nameAndVersion.back());
      }
      else
      {
        qWarning() << "Wrong syntax in" << preloadLib << ". Use <lib-name>[:version]. Skipping.";
        continue;
      }

      lib.setLoadHints(loadHints);
      log() << "Pre-loading library" << lib.fileName() << "with hints [" << static_cast<int>(loadHints) << "]";
      if (!lib.load())
      {
        qWarning() << "Pre-loading library" << lib.fileName() << "failed:" << lib.errorString() << "\nCheck your library search paths.";
      }
    }
  }

  ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  systemPluginPrivate->initSystemPlugin();

  storage = new ctkPluginStorageSQL(this);
  dataStorage = ctkPluginFrameworkUtil::getFileStorage(this, "data");
  services = new ctkServices(this);
  plugins = new ctkPlugins(this);

  plugins->load();

  log() << "inited";
  initialized = true;

  log() << "Installed plugins:";
  // Use the ordering in the plugin storage to get a sorted list of plugins.
  QList<QSharedPointer<ctkPluginArchive> > allPAs = storage->getAllPluginArchives();
  foreach (QSharedPointer<ctkPluginArchive> pa, allPAs)
  {
    QSharedPointer<ctkPlugin> plugin = plugins->getPlugin(pa->getPluginLocation().toString());
    log() << " #" << plugin->getPluginId() << " " << plugin->getSymbolicName() << ":"
        << plugin->getVersion() << " location:" << plugin->getLocation();
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::uninit()
{
  if (!initialized) return;

  log() << "uninit";

  ctkPluginFrameworkPrivate* const systemPluginPrivate = systemPlugin->d_func();
  systemPluginPrivate->uninitSystemPlugin();

  plugins->clear();
  delete plugins;
  plugins = 0;

  delete storage; // calls storage->close()
  storage = 0;

  delete services;
  services = 0;

  initialized = false;
}

//----------------------------------------------------------------------------
int ctkPluginFrameworkContext::getId() const
{
  return id;
}

//----------------------------------------------------------------------------
QFileInfo ctkPluginFrameworkContext::getDataStorage(long id)
{
  return QFileInfo(dataStorage.absolutePath() + '/' + QString::number(id) + '/');
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::checkOurPlugin(ctkPlugin* plugin) const
{
  ctkPluginPrivate* pp = plugin->d_func();
  if (this != pp->fwCtx)
  {
    throw ctkInvalidArgumentException("ctkPlugin does not belong to this framework: " + plugin->getSymbolicName());
  }
}

//----------------------------------------------------------------------------
QDebug ctkPluginFrameworkContext::log() const
{
  static QString nirvana;
  nirvana.clear();
  if (debug.framework)
    return qDebug() << "Framework instance " << getId() << ": ";
  else
    return QDebug(&nirvana);
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::resolvePlugin(ctkPluginPrivate* plugin)
{
  if (debug.resolve)
  {
    qDebug() << "resolve:" << plugin->symbolicName << "[" << plugin->id << "]";
  }

  // If we enter with tempResolved set, it means that we already have
  // resolved plugins. Check that it is true!
  if (tempResolved.size() > 0 && !tempResolved.contains(plugin))
  {
    ctkPluginException pe("resolve: InternalError1!", ctkPluginException::RESOLVE_ERROR);
    listeners.frameworkError(plugin->q_func(), pe);
    throw pe;
  }

  tempResolved.clear();
  tempResolved.insert(plugin);

  checkRequirePlugin(plugin);

  tempResolved.clear();

  if (debug.resolve)
  {
    qDebug() << "resolve: Done for" << plugin->symbolicName << "[" << plugin->id << "]";
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::checkRequirePlugin(ctkPluginPrivate *plugin)
{
  if (!plugin->require.isEmpty())
  {
    if (debug.resolve)
    {
      qDebug() << "checkRequirePlugin: check requiring plugin" << plugin->id;
    }

    QListIterator<ctkRequirePlugin*> i(plugin->require);
    while (i.hasNext())
    {
      ctkRequirePlugin* pr = i.next();
      QList<ctkPlugin*> pl = plugins->getPlugins(pr->name, pr->pluginRange);
      ctkPluginPrivate* ok = 0;
      for (QListIterator<ctkPlugin*> pci(pl); pci.hasNext() && ok == 0; )
      {
        ctkPluginPrivate* p2 = pci.next()->d_func();
        if (tempResolved.contains(p2))
        {
          ok = p2;
        }
        else if (ctkPluginPrivate::RESOLVED_FLAGS & p2->state)
        {
          ok = p2;
        }
        else if (p2->state == ctkPlugin::INSTALLED) {
          QSet<ctkPluginPrivate*> oldTempResolved = tempResolved;
          tempResolved.insert(p2);

          // TODO check if operation locking is correct in case of
          // multi-threaded plug-in start up. Maybe refactor out the dependency
          // checking (use the "package" lock)
          ctkPluginPrivate::Locker sync(&p2->operationLock);
          p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::RESOLVING);
          checkRequirePlugin(p2);
          tempResolved = oldTempResolved;
          p2->state = ctkPlugin::RESOLVED;
          listeners.emitPluginChanged(ctkPluginEvent(ctkPluginEvent::RESOLVED, p2->q_func()));
          p2->operation.fetchAndStoreOrdered(ctkPluginPrivate::IDLE);
          ok = p2;
        }
      }

      if (!ok && pr->resolution == ctkPluginConstants::RESOLUTION_MANDATORY)
      {
        tempResolved.clear();
        if (debug.resolve)
        {
          qDebug() << "checkRequirePlugin: failed to satisfy:" << pr->name;
        }
        throw ctkPluginException(QString("Failed to resolve required plugin: %1").arg(pr->name));
      }
    }
  }
}

//----------------------------------------------------------------------------
void ctkPluginFrameworkContext::deleteFWDir()
{
  QString d = ctkPluginFrameworkUtil::getFrameworkDir(this);

  QFileInfo fwDirInfo(d);
  if (fwDirInfo.exists())
  {
    if(fwDirInfo.isDir())
    {
      log() << "deleting old framework directory.";
      bool bOK = ctk::removeDirRecursively(fwDirInfo.absoluteFilePath());
      if(!bOK)
      {
        qDebug() << "Failed to remove existing fwdir" << fwDirInfo.absoluteFilePath();
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 Conrado Miranda

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Each archive has a entries with the following format:
// 1) Number of entries (size_t);
// 2.1) Size of the key (size_t);
// 2.2) Size of the object (size_t);
// 2.3) Key as serialized by boost;
// 2.4) Object as serialized by boost.

#ifndef __OBJECT_ARCHIVE_IMPL_HPP__
#define __OBJECT_ARCHIVE_IMPL_HPP__

#include "object_archive.hpp"

#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/zlib.hpp>

#if ENABLE_THREADS
#define OBJECT_ARCHIVE_MUTEX_GUARD \
  boost::lock_guard<boost::recursive_mutex> __mutex_guard(mutex_)
#else
#define OBJECT_ARCHIVE_MUTEX_GUARD do { } while(0)
#endif

template <class Key>
ObjectArchive<Key>::ObjectArchive():
  must_rebuild_file_(false),
  max_buffer_size_(0),
  buffer_size_(0),
  temporary_file_(false) {
    init();
    set_buffer_size(0);
}

template <class Key>
ObjectArchive<Key>::~ObjectArchive() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  if (!temporary_file_)
    internal_flush();
  stream_.close();
  if (temporary_file_)
    boost::filesystem::remove(filename_);
}

template <class Key>
template <class T>
std::string ObjectArchive<Key>::serialize(T const& val) {
  std::stringstream stream;
  {
    boost::iostreams::filtering_stream<boost::iostreams::output> filtering;
    filtering.push(boost::iostreams::zlib_compressor());
    filtering.push(stream);
    boost::archive::binary_oarchive ofs(filtering);
    ofs << val;
  }

  return stream.str();
}

template <class Key>
template <class T1, class T2>
std::string ObjectArchive<Key>::serialize(T2 const& val) {
  std::stringstream stream;
  {
    boost::iostreams::filtering_stream<boost::iostreams::output> filtering;
    filtering.push(boost::iostreams::zlib_compressor());
    filtering.push(stream);
    boost::archive::binary_oarchive ofs(filtering);
    ofs.register_type<T1>();
    ofs << val;
  }

  return stream.str();
}

template <class Key>
template <class T>
void ObjectArchive<Key>::deserialize(std::string const& str, T& val) {
  std::stringstream stream(str);
  boost::iostreams::filtering_stream<boost::iostreams::input> filtering;
  filtering.push(boost::iostreams::zlib_decompressor());
  filtering.push(stream);
  boost::archive::binary_iarchive ifs(filtering);

  ifs >> val;
}

template <class Key>
void ObjectArchive<Key>::init() {
  std::string filename;
  filename = boost::filesystem::temp_directory_path().string();
  filename += '/';
  filename += boost::filesystem::unique_path().string();

  init(filename, true);
}

template <class Key>
void ObjectArchive<Key>::init(std::string const& filename,
    bool temporary_file) {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  internal_flush();

  stream_.close();
  if (temporary_file_)
    boost::filesystem::remove(filename_);

  filename_ = filename;
  temporary_file_ = temporary_file;

  buffer_size_ = 0;
  objects_.clear();
  LRU_.clear();

  stream_.open(filename, std::ios_base::in | std::ios_base::out |
                         std::ios_base::binary);
  stream_.seekg(0, std::ios_base::end);

  // If the file seems ok and has entries, use it. Otherwise, overwrite.
  if (stream_.good() && stream_.tellg() > 0) {
    stream_.seekg(0);

    size_t n_entries;
    stream_.read((char*)&n_entries, sizeof(size_t));

    for (size_t i = 0; i < n_entries; i++) {
      size_t key_size;
      size_t data_size;
      stream_.read((char*)&key_size, sizeof(size_t));
      stream_.read((char*)&data_size, sizeof(size_t));

      std::string key_string;
      key_string.resize(key_size);
      stream_.read(&key_string[0], key_size);

      Key key;
      deserialize(key_string, key);

      ObjectEntry entry;
      entry.index_in_file = stream_.tellg();
      entry.size = data_size;
      entry.modified = false;
      auto it = objects_.emplace(key, entry).first;
      it->second.key = &it->first;

      stream_.seekg(data_size, std::ios_base::cur);
    }
  }
  else {
    stream_.open(filename, std::ios_base::in | std::ios_base::out |
        std::ios_base::binary | std::ios_base::trunc);
  }
}

template <class Key>
void ObjectArchive<Key>::set_buffer_size(size_t max_buffer_size) {
  max_buffer_size_ = max_buffer_size;
  unload(max_buffer_size);
}

template <class Key>
void ObjectArchive<Key>::set_buffer_size(std::string const& max_buffer_size) {
  size_t length = max_buffer_size.size();
  double buffer_size = atof(max_buffer_size.c_str());

  bool changed = false;
  for (size_t i = 0; i< length && !changed; i++) {
    switch (max_buffer_size[i]) {
      case 'k':
      case 'K':
        buffer_size *= 1e3;
        changed = true;
        break;
      case 'm':
      case 'M':
        buffer_size *= 1e6;
        changed = true;
        break;
      case 'g':
      case 'G':
        buffer_size *= 1e9;
        changed = true;
        break;
    }
  }

  set_buffer_size(buffer_size);
}

#if BOOST_OS_LINUX
#include <sys/sysinfo.h>

template <class Key>
void ObjectArchive<Key>::set_buffer_size_scale(float max_buffer_size) {
  struct sysinfo info;
  if (sysinfo(&info) == 0) {
    unsigned long freeram = info.freeram;
    set_buffer_size(freeram * max_buffer_size);
  }
}
#endif

template <class Key>
size_t ObjectArchive<Key>::get_max_buffer_size() const {
  return max_buffer_size_;
}

template <class Key>
size_t ObjectArchive<Key>::get_buffer_size() const {
  return buffer_size_;
}

template <class Key>
void ObjectArchive<Key>::remove(Key const& key) {
  if (!is_available(key))
    return;

  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto it = objects_.find(key);
  if (it == objects_.end())
    return;

  ObjectEntry& entry = it->second;
  if (entry.data.size())
    buffer_size_ -= entry.size;
  objects_.erase(key);
  LRU_.remove(&entry);
  must_rebuild_file_ = true;
}

template <class Key>
template <class T>
size_t ObjectArchive<Key>::insert(Key const& key, T const& obj,
    bool keep_in_buffer) {
  return insert_raw(key, serialize(obj), keep_in_buffer);
}

template <class Key>
size_t ObjectArchive<Key>::insert_raw(Key const& key, std::string const& data,
    bool keep_in_buffer) {
  return insert_raw(key, std::string(data), keep_in_buffer);
}

template <class Key>
size_t ObjectArchive<Key>::insert_raw(Key const& key, std::string&& data,
    bool keep_in_buffer) {
  size_t size = data.size();
  if (size > max_buffer_size_)
    keep_in_buffer = false;

  ObjectArchive<Key>::remove(key);

  if (size + buffer_size_ > max_buffer_size_ && keep_in_buffer)
    unload(max_buffer_size_ - size);

  OBJECT_ARCHIVE_MUTEX_GUARD;

  buffer_size_ += size;

  ObjectEntry entry;
  entry.data.swap(data);
  entry.size = size;
  entry.modified = true;
  auto it = objects_.emplace(key, entry).first;
  it->second.key = &it->first;

  touch_LRU(&it->second);

  if (!keep_in_buffer)
    write_back(it);

  return size;
}

template <class Key>
template <class T>
size_t ObjectArchive<Key>::load(Key const& key, T& obj, bool keep_in_buffer) {
  std::string s;
  size_t ret = load_raw(key, s, keep_in_buffer);
  if (ret == 0) return 0;
  deserialize(s, obj);
  return ret;
}

template <class Key>
size_t ObjectArchive<Key>::load_raw(Key const& key, std::string& data,
    bool keep_in_buffer) {
  if (!is_available(key))
    return 0;

  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto it = objects_.find(key);
  if (it == objects_.end())
    return 0;

  ObjectEntry& entry = it->second;

  size_t size = entry.size;
  if (size > max_buffer_size_)
    keep_in_buffer = false;

  // If the result isn't in the buffer, we must read it.
  if (entry.data.size() == 0) {
    // Only check for size if we have to load.
    if (size + buffer_size_ > max_buffer_size_ && keep_in_buffer)
      unload(max_buffer_size_ - size);

    stream_.seekg(entry.index_in_file);
    std::string& buf = entry.data;
    buf.resize(size);
    stream_.read(&buf[0], size);
    buffer_size_ += size;

    entry.modified = false;
  }

  touch_LRU(&entry);

  if (!keep_in_buffer) {
    if (!entry.modified)
      data.swap(entry.data);
    else
      data = entry.data;

    write_back(it);
  }
  else
    data = entry.data;

  return size;
}

template <class Key>
void ObjectArchive<Key>::unload(size_t desired_size) {
  OBJECT_ARCHIVE_MUTEX_GUARD;
  while (buffer_size_ > desired_size)
    write_back(*LRU_.back()->key);
}

template <class Key>
bool ObjectArchive<Key>::is_available(Key const& key) {
  OBJECT_ARCHIVE_MUTEX_GUARD;
  if (objects_.count(key) == 0)
    return false;
  return true;
}

template <class Key>
std::list<Key const*> ObjectArchive<Key>::available_objects() {
  std::list<Key const*> list;

  OBJECT_ARCHIVE_MUTEX_GUARD;
  for (auto& it : objects_)
    list.push_front(it.second.key);

  return list;
}

template <class Key>
void ObjectArchive<Key>::flush() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  internal_flush();
  init(filename_);
}

template <class Key>
void ObjectArchive<Key>::clear() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto key_list = available_objects();
  for (auto& it : key_list)
    remove(*it);

  flush();
}

template <class Key>
void ObjectArchive<Key>::internal_flush() {
  unload();

  if (!must_rebuild_file_)
    return;

  must_rebuild_file_ = false;

  boost::filesystem::path temp_filename;
  temp_filename = boost::filesystem::temp_directory_path();
  temp_filename += '/';
  temp_filename += boost::filesystem::unique_path();
  std::fstream temp_stream(temp_filename.string(),
      std::ios_base::in | std::ios_base::out |
      std::ios_base::binary | std::ios_base::trunc);

  size_t n_entries = objects_.size();
  temp_stream.write((char*)&n_entries, sizeof(size_t));

  size_t local_max_buffer_size = (max_buffer_size_ == 0 ? 1 : max_buffer_size_);

  char* temp_buffer = new char[local_max_buffer_size];

  for (auto& it : objects_) {
    ObjectEntry& entry = it.second;

    std::string key_str = serialize(it.first);

    size_t key_size = key_str.size();
    size_t data_size = entry.size;

    temp_stream.write((char*)&key_size, sizeof(size_t));
    temp_stream.write((char*)&data_size, sizeof(size_t));

    temp_stream.write((char*)&key_str[0], key_size);

    stream_.seekg(entry.index_in_file);
    size_t size = data_size;

    // Only uses the allowed buffer memory.
    for (;
         size > local_max_buffer_size;
         size -= local_max_buffer_size) {
      stream_.read(temp_buffer, local_max_buffer_size);
      temp_stream.write(temp_buffer, local_max_buffer_size);
    }
    stream_.read(temp_buffer, size);
    temp_stream.write(temp_buffer, size);
  }

  delete[] temp_buffer;

  stream_.close();
  temp_stream.close();

  boost::filesystem::remove(filename_);
  boost::filesystem::rename(temp_filename, filename_);
}

template <class Key>
bool ObjectArchive<Key>::write_back(Key const& key) {
  auto it = objects_.find(key);
  if (it == objects_.end())
    return false;

  return write_back(it);
}

template <class Key>
bool ObjectArchive<Key>::write_back(
    typename std::unordered_map<Key, ObjectEntry>::iterator const& it) {
  ObjectEntry& entry = it->second;

  if (entry.modified) {
    stream_.seekp(0, std::ios_base::end);
    entry.index_in_file = stream_.tellp();
    stream_.write((char*)&entry.data[0], entry.size);
    entry.modified = false;
    must_rebuild_file_ = true;
  }

  entry.data.clear();
  buffer_size_ -= entry.size;
  LRU_.remove(&entry);

  return true;
}

template <class Key>
void ObjectArchive<Key>::touch_LRU(ObjectEntry const* entry) {
  LRU_.remove(entry);
  LRU_.push_front(entry);
}

#endif
<commit_msg>Fixed another bug with virtual methods and added comment to remember.<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 Conrado Miranda

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// Each archive has a entries with the following format:
// 1) Number of entries (size_t);
// 2.1) Size of the key (size_t);
// 2.2) Size of the object (size_t);
// 2.3) Key as serialized by boost;
// 2.4) Object as serialized by boost.

#ifndef __OBJECT_ARCHIVE_IMPL_HPP__
#define __OBJECT_ARCHIVE_IMPL_HPP__

#include "object_archive.hpp"

#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/zlib.hpp>

#if ENABLE_THREADS
#define OBJECT_ARCHIVE_MUTEX_GUARD \
  boost::lock_guard<boost::recursive_mutex> __mutex_guard(mutex_)
#else
#define OBJECT_ARCHIVE_MUTEX_GUARD do { } while(0)
#endif

template <class Key>
ObjectArchive<Key>::ObjectArchive():
  must_rebuild_file_(false),
  max_buffer_size_(0),
  buffer_size_(0),
  temporary_file_(false) {
    init();
    set_buffer_size(0);
}

template <class Key>
ObjectArchive<Key>::~ObjectArchive() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  if (!temporary_file_)
    internal_flush();
  stream_.close();
  if (temporary_file_)
    boost::filesystem::remove(filename_);
}

template <class Key>
template <class T>
std::string ObjectArchive<Key>::serialize(T const& val) {
  std::stringstream stream;
  {
    boost::iostreams::filtering_stream<boost::iostreams::output> filtering;
    filtering.push(boost::iostreams::zlib_compressor());
    filtering.push(stream);
    boost::archive::binary_oarchive ofs(filtering);
    ofs << val;
  }

  return stream.str();
}

template <class Key>
template <class T1, class T2>
std::string ObjectArchive<Key>::serialize(T2 const& val) {
  std::stringstream stream;
  {
    boost::iostreams::filtering_stream<boost::iostreams::output> filtering;
    filtering.push(boost::iostreams::zlib_compressor());
    filtering.push(stream);
    boost::archive::binary_oarchive ofs(filtering);
    ofs.register_type<T1>();
    ofs << val;
  }

  return stream.str();
}

template <class Key>
template <class T>
void ObjectArchive<Key>::deserialize(std::string const& str, T& val) {
  std::stringstream stream(str);
  boost::iostreams::filtering_stream<boost::iostreams::input> filtering;
  filtering.push(boost::iostreams::zlib_decompressor());
  filtering.push(stream);
  boost::archive::binary_iarchive ifs(filtering);

  ifs >> val;
}

template <class Key>
void ObjectArchive<Key>::init() {
  std::string filename;
  filename = boost::filesystem::temp_directory_path().string();
  filename += '/';
  filename += boost::filesystem::unique_path().string();

  init(filename, true);
}

template <class Key>
void ObjectArchive<Key>::init(std::string const& filename,
    bool temporary_file) {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  internal_flush();

  stream_.close();
  if (temporary_file_)
    boost::filesystem::remove(filename_);

  filename_ = filename;
  temporary_file_ = temporary_file;

  buffer_size_ = 0;
  objects_.clear();
  LRU_.clear();

  stream_.open(filename, std::ios_base::in | std::ios_base::out |
                         std::ios_base::binary);
  stream_.seekg(0, std::ios_base::end);

  // If the file seems ok and has entries, use it. Otherwise, overwrite.
  if (stream_.good() && stream_.tellg() > 0) {
    stream_.seekg(0);

    size_t n_entries;
    stream_.read((char*)&n_entries, sizeof(size_t));

    for (size_t i = 0; i < n_entries; i++) {
      size_t key_size;
      size_t data_size;
      stream_.read((char*)&key_size, sizeof(size_t));
      stream_.read((char*)&data_size, sizeof(size_t));

      std::string key_string;
      key_string.resize(key_size);
      stream_.read(&key_string[0], key_size);

      Key key;
      deserialize(key_string, key);

      ObjectEntry entry;
      entry.index_in_file = stream_.tellg();
      entry.size = data_size;
      entry.modified = false;
      auto it = objects_.emplace(key, entry).first;
      it->second.key = &it->first;

      stream_.seekg(data_size, std::ios_base::cur);
    }
  }
  else {
    stream_.open(filename, std::ios_base::in | std::ios_base::out |
        std::ios_base::binary | std::ios_base::trunc);
  }
}

template <class Key>
void ObjectArchive<Key>::set_buffer_size(size_t max_buffer_size) {
  max_buffer_size_ = max_buffer_size;
  unload(max_buffer_size);
}

template <class Key>
void ObjectArchive<Key>::set_buffer_size(std::string const& max_buffer_size) {
  size_t length = max_buffer_size.size();
  double buffer_size = atof(max_buffer_size.c_str());

  bool changed = false;
  for (size_t i = 0; i< length && !changed; i++) {
    switch (max_buffer_size[i]) {
      case 'k':
      case 'K':
        buffer_size *= 1e3;
        changed = true;
        break;
      case 'm':
      case 'M':
        buffer_size *= 1e6;
        changed = true;
        break;
      case 'g':
      case 'G':
        buffer_size *= 1e9;
        changed = true;
        break;
    }
  }

  set_buffer_size(buffer_size);
}

#if BOOST_OS_LINUX
#include <sys/sysinfo.h>

template <class Key>
void ObjectArchive<Key>::set_buffer_size_scale(float max_buffer_size) {
  struct sysinfo info;
  if (sysinfo(&info) == 0) {
    unsigned long freeram = info.freeram;
    set_buffer_size(freeram * max_buffer_size);
  }
}
#endif

template <class Key>
size_t ObjectArchive<Key>::get_max_buffer_size() const {
  return max_buffer_size_;
}

template <class Key>
size_t ObjectArchive<Key>::get_buffer_size() const {
  return buffer_size_;
}

template <class Key>
void ObjectArchive<Key>::remove(Key const& key) {
  if (!is_available(key))
    return;

  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto it = objects_.find(key);
  if (it == objects_.end())
    return;

  ObjectEntry& entry = it->second;
  if (entry.data.size())
    buffer_size_ -= entry.size;
  objects_.erase(key);
  LRU_.remove(&entry);
  must_rebuild_file_ = true;
}

template <class Key>
template <class T>
size_t ObjectArchive<Key>::insert(Key const& key, T const& obj,
    bool keep_in_buffer) {
  return insert_raw(key, serialize(obj), keep_in_buffer);
}

template <class Key>
size_t ObjectArchive<Key>::insert_raw(Key const& key, std::string const& data,
    bool keep_in_buffer) {
  // Makes sure we call the local method, not its virtualization
  return ObjectArchive<Key>::insert_raw(key, std::string(data), keep_in_buffer);
}

template <class Key>
size_t ObjectArchive<Key>::insert_raw(Key const& key, std::string&& data,
    bool keep_in_buffer) {
  size_t size = data.size();
  if (size > max_buffer_size_)
    keep_in_buffer = false;

  // Makes sure we call the local method, not its virtualization
  ObjectArchive<Key>::remove(key);

  if (size + buffer_size_ > max_buffer_size_ && keep_in_buffer)
    unload(max_buffer_size_ - size);

  OBJECT_ARCHIVE_MUTEX_GUARD;

  buffer_size_ += size;

  ObjectEntry entry;
  entry.data.swap(data);
  entry.size = size;
  entry.modified = true;
  auto it = objects_.emplace(key, entry).first;
  it->second.key = &it->first;

  touch_LRU(&it->second);

  if (!keep_in_buffer)
    write_back(it);

  return size;
}

template <class Key>
template <class T>
size_t ObjectArchive<Key>::load(Key const& key, T& obj, bool keep_in_buffer) {
  std::string s;
  size_t ret = load_raw(key, s, keep_in_buffer);
  if (ret == 0) return 0;
  deserialize(s, obj);
  return ret;
}

template <class Key>
size_t ObjectArchive<Key>::load_raw(Key const& key, std::string& data,
    bool keep_in_buffer) {
  if (!is_available(key))
    return 0;

  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto it = objects_.find(key);
  if (it == objects_.end())
    return 0;

  ObjectEntry& entry = it->second;

  size_t size = entry.size;
  if (size > max_buffer_size_)
    keep_in_buffer = false;

  // If the result isn't in the buffer, we must read it.
  if (entry.data.size() == 0) {
    // Only check for size if we have to load.
    if (size + buffer_size_ > max_buffer_size_ && keep_in_buffer)
      unload(max_buffer_size_ - size);

    stream_.seekg(entry.index_in_file);
    std::string& buf = entry.data;
    buf.resize(size);
    stream_.read(&buf[0], size);
    buffer_size_ += size;

    entry.modified = false;
  }

  touch_LRU(&entry);

  if (!keep_in_buffer) {
    if (!entry.modified)
      data.swap(entry.data);
    else
      data = entry.data;

    write_back(it);
  }
  else
    data = entry.data;

  return size;
}

template <class Key>
void ObjectArchive<Key>::unload(size_t desired_size) {
  OBJECT_ARCHIVE_MUTEX_GUARD;
  while (buffer_size_ > desired_size)
    write_back(*LRU_.back()->key);
}

template <class Key>
bool ObjectArchive<Key>::is_available(Key const& key) {
  OBJECT_ARCHIVE_MUTEX_GUARD;
  if (objects_.count(key) == 0)
    return false;
  return true;
}

template <class Key>
std::list<Key const*> ObjectArchive<Key>::available_objects() {
  std::list<Key const*> list;

  OBJECT_ARCHIVE_MUTEX_GUARD;
  for (auto& it : objects_)
    list.push_front(it.second.key);

  return list;
}

template <class Key>
void ObjectArchive<Key>::flush() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  internal_flush();
  init(filename_);
}

template <class Key>
void ObjectArchive<Key>::clear() {
  OBJECT_ARCHIVE_MUTEX_GUARD;

  auto key_list = available_objects();
  for (auto& it : key_list)
    remove(*it);

  flush();
}

template <class Key>
void ObjectArchive<Key>::internal_flush() {
  unload();

  if (!must_rebuild_file_)
    return;

  must_rebuild_file_ = false;

  boost::filesystem::path temp_filename;
  temp_filename = boost::filesystem::temp_directory_path();
  temp_filename += '/';
  temp_filename += boost::filesystem::unique_path();
  std::fstream temp_stream(temp_filename.string(),
      std::ios_base::in | std::ios_base::out |
      std::ios_base::binary | std::ios_base::trunc);

  size_t n_entries = objects_.size();
  temp_stream.write((char*)&n_entries, sizeof(size_t));

  size_t local_max_buffer_size = (max_buffer_size_ == 0 ? 1 : max_buffer_size_);

  char* temp_buffer = new char[local_max_buffer_size];

  for (auto& it : objects_) {
    ObjectEntry& entry = it.second;

    std::string key_str = serialize(it.first);

    size_t key_size = key_str.size();
    size_t data_size = entry.size;

    temp_stream.write((char*)&key_size, sizeof(size_t));
    temp_stream.write((char*)&data_size, sizeof(size_t));

    temp_stream.write((char*)&key_str[0], key_size);

    stream_.seekg(entry.index_in_file);
    size_t size = data_size;

    // Only uses the allowed buffer memory.
    for (;
         size > local_max_buffer_size;
         size -= local_max_buffer_size) {
      stream_.read(temp_buffer, local_max_buffer_size);
      temp_stream.write(temp_buffer, local_max_buffer_size);
    }
    stream_.read(temp_buffer, size);
    temp_stream.write(temp_buffer, size);
  }

  delete[] temp_buffer;

  stream_.close();
  temp_stream.close();

  boost::filesystem::remove(filename_);
  boost::filesystem::rename(temp_filename, filename_);
}

template <class Key>
bool ObjectArchive<Key>::write_back(Key const& key) {
  auto it = objects_.find(key);
  if (it == objects_.end())
    return false;

  return write_back(it);
}

template <class Key>
bool ObjectArchive<Key>::write_back(
    typename std::unordered_map<Key, ObjectEntry>::iterator const& it) {
  ObjectEntry& entry = it->second;

  if (entry.modified) {
    stream_.seekp(0, std::ios_base::end);
    entry.index_in_file = stream_.tellp();
    stream_.write((char*)&entry.data[0], entry.size);
    entry.modified = false;
    must_rebuild_file_ = true;
  }

  entry.data.clear();
  buffer_size_ -= entry.size;
  LRU_.remove(&entry);

  return true;
}

template <class Key>
void ObjectArchive<Key>::touch_LRU(ObjectEntry const* entry) {
  LRU_.remove(entry);
  LRU_.push_front(entry);
}

#endif
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#pragma once

#include <memory>
#include <cstdlib>
#include <assert.h>
#include <type_traits>

namespace seastar {

/// \addtogroup memory-module
/// @{

/// Provides a mechanism for managing the lifetime of a buffer.
///
/// A \c deleter is an object that is used to inform the consumer
/// of some buffer (not referenced by the deleter itself) how to
/// delete the buffer.  This can be by calling an arbitrary function
/// or destroying an object carried by the deleter.  Examples of
/// a deleter's encapsulated actions are:
///
///  - calling \c std::free(p) on some captured pointer, p
///  - calling \c delete \c p on some captured pointer, p
///  - decrementing a reference count somewhere
///
/// A deleter performs its action from its destructor.
class deleter final {
public:
    /// \cond internal
    struct impl;
    struct raw_object_tag {};
    /// \endcond
private:
    // if bit 0 set, point to object to be freed directly.
    impl* _impl = nullptr;
public:
    /// Constructs an empty deleter that does nothing in its destructor.
    deleter() = default;
    deleter(const deleter&) = delete;
    /// Moves a deleter.
    deleter(deleter&& x) noexcept : _impl(x._impl) { x._impl = nullptr; }
    /// \cond internal
    explicit deleter(impl* i) : _impl(i) {}
    deleter(raw_object_tag tag, void* object)
        : _impl(from_raw_object(object)) {}
    /// \endcond
    /// Destroys the deleter and carries out the encapsulated action.
    ~deleter();
    deleter& operator=(deleter&& x) noexcept;
    deleter& operator=(deleter&) = delete;
    /// Performs a sharing operation.  The encapsulated action will only
    /// be carried out after both the original deleter and the returned
    /// deleter are both destroyed.
    ///
    /// \return a deleter with the same encapsulated action as this one.
    deleter share();
    /// Checks whether the deleter has an associated action.
    explicit operator bool() const { return bool(_impl); }
    /// \cond internal
    void reset(impl* i) {
        this->~deleter();
        new (this) deleter(i);
    }
    /// \endcond
    /// Appends another deleter to this deleter.  When this deleter is
    /// destroyed, both encapsulated actions will be carried out.
    void append(deleter d);
private:
    static bool is_raw_object(impl* i) {
        auto x = reinterpret_cast<uintptr_t>(i);
        return x & 1;
    }
    bool is_raw_object() const {
        return is_raw_object(_impl);
    }
    static void* to_raw_object(impl* i) {
        auto x = reinterpret_cast<uintptr_t>(i);
        return reinterpret_cast<void*>(x & ~uintptr_t(1));
    }
    void* to_raw_object() const {
        return to_raw_object(_impl);
    }
    impl* from_raw_object(void* object) {
        auto x = reinterpret_cast<uintptr_t>(object);
        return reinterpret_cast<impl*>(x | 1);
    }
};

/// \cond internal
struct deleter::impl {
    unsigned refs = 1;
    deleter next;
    impl(deleter next) : next(std::move(next)) {}
    virtual ~impl() {}
};
/// \endcond

inline
deleter::~deleter() {
    if (is_raw_object()) {
        std::free(to_raw_object());
        return;
    }
    if (_impl && --_impl->refs == 0) {
        delete _impl;
    }
}

inline
deleter& deleter::operator=(deleter&& x) noexcept {
    if (this != &x) {
        this->~deleter();
        new (this) deleter(std::move(x));
    }
    return *this;
}

/// \cond internal
template <typename Deleter>
struct lambda_deleter_impl final : deleter::impl {
    Deleter del;
    lambda_deleter_impl(deleter next, Deleter&& del)
        : impl(std::move(next)), del(std::move(del)) {}
    virtual ~lambda_deleter_impl() override { del(); }
};

template <typename Object>
struct object_deleter_impl final : deleter::impl {
    Object obj;
    object_deleter_impl(deleter next, Object&& obj)
        : impl(std::move(next)), obj(std::move(obj)) {}
};

template <typename Object>
inline
object_deleter_impl<Object>* make_object_deleter_impl(deleter next, Object obj) {
    return new object_deleter_impl<Object>(std::move(next), std::move(obj));
}
/// \endcond

/// Makes a \ref deleter that encapsulates the action of
/// destroying an object, as well as running another deleter.  The input
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
///
/// \param d deleter that will become part of the new deleter's encapsulated action
/// \param o object whose destructor becomes part of the new deleter's encapsulated action
/// \related deleter
template <typename Object>
deleter
make_deleter(deleter next, Object o) {
    return deleter(new lambda_deleter_impl<Object>(std::move(next), std::move(o)));
}

/// Makes a \ref deleter that encapsulates the action of destroying an object.  The input
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
///
/// \param o object whose destructor becomes the new deleter's encapsulated action
/// \related deleter
template <typename Object>
deleter
make_deleter(Object o) {
    return make_deleter(deleter(), std::move(o));
}

/// \cond internal
struct free_deleter_impl final : deleter::impl {
    void* obj;
    free_deleter_impl(void* obj) : impl(deleter()), obj(obj) {}
    virtual ~free_deleter_impl() override { std::free(obj); }
};
/// \endcond

inline
deleter
deleter::share() {
    if (!_impl) {
        return deleter();
    }
    if (is_raw_object()) {
        _impl = new free_deleter_impl(to_raw_object());
    }
    ++_impl->refs;
    return deleter(_impl);
}

// Appends 'd' to the chain of deleters. Avoids allocation if possible. For
// performance reasons the current chain should be shorter and 'd' should be
// longer.
inline
void deleter::append(deleter d) {
    if (!d._impl) {
        return;
    }
    impl* next_impl = _impl;
    deleter* next_d = this;
    while (next_impl) {
        if (next_impl == d._impl) {
            return; // Already appended
        }
        if (is_raw_object(next_impl)) {
            next_d->_impl = next_impl = new free_deleter_impl(to_raw_object(next_impl));
        }

        if (next_impl->refs != 1) {
            next_d->_impl = next_impl = make_object_deleter_impl(deleter(next_impl), std::move(d));
            return;
        }

        next_d = &next_impl->next;
        next_impl = next_d->_impl;
    }
    next_d->_impl = d._impl;
    d._impl = nullptr;
}

/// Makes a deleter that calls \c std::free() when it is destroyed.
///
/// \param obj object to free.
/// \related deleter
inline
deleter
make_free_deleter(void* obj) {
    if (!obj) {
        return deleter();
    }
    return deleter(deleter::raw_object_tag(), obj);
}

/// Makes a deleter that calls \c std::free() when it is destroyed, as well
/// as invoking the encapsulated action of another deleter.
///
/// \param d deleter to invoke.
/// \param obj object to free.
/// \related deleter
inline
deleter
make_free_deleter(deleter next, void* obj) {
    return make_deleter(std::move(next), [obj] () mutable { std::free(obj); });
}

/// \see make_deleter(Object)
/// \related deleter
template <typename T>
inline
deleter
make_object_deleter(T&& obj) {
    return deleter{make_object_deleter_impl(deleter(), std::move(obj))};
}

/// \see make_deleter(deleter, Object)
/// \related deleter
template <typename T>
inline
deleter
make_object_deleter(deleter d, T&& obj) {
    return deleter{make_object_deleter_impl(std::move(d), std::move(obj))};
}

/// @}

}
<commit_msg>deleter: mark non-throwing methods as noexcept<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  You may not use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
/*
 * Copyright (C) 2014 Cloudius Systems, Ltd.
 */

#pragma once

#include <memory>
#include <cstdlib>
#include <assert.h>
#include <type_traits>

namespace seastar {

/// \addtogroup memory-module
/// @{

/// Provides a mechanism for managing the lifetime of a buffer.
///
/// A \c deleter is an object that is used to inform the consumer
/// of some buffer (not referenced by the deleter itself) how to
/// delete the buffer.  This can be by calling an arbitrary function
/// or destroying an object carried by the deleter.  Examples of
/// a deleter's encapsulated actions are:
///
///  - calling \c std::free(p) on some captured pointer, p
///  - calling \c delete \c p on some captured pointer, p
///  - decrementing a reference count somewhere
///
/// A deleter performs its action from its destructor.
class deleter final {
public:
    /// \cond internal
    struct impl;
    struct raw_object_tag {};
    /// \endcond
private:
    // if bit 0 set, point to object to be freed directly.
    impl* _impl = nullptr;
public:
    /// Constructs an empty deleter that does nothing in its destructor.
    deleter() = default;
    deleter(const deleter&) = delete;
    /// Moves a deleter.
    deleter(deleter&& x) noexcept : _impl(x._impl) { x._impl = nullptr; }
    /// \cond internal
    explicit deleter(impl* i) : _impl(i) {}
    deleter(raw_object_tag tag, void* object)
        : _impl(from_raw_object(object)) {}
    /// \endcond
    /// Destroys the deleter and carries out the encapsulated action.
    ~deleter();
    deleter& operator=(deleter&& x) noexcept;
    deleter& operator=(deleter&) = delete;
    /// Performs a sharing operation.  The encapsulated action will only
    /// be carried out after both the original deleter and the returned
    /// deleter are both destroyed.
    ///
    /// \return a deleter with the same encapsulated action as this one.
    deleter share();
    /// Checks whether the deleter has an associated action.
    explicit operator bool() const noexcept { return bool(_impl); }
    /// \cond internal
    void reset(impl* i) {
        this->~deleter();
        new (this) deleter(i);
    }
    /// \endcond
    /// Appends another deleter to this deleter.  When this deleter is
    /// destroyed, both encapsulated actions will be carried out.
    void append(deleter d);
private:
    static bool is_raw_object(impl* i) noexcept {
        auto x = reinterpret_cast<uintptr_t>(i);
        return x & 1;
    }
    bool is_raw_object() const noexcept {
        return is_raw_object(_impl);
    }
    static void* to_raw_object(impl* i) noexcept {
        auto x = reinterpret_cast<uintptr_t>(i);
        return reinterpret_cast<void*>(x & ~uintptr_t(1));
    }
    void* to_raw_object() const noexcept {
        return to_raw_object(_impl);
    }
    impl* from_raw_object(void* object) noexcept {
        auto x = reinterpret_cast<uintptr_t>(object);
        return reinterpret_cast<impl*>(x | 1);
    }
};

/// \cond internal
struct deleter::impl {
    unsigned refs = 1;
    deleter next;
    impl(deleter next) : next(std::move(next)) {}
    virtual ~impl() {}
};
/// \endcond

inline
deleter::~deleter() {
    if (is_raw_object()) {
        std::free(to_raw_object());
        return;
    }
    if (_impl && --_impl->refs == 0) {
        delete _impl;
    }
}

inline
deleter& deleter::operator=(deleter&& x) noexcept {
    if (this != &x) {
        this->~deleter();
        new (this) deleter(std::move(x));
    }
    return *this;
}

/// \cond internal
template <typename Deleter>
struct lambda_deleter_impl final : deleter::impl {
    Deleter del;
    lambda_deleter_impl(deleter next, Deleter&& del)
        : impl(std::move(next)), del(std::move(del)) {}
    virtual ~lambda_deleter_impl() override { del(); }
};

template <typename Object>
struct object_deleter_impl final : deleter::impl {
    Object obj;
    object_deleter_impl(deleter next, Object&& obj)
        : impl(std::move(next)), obj(std::move(obj)) {}
};

template <typename Object>
inline
object_deleter_impl<Object>* make_object_deleter_impl(deleter next, Object obj) {
    return new object_deleter_impl<Object>(std::move(next), std::move(obj));
}
/// \endcond

/// Makes a \ref deleter that encapsulates the action of
/// destroying an object, as well as running another deleter.  The input
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
///
/// \param d deleter that will become part of the new deleter's encapsulated action
/// \param o object whose destructor becomes part of the new deleter's encapsulated action
/// \related deleter
template <typename Object>
deleter
make_deleter(deleter next, Object o) {
    return deleter(new lambda_deleter_impl<Object>(std::move(next), std::move(o)));
}

/// Makes a \ref deleter that encapsulates the action of destroying an object.  The input
/// object is moved to the deleter, and destroyed when the deleter is destroyed.
///
/// \param o object whose destructor becomes the new deleter's encapsulated action
/// \related deleter
template <typename Object>
deleter
make_deleter(Object o) {
    return make_deleter(deleter(), std::move(o));
}

/// \cond internal
struct free_deleter_impl final : deleter::impl {
    void* obj;
    free_deleter_impl(void* obj) : impl(deleter()), obj(obj) {}
    virtual ~free_deleter_impl() override { std::free(obj); }
};
/// \endcond

inline
deleter
deleter::share() {
    if (!_impl) {
        return deleter();
    }
    if (is_raw_object()) {
        _impl = new free_deleter_impl(to_raw_object());
    }
    ++_impl->refs;
    return deleter(_impl);
}

// Appends 'd' to the chain of deleters. Avoids allocation if possible. For
// performance reasons the current chain should be shorter and 'd' should be
// longer.
inline
void deleter::append(deleter d) {
    if (!d._impl) {
        return;
    }
    impl* next_impl = _impl;
    deleter* next_d = this;
    while (next_impl) {
        if (next_impl == d._impl) {
            return; // Already appended
        }
        if (is_raw_object(next_impl)) {
            next_d->_impl = next_impl = new free_deleter_impl(to_raw_object(next_impl));
        }

        if (next_impl->refs != 1) {
            next_d->_impl = next_impl = make_object_deleter_impl(deleter(next_impl), std::move(d));
            return;
        }

        next_d = &next_impl->next;
        next_impl = next_d->_impl;
    }
    next_d->_impl = d._impl;
    d._impl = nullptr;
}

/// Makes a deleter that calls \c std::free() when it is destroyed.
///
/// \param obj object to free.
/// \related deleter
inline
deleter
make_free_deleter(void* obj) {
    if (!obj) {
        return deleter();
    }
    return deleter(deleter::raw_object_tag(), obj);
}

/// Makes a deleter that calls \c std::free() when it is destroyed, as well
/// as invoking the encapsulated action of another deleter.
///
/// \param d deleter to invoke.
/// \param obj object to free.
/// \related deleter
inline
deleter
make_free_deleter(deleter next, void* obj) {
    return make_deleter(std::move(next), [obj] () mutable { std::free(obj); });
}

/// \see make_deleter(Object)
/// \related deleter
template <typename T>
inline
deleter
make_object_deleter(T&& obj) {
    return deleter{make_object_deleter_impl(deleter(), std::move(obj))};
}

/// \see make_deleter(deleter, Object)
/// \related deleter
template <typename T>
inline
deleter
make_object_deleter(deleter d, T&& obj) {
    return deleter{make_object_deleter_impl(std::move(d), std::move(obj))};
}

/// @}

}
<|endoftext|>