<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.
 *
 ************************************************************************/

#include <stdexcept>

#if UNIT_TEST
#include <iostream>
using std::cout;
using std::endl;
#endif

namespace mdds { namespace __gridmap {

template<typename _Trait>
column<_Trait>::block::block() : m_size(0), mp_data(NULL) {}

template<typename _Trait>
column<_Trait>::block::block(row_key_type _size) : m_size(_size), mp_data(NULL) {}

template<typename _Trait>
column<_Trait>::block::~block()
{
    cell_block_modifier::delete_block(mp_data);
}

template<typename _Trait>
column<_Trait>::column(row_key_type max_row_size) : m_max_row_size(max_row_size)
{
    // Initialize with an empty block that spans from 0 to max.
    m_blocks.push_back(new block(max_row_size));
}

template<typename _Trait>
column<_Trait>::~column()
{
    std::for_each(m_blocks.begin(), m_blocks.end(), default_deleter<block>());
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::set_cell(row_key_type row, const _T& cell)
{
    if (row < 0 || row >= m_max_row_size)
        throw std::out_of_range("Specified row index is out-of-bound.");

    cell_category_type cat = get_type(cell);

    // Find the right block ID from the row ID.
    row_key_type start_row = 0; // row ID of the first cell in a block.
    size_t block_index = 0;
    for (size_t i = 0, n = m_blocks.size(); i < n; ++i)
    {
        const block& blk = *m_blocks[i];
        if (row < start_row + blk.m_size)
        {
            // Row is in this block.
            block_index = i;
            break;
        }

        // Specified row is not in this block.
        start_row += blk.m_size;
    }

    block* blk = m_blocks[block_index];
    assert(blk->m_size > 0); // block size should never be zero at any time.

    row_key_type pos_in_block = row - start_row;
    assert(pos_in_block < blk->m_size);

    if (!blk->mp_data)
    {
        // This is an empty block.
        set_cell_to_empty_block(block_index, pos_in_block, cell);
        return;
    }

    assert(blk->mp_data);
    cell_category_type block_cat = get_block_type(*blk->mp_data);

    if (block_cat == cat)
    {
        // This block is of the same type as the cell being inserted.
        row_key_type i = row - start_row;
        cell_block_modifier::set_value(blk->mp_data, i, cell);
    }
    else if (row == start_row)
    {
        // Insertion point is at the start of the block.
    }
    else if (row == (start_row + blk->m_size - 1))
    {
        // Insertion point is at the end of the block.
    }
    else
    {
        // Insertion point is somewhere in the middle of the block.
    }
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::create_new_block_with_new_cell(cell_block_type*& data, const _T& cell)
{
    cell_category_type cat = get_type(cell);

    // New cell block is always size 1.
    data = cell_block_modifier::create_new_block(cat);
    if (!data)
        throw general_error("Failed to create new block.");

    cell_block_modifier::set_value(data, 0, cell);
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::insert_cell_to_middle_of_empty_block(
    size_t block_index, row_key_type pos_in_block, const _T& cell)
{
    block* blk = m_blocks[block_index];

    assert(pos_in_block > 0 && pos_in_block < blk->m_size - 1);
    assert(blk->m_size >= 3);
    row_key_type orig_size = blk->m_size;
    blk->m_size = pos_in_block;

    typename blocks_type::iterator it = m_blocks.begin();
    std::advance(it, block_index+1);
    m_blocks.insert(it, new block(1));
    it = m_blocks.begin();
    std::advance(it, block_index+1);
    blk = *it;
    ++it;
    m_blocks.insert(it, new block(orig_size-pos_in_block-1));

    create_new_block_with_new_cell(blk->mp_data, cell);
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::set_cell_to_empty_block(
    size_t block_index, row_key_type pos_in_block, const _T& cell)
{
    block* blk = m_blocks[block_index];

    if (block_index == 0)
    {
        // Topmost block.
        if (m_blocks.size() == 1)
        {
            // this is the only block.
            assert(blk->m_size == m_max_row_size);
            if (m_max_row_size == 1)
            {
                // This column is allowed to have only one row!
                assert(pos_in_block == 0);
                create_new_block_with_new_cell(blk->mp_data, cell);
            }
            else
            {
                // block has multiple rows.
                if (pos_in_block == 0)
                {
                    // Insert into the first cell in block.
                    blk->m_size -= 1;
                    assert(blk->m_size > 0);

                    m_blocks.insert(m_blocks.begin(), new block(1));
                    blk = m_blocks[block_index];
                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else if (pos_in_block == blk->m_size - 1)
                {
                    // Insert into the last cell in block.
                    blk->m_size -= 1;
                    assert(blk->m_size > 0);

                    m_blocks.push_back(new block(1));
                    blk = m_blocks.back();

                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else
                {
                    // Insert into the middle of the block.
                    insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
                }
            }
        }
        else
        {
            // this empty block is followed by a non-empty block.
            assert(block_index < m_blocks.size()-1);
            if (pos_in_block == 0)
            {
                if (blk->m_size == 1)
                {
                    // Top empty block with only one cell size.
                    block* blk_next = m_blocks[block_index+1];
                    assert(blk_next->mp_data);
                    cell_category_type cat = get_type(cell);
                    cell_category_type cat_next = get_block_type(*blk_next->mp_data);

                    if (cat == cat_next)
                    {
                        // Remove this one-cell empty block from the top, and
                        // prepend the cell to the next block.
                        m_blocks.erase(m_blocks.begin());
                        blk = m_blocks.front();
                        blk->m_size += 1;
                        cell_block_modifier::prepend_value(blk->mp_data, cell);
                    }
                    else
                        create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else
                {
                    assert(blk->m_size > 1);
                    blk->m_size -= 1;
                    m_blocks.insert(m_blocks.begin(), new block(1));
                    blk = m_blocks.front();
                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
            }
            else if (pos_in_block == blk->m_size - 1)
            {
                // Immediately above a non-empty block.
                block* blk_next = m_blocks[block_index+1];
                assert(blk_next->mp_data);
                cell_category_type cat = get_type(cell);
                cell_category_type cat_next = get_block_type(*blk_next->mp_data);
                assert(blk->m_size > 1);

                if (cat == cat_next)
                {
                    // Shrink this empty block by one, and prepend the cell to the next block.
                    blk->m_size -= 1;
                    blk_next->m_size += 1;
                    cell_block_modifier::prepend_value(blk_next->mp_data, cell);
                }
                else
                {
                    assert(!"not implemented yet.");
                }
            }
            else
            {
                // Inserting into the middle of an empty block.
                insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
            }
        }

        return;
    }

    // This empty block is right below a non-empty block.
    assert(block_index > 0 && m_blocks[block_index-1]->mp_data != NULL);

    if (pos_in_block == 0)
    {
        // New cell is right below the non-empty block.
        cell_category_type blk_cat_prev = get_block_type(*m_blocks[block_index-1]->mp_data);
        cell_category_type cat = get_type(cell);
        if (blk_cat_prev == cat)
        {
            // Extend the previous block by one to insert this cell.
            if (blk->m_size == 1)
            {
                // Check if we need to merge with the following block.
                if (block_index == m_blocks.size()-1)
                {
                    // Last block.  Delete this block and extend the previous
                    // block by one.
                    delete m_blocks[block_index];
                    m_blocks.pop_back();
                    blk = m_blocks.back();
                    blk->m_size += 1;
                    cell_block_modifier::append_value(blk->mp_data, cell);
                }
                else
                {
                    block* blk_next = m_blocks[block_index+1];
                    cell_block_type* data_next = blk_next->mp_data;
                    assert(data_next); // Empty block must not be followed by another empty block.
                    cell_category_type blk_cat_next = get_block_type(*data_next);
                    if (blk_cat_prev == blk_cat_next)
                    {
                        // We need to merge the previous and next blocks.
                        blk = m_blocks[block_index-1];
                        blk->m_size += 1 + blk_next->m_size;
                        cell_block_modifier::append_value(blk->mp_data, cell);
                        cell_block_modifier::append_value(blk->mp_data, data_next);
                        delete m_blocks.back();
                        m_blocks.pop_back();
                    }
                    else
                    {
                        // Ignore the next block. Just extend the previous block.
                        assert(!"not implemented yet.");
                    }
                }
            }
            else
            {
                // Extend the previous block to append the cell.
                assert(blk->m_size > 1);
                block* blk_prev = m_blocks[block_index-1];
                blk_prev->m_size += 1;
                blk->m_size -= 1;
                cell_block_modifier::append_value(blk_prev->mp_data, cell);
            }
        }
        else
        {
            // cell type is different from the type of the previous block.
            assert(!"not implemented yet.");
        }
    }
    else if (pos_in_block == blk->m_size - 1)
    {
        // New cell is at the last cell position.
        assert(blk->m_size > 1);
        if (block_index == m_blocks.size()-1)
        {
            // This is the last block.
            blk->m_size -= 1;
            m_blocks.push_back(new block(1));
            blk = m_blocks.back();
            create_new_block_with_new_cell(blk->mp_data, cell);
        }
        else
        {
            // A non-empty block exists below.
            cell_category_type cat = get_type(cell);
            block* blk_next = m_blocks[block_index+1];
            assert(blk_next->mp_data);
            cell_category_type blk_cat_next = get_block_type(*blk_next->mp_data);
            if (cat == blk_cat_next)
            {
                // Shrink this empty block and extend the next block.
                blk->m_size -= 1;
                blk_next->m_size += 1;
                cell_block_modifier::prepend_value(blk_next->mp_data, cell);
            }
            else
            {
                // Just insert this new cell.
                assert(!"not implemented yet");
            }
        }
    }
    else
    {
        // New cell is somewhere in the middle of an empty block.
        insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
    }
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::get_cell(row_key_type row, _T& cell) const
{
    if (row >= m_max_row_size)
        throw std::out_of_range("Specified row index is out-of-bound.");

    row_key_type start_row = 0;
    const block* blk = NULL;
    for (size_t i = 0, n = m_blocks.size(); i < n; ++i)
    {
        blk = m_blocks[i];
        assert(blk->m_size > 0);
        if (row < start_row + blk->m_size)
            break;

        // Specified row is not in this block.
        start_row += blk->m_size;
    }

    assert(blk);

    if (!blk->mp_data)
    {
        // empty cell block.
        cell_block_modifier::get_empty_value(cell);
        return;
    }

    assert(row >= start_row);
    assert(blk->mp_data); // data for non-empty blocks should never be NULL.
    row_key_type idx = row - start_row;
    cell_block_modifier::get_value(blk->mp_data, idx, cell);
}

}}
<commit_msg>Fixed memory leak, thanks to valgrind.<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.
 *
 ************************************************************************/

#include <stdexcept>

#if UNIT_TEST
#include <iostream>
using std::cout;
using std::endl;
#endif

namespace mdds { namespace __gridmap {

template<typename _Trait>
column<_Trait>::block::block() : m_size(0), mp_data(NULL) {}

template<typename _Trait>
column<_Trait>::block::block(row_key_type _size) : m_size(_size), mp_data(NULL) {}

template<typename _Trait>
column<_Trait>::block::~block()
{
    cell_block_modifier::delete_block(mp_data);
}

template<typename _Trait>
column<_Trait>::column(row_key_type max_row_size) : m_max_row_size(max_row_size)
{
    // Initialize with an empty block that spans from 0 to max.
    m_blocks.push_back(new block(max_row_size));
}

template<typename _Trait>
column<_Trait>::~column()
{
    std::for_each(m_blocks.begin(), m_blocks.end(), default_deleter<block>());
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::set_cell(row_key_type row, const _T& cell)
{
    if (row < 0 || row >= m_max_row_size)
        throw std::out_of_range("Specified row index is out-of-bound.");

    cell_category_type cat = get_type(cell);

    // Find the right block ID from the row ID.
    row_key_type start_row = 0; // row ID of the first cell in a block.
    size_t block_index = 0;
    for (size_t i = 0, n = m_blocks.size(); i < n; ++i)
    {
        const block& blk = *m_blocks[i];
        if (row < start_row + blk.m_size)
        {
            // Row is in this block.
            block_index = i;
            break;
        }

        // Specified row is not in this block.
        start_row += blk.m_size;
    }

    block* blk = m_blocks[block_index];
    assert(blk->m_size > 0); // block size should never be zero at any time.

    row_key_type pos_in_block = row - start_row;
    assert(pos_in_block < blk->m_size);

    if (!blk->mp_data)
    {
        // This is an empty block.
        set_cell_to_empty_block(block_index, pos_in_block, cell);
        return;
    }

    assert(blk->mp_data);
    cell_category_type block_cat = get_block_type(*blk->mp_data);

    if (block_cat == cat)
    {
        // This block is of the same type as the cell being inserted.
        row_key_type i = row - start_row;
        cell_block_modifier::set_value(blk->mp_data, i, cell);
    }
    else if (row == start_row)
    {
        // Insertion point is at the start of the block.
    }
    else if (row == (start_row + blk->m_size - 1))
    {
        // Insertion point is at the end of the block.
    }
    else
    {
        // Insertion point is somewhere in the middle of the block.
    }
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::create_new_block_with_new_cell(cell_block_type*& data, const _T& cell)
{
    cell_category_type cat = get_type(cell);

    // New cell block is always size 1.
    data = cell_block_modifier::create_new_block(cat);
    if (!data)
        throw general_error("Failed to create new block.");

    cell_block_modifier::set_value(data, 0, cell);
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::insert_cell_to_middle_of_empty_block(
    size_t block_index, row_key_type pos_in_block, const _T& cell)
{
    block* blk = m_blocks[block_index];

    assert(pos_in_block > 0 && pos_in_block < blk->m_size - 1);
    assert(blk->m_size >= 3);
    row_key_type orig_size = blk->m_size;
    blk->m_size = pos_in_block;

    typename blocks_type::iterator it = m_blocks.begin();
    std::advance(it, block_index+1);
    m_blocks.insert(it, new block(1));
    it = m_blocks.begin();
    std::advance(it, block_index+1);
    blk = *it;
    ++it;
    m_blocks.insert(it, new block(orig_size-pos_in_block-1));

    create_new_block_with_new_cell(blk->mp_data, cell);
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::set_cell_to_empty_block(
    size_t block_index, row_key_type pos_in_block, const _T& cell)
{
    block* blk = m_blocks[block_index];

    if (block_index == 0)
    {
        // Topmost block.
        if (m_blocks.size() == 1)
        {
            // this is the only block.
            assert(blk->m_size == m_max_row_size);
            if (m_max_row_size == 1)
            {
                // This column is allowed to have only one row!
                assert(pos_in_block == 0);
                create_new_block_with_new_cell(blk->mp_data, cell);
            }
            else
            {
                // block has multiple rows.
                if (pos_in_block == 0)
                {
                    // Insert into the first cell in block.
                    blk->m_size -= 1;
                    assert(blk->m_size > 0);

                    m_blocks.insert(m_blocks.begin(), new block(1));
                    blk = m_blocks[block_index];
                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else if (pos_in_block == blk->m_size - 1)
                {
                    // Insert into the last cell in block.
                    blk->m_size -= 1;
                    assert(blk->m_size > 0);

                    m_blocks.push_back(new block(1));
                    blk = m_blocks.back();

                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else
                {
                    // Insert into the middle of the block.
                    insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
                }
            }
        }
        else
        {
            // this empty block is followed by a non-empty block.
            assert(block_index < m_blocks.size()-1);
            if (pos_in_block == 0)
            {
                if (blk->m_size == 1)
                {
                    // Top empty block with only one cell size.
                    block* blk_next = m_blocks[block_index+1];
                    assert(blk_next->mp_data);
                    cell_category_type cat = get_type(cell);
                    cell_category_type cat_next = get_block_type(*blk_next->mp_data);

                    if (cat == cat_next)
                    {
                        // Remove this one-cell empty block from the top, and
                        // prepend the cell to the next block.
                        delete m_blocks.front();
                        m_blocks.erase(m_blocks.begin());
                        blk = m_blocks.front();
                        blk->m_size += 1;
                        cell_block_modifier::prepend_value(blk->mp_data, cell);
                    }
                    else
                        create_new_block_with_new_cell(blk->mp_data, cell);
                }
                else
                {
                    assert(blk->m_size > 1);
                    blk->m_size -= 1;
                    m_blocks.insert(m_blocks.begin(), new block(1));
                    blk = m_blocks.front();
                    create_new_block_with_new_cell(blk->mp_data, cell);
                }
            }
            else if (pos_in_block == blk->m_size - 1)
            {
                // Immediately above a non-empty block.
                block* blk_next = m_blocks[block_index+1];
                assert(blk_next->mp_data);
                cell_category_type cat = get_type(cell);
                cell_category_type cat_next = get_block_type(*blk_next->mp_data);
                assert(blk->m_size > 1);

                if (cat == cat_next)
                {
                    // Shrink this empty block by one, and prepend the cell to the next block.
                    blk->m_size -= 1;
                    blk_next->m_size += 1;
                    cell_block_modifier::prepend_value(blk_next->mp_data, cell);
                }
                else
                {
                    assert(!"not implemented yet.");
                }
            }
            else
            {
                // Inserting into the middle of an empty block.
                insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
            }
        }

        return;
    }

    // This empty block is right below a non-empty block.
    assert(block_index > 0 && m_blocks[block_index-1]->mp_data != NULL);

    if (pos_in_block == 0)
    {
        // New cell is right below the non-empty block.
        cell_category_type blk_cat_prev = get_block_type(*m_blocks[block_index-1]->mp_data);
        cell_category_type cat = get_type(cell);
        if (blk_cat_prev == cat)
        {
            // Extend the previous block by one to insert this cell.
            if (blk->m_size == 1)
            {
                // Check if we need to merge with the following block.
                if (block_index == m_blocks.size()-1)
                {
                    // Last block.  Delete this block and extend the previous
                    // block by one.
                    delete m_blocks[block_index];
                    m_blocks.pop_back();
                    blk = m_blocks.back();
                    blk->m_size += 1;
                    cell_block_modifier::append_value(blk->mp_data, cell);
                }
                else
                {
                    block* blk_next = m_blocks[block_index+1];
                    cell_block_type* data_next = blk_next->mp_data;
                    assert(data_next); // Empty block must not be followed by another empty block.
                    cell_category_type blk_cat_next = get_block_type(*data_next);
                    if (blk_cat_prev == blk_cat_next)
                    {
                        // We need to merge the previous and next blocks.
                        blk = m_blocks[block_index-1];
                        blk->m_size += 1 + blk_next->m_size;
                        cell_block_modifier::append_value(blk->mp_data, cell);
                        cell_block_modifier::append_value(blk->mp_data, data_next);
                        delete m_blocks.back();
                        m_blocks.pop_back();
                    }
                    else
                    {
                        // Ignore the next block. Just extend the previous block.
                        assert(!"not implemented yet.");
                    }
                }
            }
            else
            {
                // Extend the previous block to append the cell.
                assert(blk->m_size > 1);
                block* blk_prev = m_blocks[block_index-1];
                blk_prev->m_size += 1;
                blk->m_size -= 1;
                cell_block_modifier::append_value(blk_prev->mp_data, cell);
            }
        }
        else
        {
            // cell type is different from the type of the previous block.
            assert(!"not implemented yet.");
        }
    }
    else if (pos_in_block == blk->m_size - 1)
    {
        // New cell is at the last cell position.
        assert(blk->m_size > 1);
        if (block_index == m_blocks.size()-1)
        {
            // This is the last block.
            blk->m_size -= 1;
            m_blocks.push_back(new block(1));
            blk = m_blocks.back();
            create_new_block_with_new_cell(blk->mp_data, cell);
        }
        else
        {
            // A non-empty block exists below.
            cell_category_type cat = get_type(cell);
            block* blk_next = m_blocks[block_index+1];
            assert(blk_next->mp_data);
            cell_category_type blk_cat_next = get_block_type(*blk_next->mp_data);
            if (cat == blk_cat_next)
            {
                // Shrink this empty block and extend the next block.
                blk->m_size -= 1;
                blk_next->m_size += 1;
                cell_block_modifier::prepend_value(blk_next->mp_data, cell);
            }
            else
            {
                // Just insert this new cell.
                assert(!"not implemented yet");
            }
        }
    }
    else
    {
        // New cell is somewhere in the middle of an empty block.
        insert_cell_to_middle_of_empty_block(block_index, pos_in_block, cell);
    }
}

template<typename _Trait>
template<typename _T>
void column<_Trait>::get_cell(row_key_type row, _T& cell) const
{
    if (row >= m_max_row_size)
        throw std::out_of_range("Specified row index is out-of-bound.");

    row_key_type start_row = 0;
    const block* blk = NULL;
    for (size_t i = 0, n = m_blocks.size(); i < n; ++i)
    {
        blk = m_blocks[i];
        assert(blk->m_size > 0);
        if (row < start_row + blk->m_size)
            break;

        // Specified row is not in this block.
        start_row += blk->m_size;
    }

    assert(blk);

    if (!blk->mp_data)
    {
        // empty cell block.
        cell_block_modifier::get_empty_value(cell);
        return;
    }

    assert(row >= start_row);
    assert(blk->mp_data); // data for non-empty blocks should never be NULL.
    row_key_type idx = row - start_row;
    cell_block_modifier::get_value(blk->mp_data, idx, cell);
}

}}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2018, NetApp, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <arpa/inet.h>
#include <cstdint>
#include <fcntl.h>
#include <libgen.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <benchmark/benchmark.h>
#include <quant/quant.h>
#include <warpcore/warpcore.h>

static struct w_engine * w;
static struct q_conn *cc, *sc;

static inline uint64_t io(const uint64_t len)
{
    // reserve a new stream
    struct q_stream * const cs = q_rsv_stream(cc, true);
    if (unlikely(cs == nullptr))
        return 0;

    // allocate buffers to transmit a packet
    struct w_iov_sq o = w_iov_sq_initializer(o);
    q_alloc(w, &o, len);

    // send the data
    q_write(cs, &o, true);

    // read the data
    struct w_iov_sq i = w_iov_sq_initializer(i);
    struct q_stream * const ss = q_read(sc, &i, true);
    if (likely(ss)) {
        q_readall_stream(ss, &i);
        q_close_stream(ss);
    }
    q_close_stream(cs);

    const uint64_t ilen = w_iov_sq_len(&i);
    q_free(&i);
    q_free(&o);

    return ilen;
}

static void BM_conn(benchmark::State & state)
{
    const auto len = uint64_t(state.range(0));
    for (auto _ : state) {
        const uint64_t ilen = io(len);
        if (ilen != len) {
            state.SkipWithError("error");
            return;
        }
    }
    state.SetBytesProcessed(int64_t(state.iterations() * len)); // NOLINT
}

BENCHMARK(BM_conn)->RangeMultiplier(2)->Range(1024, 1024 * 1024 * 2)
    // ->Unit(benchmark::kMillisecond)
    ;

// BENCHMARK_MAIN()

int main(int argc __attribute__((unused)), char ** argv)
{
#ifndef NDEBUG
    util_dlevel = DBG; // default to maximum compiled-in verbosity
#endif

    // init
    const int cwd = open(".", O_CLOEXEC);
    ensure(cwd != -1, "cannot open");
    ensure(chdir(dirname(argv[0])) == 0, "cannot chdir");
    const struct q_conf conf = {nullptr, "dummy.crt", "dummy.key"};
    w = q_init("lo"
#ifndef __linux__
               "0"
#endif
               ,
               &conf);
    ensure(fchdir(cwd) == 0, "cannot fchdir");

    // bind server socket
    q_bind(w, 55555);

    // connect to server
    struct sockaddr_in sip = {};
    sip.sin_family = AF_INET;
    sip.sin_port = htons(55555);
    sip.sin_addr.s_addr = inet_addr("127.0.0.1");
    cc = q_connect(w, reinterpret_cast<struct sockaddr *>(&sip), // NOLINT
                   "localhost", nullptr, nullptr, true, nullptr);
    ensure(cc, "is zero");

    // accept connection
    sc = q_accept(nullptr);
    ensure(sc, "is zero");

    benchmark::RunSpecifiedBenchmarks();

    // close connections
    q_close(cc, 0, nullptr);
    q_close(sc, 0, nullptr);
    q_cleanup(w);
}
<commit_msg>Log some conn stats<commit_after>// Copyright (c) 2014-2018, NetApp, Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <arpa/inet.h>
#include <cstdint>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <libgen.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <benchmark/benchmark.h>
#include <quant/quant.h>
#include <warpcore/warpcore.h>

static struct w_engine * w;
static struct q_conn *cc, *sc;

static void log(const struct q_conn_info * const cci,
                const struct q_conn_info * const sci)
{
    std::cout << std::fixed << std::setprecision(4)
              << "C: i=" << cci->pkts_in_valid << " o=" << cci->pkts_out
              << " ol=" << cci->pkts_out_lost << " pto=" << cci->pto_cnt
              << " cwnd=" << cci->cwnd << "\t"
              << "S: i=" << sci->pkts_in_valid << " o=" << sci->pkts_out
              << " ol=" << sci->pkts_out_lost << " pto=" << sci->pto_cnt
              << " cwnd=" << sci->cwnd << std::endl;
}

static inline uint64_t io(const uint64_t len)
{
    // reserve a new stream
    struct q_stream * const cs = q_rsv_stream(cc, true);
    if (unlikely(cs == nullptr))
        return 0;

    // allocate buffers to transmit a packet
    struct w_iov_sq o = w_iov_sq_initializer(o);
    q_alloc(w, &o, len);

    // send the data
    q_write(cs, &o, true);

    // read the data
    struct w_iov_sq i = w_iov_sq_initializer(i);
    struct q_stream * const ss = q_read(sc, &i, true);
    if (likely(ss)) {
        q_readall_stream(ss, &i);
        q_close_stream(ss);
    }
    q_close_stream(cs);

    const uint64_t ilen = w_iov_sq_len(&i);
    q_free(&i);
    q_free(&o);

    struct q_conn_info cci = {0};
    struct q_conn_info sci = {0};
    q_info(cc, &cci);
    q_info(sc, &sci);
    log(&cci, &sci);

    return ilen;
}

static void BM_conn(benchmark::State & state)
{
    const auto len = uint64_t(state.range(0));
    for (auto _ : state) {
        const uint64_t ilen = io(len);
        if (ilen != len) {
            state.SkipWithError("error");
            return;
        }
    }
    state.SetBytesProcessed(int64_t(state.iterations() * len)); // NOLINT
}

BENCHMARK(BM_conn)->RangeMultiplier(2)->Range(1024, 1024 * 1024 * 2)
    // ->Unit(benchmark::kMillisecond)
    ;

// BENCHMARK_MAIN()

int main(int argc __attribute__((unused)), char ** argv)
{
#ifndef NDEBUG
    util_dlevel = DBG; // default to maximum compiled-in verbosity
#endif

    // init
    const int cwd = open(".", O_CLOEXEC);
    ensure(cwd != -1, "cannot open");
    ensure(chdir(dirname(argv[0])) == 0, "cannot chdir");
    const struct q_conf conf = {nullptr, "dummy.crt", "dummy.key"};
    w = q_init("lo"
#ifndef __linux__
               "0"
#endif
               ,
               &conf);
    ensure(fchdir(cwd) == 0, "cannot fchdir");

    // bind server socket
    q_bind(w, 55555);

    // connect to server
    struct sockaddr_in sip = {};
    sip.sin_family = AF_INET;
    sip.sin_port = htons(55555);
    sip.sin_addr.s_addr = inet_addr("127.0.0.1");
    cc = q_connect(w, reinterpret_cast<struct sockaddr *>(&sip), // NOLINT
                   "localhost", nullptr, nullptr, true, nullptr);
    ensure(cc, "is zero");

    // accept connection
    sc = q_accept(nullptr);
    ensure(sc, "is zero");

    benchmark::RunSpecifiedBenchmarks();

    // close connections
    q_close(cc, 0, nullptr);
    q_close(sc, 0, nullptr);
    q_cleanup(w);
}
<|endoftext|>


<commit_before>#pragma once
/*
	_ExtInt is for only clang-12 or before
	clang-14 does not support it.
	so use a generated ll by clang-12.
	If gcc and clang supports _BitInt then replace them.
	cpp ->(clang -emit-llvm)-> bc ->(llvm-dis) -> ll
*/

#include <mcl/config.hpp>
#include <assert.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace mcl { namespace bint {

inline uint64_t make64(uint32_t H, uint32_t L)
{
	return ((uint64_t)H << 32) | L;
}

inline void split64(uint32_t *H, uint32_t *L, uint64_t x)
{
	*H = uint32_t(x >> 32);
	*L = uint32_t(x);
}

// return the real size of x
// return 1 if x[n] == 0
inline size_t getRealSize(const Unit *x, size_t n)
{
	while (n > 0) {
		if (x[n - 1]) break;
		n--;
	}
	return n > 0 ? n : 1;
}

/*
	[H:L] <= x * y
	@return L
*/
inline uint32_t mulUnit1(uint32_t *pH, uint32_t x, uint32_t y)
{
	uint64_t t = uint64_t(x) * y;
	uint32_t L;
	split64(pH, &L, t);
	return L;
}

/*
	q = [H:L] / y
	r = [H:L] % y
	return q
*/
inline uint32_t divUnit1(uint32_t *pr, uint32_t H, uint32_t L, uint32_t y)
{
	assert(y != 0);
	uint64_t t = make64(H, L);
	uint32_t q = uint32_t(t / y);
	*pr = uint32_t(t % y);
	return q;
}

#if MCL_SIZEOF_UNIT == 8
inline uint64_t mulUnit1(uint64_t *pH, uint64_t x, uint64_t y)
{
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
	return _umul128(x, y, pH);
#else
	typedef __attribute__((mode(TI))) unsigned int uint128;
	uint128 t = uint128(x) * y;
	*pH = uint64_t(t >> 64);
	return uint64_t(t);
#endif
}

inline uint64_t divUnit1(uint64_t *pr, uint64_t H, uint64_t L, uint64_t y)
{
	assert(y != 0);
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
	return _udiv128(H, L, y, pr);
#else
	typedef __attribute__((mode(TI))) unsigned int uint128;
	uint128 t = (uint128(H) << 64) | L;
	uint64_t q = uint64_t(t / y);
	*pr = uint64_t(t % y);
	return q;
#endif
}

#endif // MCL_SIZEOF_UNIT == 8

template<size_t N>
struct BitInt {
	static const size_t bitSize = sizeof(Unit) * 8 * N;
	typedef unsigned _ExtInt(bitSize) Type;
	Type v;
	Unit getTopUnit() const
	{
		return static_cast<Unit>(v >> (bitSize - sizeof(Unit) * 8));
	}
	Unit getMSB() const
	{
		return getTopUnit() >> (sizeof(Unit) * 8 - 1);
	}
	static const BitInt<N>& load(const void *x)
	{
		return *(const BitInt<N>*)x;
	}
	void save(void *x) const
	{
		*(BitInt<N>*)(x) = *this;
	}
	template<size_t M>
	BitInt<M> cvt() const
	{
		BitInt<M> ret;
		ret.v = static_cast<typename BitInt<M>::Type>(this->v);
		return ret;
	}
};

// true if x[N] == y[N]
template<size_t N>
bool cmpEqT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v == y.v;
}

// true if x[N] >= y[N]
template<size_t N>
bool cmpGeT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v >= y.v;
}

// true if x[N] > y[N]
template<size_t N>
bool cmpGtT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v > y.v;
}

// true if x[N] <= y[N]
template<size_t N>
bool cmpLeT(const Unit *px, const Unit *py)
{
	return !cmpGtT<N>(px, py);
}

// true if x[N] < y[N]
template<size_t N>
bool cmpLtT(const Unit *px, const Unit *py)
{
	return !cmpGeT<N>(px, py);
}

// z[N] = x[N] + y[N] and return CF(0 or 1)
template<size_t N>
Unit addT(Unit *pz, const Unit *px, const Unit *py)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto y = BitInt<N>::load(py).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v + y.v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[N] = x[N] - y[N] and return CF(0 or 1)
template<size_t N>
Unit subT(Unit *pz, const Unit *px, const Unit *py)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto y = BitInt<N>::load(py).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v - y.v;
	z.template cvt<N>().save(pz);
	return z.getMSB();
}

// [ret:z[N]] = x[N] * y
template<size_t N>
Unit mulUnitT(Unit *pz, const Unit *px, Unit y)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	BitInt<1> y1;
	BitInt<N+1> z;
	y1.v = y;
	z.v = x.v * y1.template cvt<N+1>().v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// [ret:z[N]] = z[N] + x[N] * y
template<size_t N>
Unit mulUnitAddT(Unit *pz, const Unit *px, Unit y)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto z = BitInt<N>::load(pz).template cvt<N+1>();
	BitInt<1> y1;
	y1.v = y;
	z.v += x.v * y1.template cvt<N+1>().v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[2N] = x[N] * y[N]
template<size_t N>
void mulT(Unit *pz, const Unit *px, const Unit *py)
{
	pz[N] = mulUnitT<N>(pz, px, py[0]);
	for (size_t i = 1; i < N; i++) {
		pz[N + i] = mulUnitAddT<N>(&pz[i], px, py[i]);
	}
}

// [ret:z[N]] = x[N] << y
template<size_t N>
Unit shlT(Unit *pz, const Unit *px, Unit y)
{
	assert(0 < y && y < sizeof(Unit) * 8);
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v << y;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[N] = x[N] >> y
template<size_t N>
void shrT(Unit *pz, const Unit *px, Unit y)
{
	assert(0 < y && y < sizeof(Unit) * 8);
	auto x = BitInt<N>::load(px);
	x.v >>= y;
	x.template cvt<N>().save(pz);
}

// z[n] = x[n] + y
inline Unit addUnit(Unit *z, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit t = x[0] + y;
	z[0] = t;
	size_t i = 0;
	if (t >= y) goto EXIT_0;
	i = 1;
	for (; i < n; i++) {
		t = x[i] + 1;
		z[i] = t;
		if (t != 0) goto EXIT_0;
	}
	return 1;
EXIT_0:
	i++;
	for (; i < n; i++) {
		z[i] = x[i];
	}
	return 0;
}
// x[n] += y
inline Unit addUnit(Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit t = x[0] + y;
	x[0] = t;
	size_t i = 0;
	if (t >= y) return 0;
	i = 1;
	for (; i < n; i++) {
		t = x[i] + 1;
		x[i] = t;
		if (t != 0) return 0;
	}
	return 1;
}

// z[n] = x[n] - y
inline Unit subUnit(Unit *z, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit c = x[0] < y ? 1 : 0;
	z[0] = x[0] - y;
	for (size_t i = 1; i < n; i++) {
		if (x[i] < c) {
			z[i] = Unit(-1);
		} else {
			z[i] = x[i] - c;
			c = 0;
		}
	}
	return c;
}

/*
	q[] = x[] / y
	@retval r = x[] % y
	accept q == x
*/
inline Unit divUnit(Unit *q, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit r = 0;
	for (int i = (int)n - 1; i >= 0; i--) {
		q[i] = divUnit1(&r, r, x[i], y);
	}
	return r;
}
/*
	q[] = x[] / y
	@retval r = x[] % y
*/
inline Unit modUnit(const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit r = 0;
	for (int i = (int)n - 1; i >= 0; i--) {
		divUnit1(&r, r, x[i], y);
	}
	return r;
}

// x[n] = 0
inline void clear(Unit *x, size_t n)
{
	for (size_t i = 0; i < n; i++) x[i] = 0;
}

template<size_t N>
struct FuncT {
	static inline Unit add(Unit *z, const Unit *x, const Unit *y)
	{
		return addT<N>(z, x, y);
	}
	static inline Unit sub(Unit *z, const Unit *x, const Unit *y)
	{
		return subT<N>(z, x, y);
	}
	static inline Unit mulUnit(Unit *z, const Unit *x, Unit y)
	{
		return mulUnitT<N>(z, x, y);
	}
	static inline bool cmpGe(const Unit *x, const Unit *y)
	{
		return cmpGeT<N>(x, y);
	}
};
/*
	y must be sizeof(Unit) * 8 * N bit
	x[xn] = x[xn] % y[N]
	q[qn] = x[xn] / y[N] if q != NULL
	return new xn
*/
template<typename Func, size_t N>
size_t divFullBitT(Unit *q, size_t qn, Unit *x, size_t xn, const Unit *y)
{
	assert(xn > 0);
	assert((y[N - 1] >> (sizeof(Unit) * 8 - 1)) != 0);
	assert(q != x && q != y && x != y);
	if (q) clear(q, qn);
	Unit *t = (Unit*)CYBOZU_ALLOCA(sizeof(Unit) * (N + 1));
	while (xn > N) {
		if (x[xn - 1] == 0) {
			xn--;
			continue;
		}
		size_t d = xn - N;
		if (Func::cmpGe(x + d, y)) {
			Func::sub(x + d, x + d, y);
			if (q) addUnit(q + d, qn - d, 1);
		} else {
			Unit xTop = x[xn - 1];
			if (xTop == 1) {
				Unit ret = Func::sub(x + d - 1, x + d - 1, y);
				x[xn-1] -= ret;
			} else {
				t[N] = Func::mulUnit(t, y, xTop);
				Unit ret = Func::sub(x + d - 1, x + d - 1, t);
				x[xn-1] -= t[N] + ret;
			}
			if (q) addUnit(q + d - 1, qn - d + 1, xTop);
		}
	}
	if (Func::cmpGe(x, y)) {
		Func::sub(x, x, y);
		if (q) addUnit(q, qn, 1);
	}
	xn = getRealSize(x, xn);
	return xn;
}

} } // mcl::bint

<commit_msg>decrease temporary buf<commit_after>#pragma once
/*
	_ExtInt is for only clang-12 or before
	clang-14 does not support it.
	so use a generated ll by clang-12.
	If gcc and clang supports _BitInt then replace them.
	cpp ->(clang -emit-llvm)-> bc ->(llvm-dis) -> ll
*/

#include <mcl/config.hpp>
#include <assert.h>
#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace mcl { namespace bint {

inline uint64_t make64(uint32_t H, uint32_t L)
{
	return ((uint64_t)H << 32) | L;
}

inline void split64(uint32_t *H, uint32_t *L, uint64_t x)
{
	*H = uint32_t(x >> 32);
	*L = uint32_t(x);
}

// return the real size of x
// return 1 if x[n] == 0
inline size_t getRealSize(const Unit *x, size_t n)
{
	while (n > 0) {
		if (x[n - 1]) break;
		n--;
	}
	return n > 0 ? n : 1;
}

/*
	[H:L] <= x * y
	@return L
*/
inline uint32_t mulUnit1(uint32_t *pH, uint32_t x, uint32_t y)
{
	uint64_t t = uint64_t(x) * y;
	uint32_t L;
	split64(pH, &L, t);
	return L;
}

/*
	q = [H:L] / y
	r = [H:L] % y
	return q
*/
inline uint32_t divUnit1(uint32_t *pr, uint32_t H, uint32_t L, uint32_t y)
{
	assert(y != 0);
	uint64_t t = make64(H, L);
	uint32_t q = uint32_t(t / y);
	*pr = uint32_t(t % y);
	return q;
}

#if MCL_SIZEOF_UNIT == 8
inline uint64_t mulUnit1(uint64_t *pH, uint64_t x, uint64_t y)
{
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
	return _umul128(x, y, pH);
#else
	typedef __attribute__((mode(TI))) unsigned int uint128;
	uint128 t = uint128(x) * y;
	*pH = uint64_t(t >> 64);
	return uint64_t(t);
#endif
}

inline uint64_t divUnit1(uint64_t *pr, uint64_t H, uint64_t L, uint64_t y)
{
	assert(y != 0);
#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
	return _udiv128(H, L, y, pr);
#else
	typedef __attribute__((mode(TI))) unsigned int uint128;
	uint128 t = (uint128(H) << 64) | L;
	uint64_t q = uint64_t(t / y);
	*pr = uint64_t(t % y);
	return q;
#endif
}

#endif // MCL_SIZEOF_UNIT == 8

template<size_t N>
struct BitInt {
	static const size_t bitSize = sizeof(Unit) * 8 * N;
	typedef unsigned _ExtInt(bitSize) Type;
	Type v;
	Unit getTopUnit() const
	{
		return static_cast<Unit>(v >> (bitSize - sizeof(Unit) * 8));
	}
	Unit getMSB() const
	{
		return getTopUnit() >> (sizeof(Unit) * 8 - 1);
	}
	static const BitInt<N>& load(const void *x)
	{
		return *(const BitInt<N>*)x;
	}
	void save(void *x) const
	{
		*(BitInt<N>*)(x) = *this;
	}
	template<size_t M>
	BitInt<M> cvt() const
	{
		BitInt<M> ret;
		ret.v = static_cast<typename BitInt<M>::Type>(this->v);
		return ret;
	}
};

// true if x[N] == y[N]
template<size_t N>
bool cmpEqT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v == y.v;
}

// true if x[N] >= y[N]
template<size_t N>
bool cmpGeT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v >= y.v;
}

// true if x[N] > y[N]
template<size_t N>
bool cmpGtT(const Unit *px, const Unit *py)
{
	const auto& x = BitInt<N>::load(px);
	const auto& y = BitInt<N>::load(py);
	return x.v > y.v;
}

// true if x[N] <= y[N]
template<size_t N>
bool cmpLeT(const Unit *px, const Unit *py)
{
	return !cmpGtT<N>(px, py);
}

// true if x[N] < y[N]
template<size_t N>
bool cmpLtT(const Unit *px, const Unit *py)
{
	return !cmpGeT<N>(px, py);
}

// z[N] = x[N] + y[N] and return CF(0 or 1)
template<size_t N>
Unit addT(Unit *pz, const Unit *px, const Unit *py)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto y = BitInt<N>::load(py).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v + y.v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[N] = x[N] - y[N] and return CF(0 or 1)
template<size_t N>
Unit subT(Unit *pz, const Unit *px, const Unit *py)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto y = BitInt<N>::load(py).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v - y.v;
	z.template cvt<N>().save(pz);
	return z.getMSB();
}

// [ret:z[N]] = x[N] * y
template<size_t N>
Unit mulUnitT(Unit *pz, const Unit *px, Unit y)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	BitInt<1> y1;
	BitInt<N+1> z;
	y1.v = y;
	z.v = x.v * y1.template cvt<N+1>().v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// [ret:z[N]] = z[N] + x[N] * y
template<size_t N>
Unit mulUnitAddT(Unit *pz, const Unit *px, Unit y)
{
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	auto z = BitInt<N>::load(pz).template cvt<N+1>();
	BitInt<1> y1;
	y1.v = y;
	z.v += x.v * y1.template cvt<N+1>().v;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[2N] = x[N] * y[N]
template<size_t N>
void mulT(Unit *pz, const Unit *px, const Unit *py)
{
	pz[N] = mulUnitT<N>(pz, px, py[0]);
	for (size_t i = 1; i < N; i++) {
		pz[N + i] = mulUnitAddT<N>(&pz[i], px, py[i]);
	}
}

// [ret:z[N]] = x[N] << y
template<size_t N>
Unit shlT(Unit *pz, const Unit *px, Unit y)
{
	assert(0 < y && y < sizeof(Unit) * 8);
	auto x = BitInt<N>::load(px).template cvt<N+1>();
	BitInt<N+1> z;
	z.v = x.v << y;
	z.template cvt<N>().save(pz);
	return z.getTopUnit();
}

// z[N] = x[N] >> y
template<size_t N>
void shrT(Unit *pz, const Unit *px, Unit y)
{
	assert(0 < y && y < sizeof(Unit) * 8);
	auto x = BitInt<N>::load(px);
	x.v >>= y;
	x.template cvt<N>().save(pz);
}

// z[n] = x[n] + y
inline Unit addUnit(Unit *z, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit t = x[0] + y;
	z[0] = t;
	size_t i = 0;
	if (t >= y) goto EXIT_0;
	i = 1;
	for (; i < n; i++) {
		t = x[i] + 1;
		z[i] = t;
		if (t != 0) goto EXIT_0;
	}
	return 1;
EXIT_0:
	i++;
	for (; i < n; i++) {
		z[i] = x[i];
	}
	return 0;
}
// x[n] += y
inline Unit addUnit(Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit t = x[0] + y;
	x[0] = t;
	size_t i = 0;
	if (t >= y) return 0;
	i = 1;
	for (; i < n; i++) {
		t = x[i] + 1;
		x[i] = t;
		if (t != 0) return 0;
	}
	return 1;
}

// z[n] = x[n] - y
inline Unit subUnit(Unit *z, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit c = x[0] < y ? 1 : 0;
	z[0] = x[0] - y;
	for (size_t i = 1; i < n; i++) {
		if (x[i] < c) {
			z[i] = Unit(-1);
		} else {
			z[i] = x[i] - c;
			c = 0;
		}
	}
	return c;
}

/*
	q[] = x[] / y
	@retval r = x[] % y
	accept q == x
*/
inline Unit divUnit(Unit *q, const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit r = 0;
	for (int i = (int)n - 1; i >= 0; i--) {
		q[i] = divUnit1(&r, r, x[i], y);
	}
	return r;
}
/*
	q[] = x[] / y
	@retval r = x[] % y
*/
inline Unit modUnit(const Unit *x, size_t n, Unit y)
{
	assert(n > 0);
	Unit r = 0;
	for (int i = (int)n - 1; i >= 0; i--) {
		divUnit1(&r, r, x[i], y);
	}
	return r;
}

// x[n] = 0
inline void clear(Unit *x, size_t n)
{
	for (size_t i = 0; i < n; i++) x[i] = 0;
}

template<size_t N>
struct FuncT {
	static inline Unit add(Unit *z, const Unit *x, const Unit *y)
	{
		return addT<N>(z, x, y);
	}
	static inline Unit sub(Unit *z, const Unit *x, const Unit *y)
	{
		return subT<N>(z, x, y);
	}
	static inline Unit mulUnit(Unit *z, const Unit *x, Unit y)
	{
		return mulUnitT<N>(z, x, y);
	}
	static inline bool cmpGe(const Unit *x, const Unit *y)
	{
		return cmpGeT<N>(x, y);
	}
};
/*
	y must be sizeof(Unit) * 8 * N bit
	x[xn] = x[xn] % y[N]
	q[qn] = x[xn] / y[N] if q != NULL
	return new xn
*/
template<typename Func, size_t N>
size_t divFullBitT(Unit *q, size_t qn, Unit *x, size_t xn, const Unit *y)
{
	assert(xn > 0);
	assert((y[N - 1] >> (sizeof(Unit) * 8 - 1)) != 0);
	assert(q != x && q != y && x != y);
	if (q) clear(q, qn);
	Unit *t = (Unit*)CYBOZU_ALLOCA(sizeof(Unit) * N + 1);
	while (xn > N) {
		if (x[xn - 1] == 0) {
			xn--;
			continue;
		}
		size_t d = xn - N;
		if (Func::cmpGe(x + d, y)) {
			Func::sub(x + d, x + d, y);
			if (q) addUnit(q + d, qn - d, 1);
		} else {
			Unit xTop = x[xn - 1];
			if (xTop == 1) {
				Unit ret = Func::sub(x + d - 1, x + d - 1, y);
				x[xn-1] -= ret;
			} else {
				Unit ret = Func::mulUnit(t, y, xTop);
				ret += Func::sub(x + d - 1, x + d - 1, t);
				x[xn-1] -= ret;
			}
			if (q) addUnit(q + d - 1, qn - d + 1, xTop);
		}
	}
	if (Func::cmpGe(x, y)) {
		Func::sub(x, x, y);
		if (q) addUnit(q, qn, 1);
	}
	xn = getRealSize(x, xn);
	return xn;
}

} } // mcl::bint

<|endoftext|>


<commit_before>#pragma once

#include <pajlada/settings/common.hpp>
#include <pajlada/settings/equal.hpp>
#include <pajlada/settings/settingdata.hpp>
#include <pajlada/settings/settingmanager.hpp>

#include <rapidjson/document.h>
#include <pajlada/signals/signal.hpp>

#include <iostream>
#include <type_traits>

namespace pajlada {
namespace Settings {

namespace {

SignalArgs
onConnectArgs()
{
    static SignalArgs a = []() {
        SignalArgs v;
        v.source = SignalArgs::Source::OnConnect;

        return v;
    }();

    return a;
}

}  // namespace

// A default value passed to a setting is only local to this specific instance of the setting
// it is never shared between other settings at the same path
template <typename Type>
class Setting
{
    const std::string path;

public:
    explicit Setting(const std::string &_path,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
    {
    }

    explicit Setting(const char *_path,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
    {
    }

    explicit Setting(const std::string &_path, Type _defaultValue,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const char *_path, Type _defaultValue,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const std::string &_path,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
    {
    }

    explicit Setting(const char *_path,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
    {
    }

    explicit Setting(const std::string &_path, Type _defaultValue,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const char *_path, Type _defaultValue,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , defaultValue(std::move(_defaultValue))
    {
    }

    inline bool
    optionEnabled(SettingOption option) const
    {
        return (this->options & option) == option;
    }

    ~Setting() = default;

    bool
    isValid() const
    {
        return !this->data.expired();
    }

    const std::string &
    getPath() const
    {
        return this->path;
    }

    const Type &
    getValue() const
    {
        auto lockedSetting = this->data.lock();

        if (!lockedSetting) {
            if (this->value) {
                return *this->value;
            }

            return this->defaultValue;
        }

        if (this->updateIteration == lockedSetting->getUpdateIteration()) {
            // Value hasn't been updated
            if (this->value) {
                return *this->value;
            }

            return this->defaultValue;
        }

        auto p = lockedSetting->template unmarshal<Type>();
        if (p.value) {
            this->value = p.value;
            this->updateIteration = p.updateIteration;
        }

        if (this->value) {
            return *this->value;
        }

        return this->defaultValue;
    }

    template <typename T = Type,
              typename = std::enable_if_t<is_stl_container<T>::value>>
    const Type &
    getArray() const
    {
        auto lockedSetting = this->data.lock();

        if (!lockedSetting) {
            if (this->defaultValue) {
                return *this->defaultValue;
            }

            return this->value;
        }

        auto p = lockedSetting->template unmarshal<Type>();
        if (p.second) {
            return p.first;
        }

        if (this->defaultValue) {
            return *this->defaultValue;
        }

        return p.first;
    }

    // Implement vector helper stuff
    template <typename T = Type,
              typename = std::enable_if_t<is_stl_container<T>::value>>
    void
    push_back(typename T::value_type &&value) const
    {
        auto lockedSetting = this->getLockedData();

        lockedSetting->push_back(std::move(value));
    }

    bool
    setValue(const Type &newValue, SignalArgs &&args = SignalArgs())
    {
        this->value = newValue;

        if (this->optionEnabled(SettingOption::DoNotWriteToJSON)) {
            args.writeToFile = false;
        }

        auto lockedSetting = this->data.lock();

        if (lockedSetting) {
            if (args.source == SignalArgs::Source::Unset) {
                args.source = SignalArgs::Source::Setter;
            }
            return lockedSetting->marshal(newValue, std::move(args));
        }

        return false;
    }

    Setting &
    operator=(const Type &newValue)
    {
        this->setValue(newValue);

        return *this;
    }

    template <typename T2>
    Setting &
    operator=(const T2 &newValue)
    {
        this->setValue(Type(newValue));

        return *this;
    }

    Setting &
    operator=(Type &&newValue)
    {
        this->setValue(std::move(newValue));

        return *this;
    }

    bool
    operator==(const Type &rhs) const
    {
        assert(this->isValid());

        return this->getValue() == rhs;
    }

    bool
    operator!=(const Type &rhs) const
    {
        assert(this->isValid());

        return this->getValue() != rhs;
    }

    operator Type() const
    {
        assert(this->isValid());

        return this->getValue();
    }

    void
    resetToDefaultValue(SignalArgs &&args = SignalArgs())
    {
        this->setValue(this->defaultValue, std::move(args));
    }

    void
    setDefaultValue(const Type &newDefaultValue)
    {
        this->defaultValue = newDefaultValue;
    }

    Type
    getDefaultValue() const
    {
        return this->defaultValue;
    }

    // Returns true if the current value is the same as the default value
    // boost::any cannot be properly compared
    bool
    isDefaultValue() const
    {
        return IsEqual<Type>::get(this->getValue(), this->getDefaultValue());
    }

    // Remove will invalidate this setting and all other settings that point at the same path
    // If the setting is an object or array, any child settings will also be invalidated
    // the remove function handles the exception handling in case this setting is already invalid
    bool
    remove()
    {
        return SettingManager::removeSetting(this->getPath());
    }

private:
    std::weak_ptr<SettingData> data;
    SettingOption options = SettingOption::Default;
    Type defaultValue{};

    // These two are mutable because they can be modified from the "getValue" function
    mutable OptionalType<Type> value;
    mutable int updateIteration = -1;

public:
    std::weak_ptr<SettingData>
    getData()
    {
        return this->data;
    }

    // ConnectJSON: Connect with rapidjson::Value and SignalArgs as arguments
    // No deserialization is made by the setting
    void
    connectJSON(
        std::function<void(const rapidjson::Value &, const SignalArgs &)> func,
        bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(func);

        if (autoInvoke) {
            auto ptr = lockedSetting->unmarshalJSON();
            rapidjson::Document d;
            if (ptr != nullptr) {
                d.CopyFrom(*ptr, d.GetAllocator());
            }
            connection.invoke(std::move(d), onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connectJSON(
        std::function<void(const rapidjson::Value &, const SignalArgs &)> func,
        ConnectionManager &userDefinedManagedConnections,
        bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(func);

        if (autoInvoke) {
            auto ptr = lockedSetting->unmarshalJSON();
            rapidjson::Document d;
            if (ptr != nullptr) {
                d.CopyFrom(*ptr, d.GetAllocator());
            }
            connection.invoke(std::move(d), onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: Value and SignalArgs
    void
    connect(std::function<void(const Type &, const SignalArgs &)> func,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &args) {
                func(Deserialize<Type>::get(value), args);  //
            });

        if (autoInvoke) {
            func(this->getValue(), onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void(const Type &, const SignalArgs &)> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &args) {
                func(Deserialize<Type>::get(value), args);  //
            });

        if (autoInvoke) {
            func(this->getValue(), onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: Value
    void
    connect(std::function<void(const Type &)> func, bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &) {
                func(Deserialize<Type>::get(value));  //
            });

        if (autoInvoke) {
            func(this->getValue());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void(const Type &)> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &) {
                func(Deserialize<Type>::get(value));  //
            });

        if (autoInvoke) {
            func(this->getValue());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: no args
    void
    connect(std::function<void()> func, bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &) {
                func();  //
            });

        if (autoInvoke) {
            func();
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void()> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &) {
                func();  //
            });

        if (autoInvoke) {
            func();
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // ConnectSimple: Signal args only
    void
    connectSimple(std::function<void(const SignalArgs &)> func,
                  bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &args) {
                func(args);  //
            });

        if (autoInvoke) {
            func(onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connectSimple(std::function<void(const SignalArgs &)> func,
                  ConnectionManager &userDefinedManagedConnections,
                  bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &args) {
                func(args);  //
            });

        if (autoInvoke) {
            func(onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Static helper methods for one-offs (get or set setting)
    static const Type
    get(const std::string &path, SettingOption options = SettingOption::Default)
    {
        Setting<Type> setting(path, options);

        return setting.getValue();
    }

    static void
    set(const std::string &path, const Type &newValue,
        SettingOption options = SettingOption::Default)
    {
        Setting<Type> setting(path, options);

        setting.setValue(newValue);
    }

private:
    std::vector<Signals::ScopedConnection> managedConnections;
};

}  // namespace Settings
}  // namespace pajlada
<commit_msg>Mutex-lock access to a Setting's value<commit_after>#pragma once

#include <pajlada/settings/common.hpp>
#include <pajlada/settings/equal.hpp>
#include <pajlada/settings/settingdata.hpp>
#include <pajlada/settings/settingmanager.hpp>

#include <rapidjson/document.h>
#include <pajlada/signals/signal.hpp>

#include <iostream>
#include <mutex>
#include <type_traits>

namespace pajlada {
namespace Settings {

namespace {

SignalArgs
onConnectArgs()
{
    static SignalArgs a = []() {
        SignalArgs v;
        v.source = SignalArgs::Source::OnConnect;

        return v;
    }();

    return a;
}

}  // namespace

// A default value passed to a setting is only local to this specific instance of the setting
// it is never shared between other settings at the same path
template <typename Type>
class Setting
{
    const std::string path;

public:
    explicit Setting(const std::string &_path,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
    {
    }

    explicit Setting(const char *_path,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
    {
    }

    explicit Setting(const std::string &_path, Type _defaultValue,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const char *_path, Type _defaultValue,
                     SettingOption _options = SettingOption::Default,
                     std::shared_ptr<SettingManager> instance = nullptr)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , options(_options)
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const std::string &_path,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
    {
    }

    explicit Setting(const char *_path,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
    {
    }

    explicit Setting(const std::string &_path, Type _defaultValue,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , defaultValue(std::move(_defaultValue))
    {
    }

    explicit Setting(const char *_path, Type _defaultValue,
                     std::shared_ptr<SettingManager> instance)
        : path(_path)
        , data(SettingManager::getSetting(_path, instance))
        , defaultValue(std::move(_defaultValue))
    {
    }

    // Copy constructor
    Setting(const Setting &other)
        : path(other.path)
        , data(other.data)
        , options(other.options)
        , defaultValue(other.defaultValue)
        , value(other.value)
        , updateIteration(other.updateIteration)
    {
        // managedConnections is not copied on purpose
        // valueMutex is not copied on purpose
    }

    inline bool
    optionEnabled(SettingOption option) const
    {
        return (this->options & option) == option;
    }

    ~Setting() = default;

    bool
    isValid() const
    {
        return !this->data.expired();
    }

    const std::string &
    getPath() const
    {
        return this->path;
    }

    const Type &
    getValue() const
    {
        std::unique_lock<std::mutex> lock(this->valueMutex);

        auto lockedSetting = this->data.lock();

        if (!lockedSetting) {
            if (this->value) {
                return *this->value;
            }

            return this->defaultValue;
        }

        if (this->updateIteration == lockedSetting->getUpdateIteration()) {
            // Value hasn't been updated
            if (this->value) {
                return *this->value;
            }

            return this->defaultValue;
        }

        auto p = lockedSetting->template unmarshal<Type>();
        if (p.value) {
            this->value = p.value;
            this->updateIteration = p.updateIteration;
        }

        if (this->value) {
            return *this->value;
        }

        return this->defaultValue;
    }

    template <typename T = Type,
              typename = std::enable_if_t<is_stl_container<T>::value>>
    const Type &
    getArray() const
    {
        auto lockedSetting = this->data.lock();

        if (!lockedSetting) {
            if (this->defaultValue) {
                return *this->defaultValue;
            }

            return this->value;
        }

        auto p = lockedSetting->template unmarshal<Type>();
        if (p.second) {
            return p.first;
        }

        if (this->defaultValue) {
            return *this->defaultValue;
        }

        return p.first;
    }

    // Implement vector helper stuff
    template <typename T = Type,
              typename = std::enable_if_t<is_stl_container<T>::value>>
    void
    push_back(typename T::value_type &&value) const
    {
        auto lockedSetting = this->getLockedData();

        lockedSetting->push_back(std::move(value));
    }

    bool
    setValue(const Type &newValue, SignalArgs &&args = SignalArgs())
    {
        {
            std::unique_lock<std::mutex> lock(this->valueMutex);
            this->value = newValue;
        }

        if (this->optionEnabled(SettingOption::DoNotWriteToJSON)) {
            args.writeToFile = false;
        }

        auto lockedSetting = this->data.lock();

        if (lockedSetting) {
            if (args.source == SignalArgs::Source::Unset) {
                args.source = SignalArgs::Source::Setter;
            }
            return lockedSetting->marshal(newValue, std::move(args));
        }

        return false;
    }

    Setting &
    operator=(const Type &newValue)
    {
        this->setValue(newValue);

        return *this;
    }

    template <typename T2>
    Setting &
    operator=(const T2 &newValue)
    {
        this->setValue(Type(newValue));

        return *this;
    }

    Setting &
    operator=(Type &&newValue)
    {
        this->setValue(std::move(newValue));

        return *this;
    }

    bool
    operator==(const Type &rhs) const
    {
        assert(this->isValid());

        return this->getValue() == rhs;
    }

    bool
    operator!=(const Type &rhs) const
    {
        assert(this->isValid());

        return this->getValue() != rhs;
    }

    operator Type() const
    {
        assert(this->isValid());

        return this->getValue();
    }

    void
    resetToDefaultValue(SignalArgs &&args = SignalArgs())
    {
        this->setValue(this->defaultValue, std::move(args));
    }

    void
    setDefaultValue(const Type &newDefaultValue)
    {
        this->defaultValue = newDefaultValue;
    }

    Type
    getDefaultValue() const
    {
        return this->defaultValue;
    }

    // Returns true if the current value is the same as the default value
    // boost::any cannot be properly compared
    bool
    isDefaultValue() const
    {
        return IsEqual<Type>::get(this->getValue(), this->getDefaultValue());
    }

    // Remove will invalidate this setting and all other settings that point at the same path
    // If the setting is an object or array, any child settings will also be invalidated
    // the remove function handles the exception handling in case this setting is already invalid
    bool
    remove()
    {
        return SettingManager::removeSetting(this->getPath());
    }

private:
    std::weak_ptr<SettingData> data;
    SettingOption options = SettingOption::Default;
    Type defaultValue{};

    // These are mutable because they can be modified from the "getValue" function
    mutable std::mutex valueMutex;
    mutable OptionalType<Type> value;
    mutable int updateIteration = -1;

public:
    std::weak_ptr<SettingData>
    getData()
    {
        return this->data;
    }

    // ConnectJSON: Connect with rapidjson::Value and SignalArgs as arguments
    // No deserialization is made by the setting
    void
    connectJSON(
        std::function<void(const rapidjson::Value &, const SignalArgs &)> func,
        bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(func);

        if (autoInvoke) {
            auto ptr = lockedSetting->unmarshalJSON();
            rapidjson::Document d;
            if (ptr != nullptr) {
                d.CopyFrom(*ptr, d.GetAllocator());
            }
            connection.invoke(std::move(d), onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connectJSON(
        std::function<void(const rapidjson::Value &, const SignalArgs &)> func,
        ConnectionManager &userDefinedManagedConnections,
        bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(func);

        if (autoInvoke) {
            auto ptr = lockedSetting->unmarshalJSON();
            rapidjson::Document d;
            if (ptr != nullptr) {
                d.CopyFrom(*ptr, d.GetAllocator());
            }
            connection.invoke(std::move(d), onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: Value and SignalArgs
    void
    connect(std::function<void(const Type &, const SignalArgs &)> func,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &args) {
                func(Deserialize<Type>::get(value), args);  //
            });

        if (autoInvoke) {
            func(this->getValue(), onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void(const Type &, const SignalArgs &)> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &args) {
                func(Deserialize<Type>::get(value), args);  //
            });

        if (autoInvoke) {
            func(this->getValue(), onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: Value
    void
    connect(std::function<void(const Type &)> func, bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &) {
                func(Deserialize<Type>::get(value));  //
            });

        if (autoInvoke) {
            func(this->getValue());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void(const Type &)> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &value, const SignalArgs &) {
                func(Deserialize<Type>::get(value));  //
            });

        if (autoInvoke) {
            func(this->getValue());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Connect: no args
    void
    connect(std::function<void()> func, bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &) {
                func();  //
            });

        if (autoInvoke) {
            func();
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connect(std::function<void()> func,
            ConnectionManager &userDefinedManagedConnections,
            bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &) {
                func();  //
            });

        if (autoInvoke) {
            func();
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // ConnectSimple: Signal args only
    void
    connectSimple(std::function<void(const SignalArgs &)> func,
                  bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &args) {
                func(args);  //
            });

        if (autoInvoke) {
            func(onConnectArgs());
        }

        this->managedConnections.emplace_back(std::move(connection));
    }

    template <typename ConnectionManager>
    void
    connectSimple(std::function<void(const SignalArgs &)> func,
                  ConnectionManager &userDefinedManagedConnections,
                  bool autoInvoke = true)
    {
        auto lockedSetting = this->data.lock();
        if (!lockedSetting) {
            return;
        }

        auto connection = lockedSetting->updated.connect(
            [=](const rapidjson::Value &, const SignalArgs &args) {
                func(args);  //
            });

        if (autoInvoke) {
            func(onConnectArgs());
        }

        userDefinedManagedConnections.emplace_back(std::move(connection));
    }

    // Static helper methods for one-offs (get or set setting)
    static const Type
    get(const std::string &path, SettingOption options = SettingOption::Default)
    {
        Setting<Type> setting(path, options);

        return setting.getValue();
    }

    static void
    set(const std::string &path, const Type &newValue,
        SettingOption options = SettingOption::Default)
    {
        Setting<Type> setting(path, options);

        setting.setValue(newValue);
    }

private:
    std::vector<Signals::ScopedConnection> managedConnections;
};

}  // namespace Settings
}  // namespace pajlada
<|endoftext|>


<commit_before>#line 2 "togo/image/pixmap/impl/private.ipp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#pragma once

#include <togo/image/config.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/image/pixmap/pixmap.hpp>
#include <togo/image/pixmap/impl/private.hpp>

#include <cstring>

namespace togo {

// private

void pixmap::fill_rgb(Pixmap& p, Color& color, UVec4& rect) {
	enum : unsigned { pixel_size = 3 };
	unsigned num_rows = rect.height;
	unsigned bytes_to_next_row = (p.size.width - rect.width) * pixel_size;
	u8* dst = p.data + (rect.y * p.size.width + rect.x) * pixel_size;
	unsigned n;
	while (num_rows--) {
		n = rect.width;
		while (n--) {
			*dst++ = color.r;
			*dst++ = color.g;
			*dst++ = color.b;
		}
		dst += bytes_to_next_row;
	}
}

void pixmap::fill_packed(Pixmap& p, Color& color, UVec4& rect) {
	enum : unsigned { pixel_size = 4 };
	unsigned num_rows = rect.height;
	unsigned bytes_to_next_row = (p.size.width - rect.width) * pixel_size;
	u32* dst = pointer_add(
		reinterpret_cast<u32*>(p.data),
		(rect.y * p.size.width + rect.x) * pixel_size
	);
	u32 packed = pixel_format::pack(p.format, color);
	unsigned n;
	while (num_rows--) {
		n = rect.width;
		while (n >= 4) {
			*dst++ = packed;
			*dst++ = packed;
			*dst++ = packed;
			*dst++ = packed;
			n -= 4;
		}
		switch (n) {
		case 3: *dst++ = packed;
		case 2: *dst++ = packed;
		case 1: *dst++ = packed;
		case 0: dst += bytes_to_next_row;
		}
	}
}

void pixmap::blit_unscaled_copy(
	PixelFormat const& format,
	u8* dst,
	unsigned dst_width,
	UVec2 dst_pos,
	u8 const* src,
	unsigned src_width,
	UVec4 src_rect
) {
	unsigned pixel_size = pixel_format::pixel_size(format);
	unsigned num_rows = src_rect.height;
	unsigned num_bytes = src_rect.width * pixel_size;

	dst += (dst_pos.y * dst_width + dst_pos.x) * pixel_size;
	src += (src_rect.y * src_width + src_rect.x) * pixel_size;
	dst_width *= pixel_size;
	src_width *= pixel_size;
	// TODO: Optimize for small areas
	while (num_rows--) {
		std::memmove(dst, src, num_bytes);
		dst += dst_width;
		src += src_width;
	}
}

void pixmap::blit_unscaled_choose(
	PixelFormat const& dst_format,
	u8* dst,
	unsigned dst_width,
	UVec2 dst_pos,
	PixelFormat const& src_format,
	u8 const* src,
	unsigned src_width,
	UVec4 src_rect
) {
	if (pixel_format::compare_equal(dst_format, src_format)) {
		pixmap::blit_unscaled_copy(
			dst_format,
			dst, dst_width, dst_pos,
			src, src_width, src_rect
		);
	} else {
		TOGO_ASSERT(false, "TODO: unequal pixmap formats");
	}
}

} // namespace togo
<commit_msg>lib/image/pixmap: corrected fill_packed() row-stepping.<commit_after>#line 2 "togo/image/pixmap/impl/private.ipp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.
*/

#pragma once

#include <togo/image/config.hpp>
#include <togo/core/error/assert.hpp>
#include <togo/core/utility/utility.hpp>
#include <togo/image/pixmap/pixmap.hpp>
#include <togo/image/pixmap/impl/private.hpp>

#include <cstring>

namespace togo {

// private

void pixmap::fill_rgb(Pixmap& p, Color& color, UVec4& rect) {
	enum : unsigned { pixel_size = 3 };
	unsigned num_rows = rect.height;
	unsigned bytes_to_next_row = (p.size.width - rect.width) * pixel_size;
	u8* dst = p.data + (rect.y * p.size.width + rect.x) * pixel_size;
	unsigned n;
	while (num_rows--) {
		n = rect.width;
		while (n--) {
			*dst++ = color.r;
			*dst++ = color.g;
			*dst++ = color.b;
		}
		dst += bytes_to_next_row;
	}
}

void pixmap::fill_packed(Pixmap& p, Color& color, UVec4& rect) {
	enum : unsigned { pixel_size = 4 };
	unsigned num_rows = rect.height;
	unsigned to_next_row = p.size.width - rect.width;
	u32* dst = pointer_add(
		reinterpret_cast<u32*>(p.data),
		(rect.y * p.size.width + rect.x) * pixel_size
	);
	u32 packed = pixel_format::pack(p.format, color);
	unsigned n;
	while (num_rows--) {
		n = rect.width;
		while (n >= 4) {
			*dst++ = packed;
			*dst++ = packed;
			*dst++ = packed;
			*dst++ = packed;
			n -= 4;
		}
		switch (n) {
		case 3: *dst++ = packed;
		case 2: *dst++ = packed;
		case 1: *dst++ = packed;
		case 0: dst += to_next_row;
		}
	}
}

void pixmap::blit_unscaled_copy(
	PixelFormat const& format,
	u8* dst,
	unsigned dst_width,
	UVec2 dst_pos,
	u8 const* src,
	unsigned src_width,
	UVec4 src_rect
) {
	unsigned pixel_size = pixel_format::pixel_size(format);
	unsigned num_rows = src_rect.height;
	unsigned num_bytes = src_rect.width * pixel_size;

	dst += (dst_pos.y * dst_width + dst_pos.x) * pixel_size;
	src += (src_rect.y * src_width + src_rect.x) * pixel_size;
	dst_width *= pixel_size;
	src_width *= pixel_size;
	// TODO: Optimize for small areas
	while (num_rows--) {
		std::memmove(dst, src, num_bytes);
		dst += dst_width;
		src += src_width;
	}
}

void pixmap::blit_unscaled_choose(
	PixelFormat const& dst_format,
	u8* dst,
	unsigned dst_width,
	UVec2 dst_pos,
	PixelFormat const& src_format,
	u8 const* src,
	unsigned src_width,
	UVec4 src_rect
) {
	if (pixel_format::compare_equal(dst_format, src_format)) {
		pixmap::blit_unscaled_copy(
			dst_format,
			dst, dst_width, dst_pos,
			src, src_width, src_rect
		);
	} else {
		TOGO_ASSERT(false, "TODO: unequal pixmap formats");
	}
}

} // namespace togo
<|endoftext|>