<commit_before>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>

namespace
{
    const auto whitespace = " \t\n";
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count) noexcept
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

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

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index - start_index));
        start_index = std::min(end_index, s.size()) + delim.size();
        ++split_count;
    }

    if(start_index <= s.size())
    {
        result.push_back(s.substr(start_index));
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning) noexcept
{
    return (beginning.size() <= s.size()) && std::equal(beginning.begin(), beginning.end(), s.begin());
}

bool String::ends_with(const std::string& s, const std::string& ending) noexcept
{
    return (ending.size() <= s.size()) && std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}

std::string String::trim_outer_whitespace(const std::string& s) noexcept
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s) noexcept
{
    std::string s2 = trim_outer_whitespace(s);
    std::replace_if(s2.begin(), s2.end(), [](auto c) { return String::contains(whitespace, c); }, ' ');
    std::string result;
    std::copy_if(s2.begin(), s2.end(), std::back_inserter(result),
                 [&result](auto c)
                 {
                     return  c != ' ' || result.back() != ' ';
                 });

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment) noexcept
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    try
    {
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::extract_delimited_text(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_split = split(str, start, 1);
    if(start_split.size() != 2)
    {
        throw std::invalid_argument("Starting delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    auto start_of_inside = start_split[1];
    auto inside_split = split(start_of_inside, end, 1);
    if(inside_split.size() != 2)
    {
        throw std::invalid_argument("Ending delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    return inside_split[0];
}

std::string String::lowercase(std::string s) noexcept
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}

std::string String::round_to_precision(double x, double precision) noexcept
{
    auto result = std::to_string(std::round(x/precision)*precision);
    auto last_significant_index = result.find_last_not_of('0');
    if(last_significant_index == std::string::npos)
    {
        return result;
    }
    else
    {
        if(result[last_significant_index] == '.')
        {
            if(precision < 1.0)
            {
                // Include ".0"
                return result.substr(0, last_significant_index + 2);
            }
            else
            {
                // Precision is greater than one, so delete fractional part
                return result.substr(0, last_significant_index);
            }
        }
        else
        {
            // Include first non-significant digit
            return result.substr(0, last_significant_index + 1);
        }
    }
}

size_t String::string_to_size_t(const std::string& s)
{
    size_t result;
    std::string remainder;
    std::istringstream(s) >> result >> remainder;
    if( ! String::trim_outer_whitespace(remainder).empty())
    {
        throw std::invalid_argument("Non-numeric characters in string: " + s);
    }
    return result;
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format) noexcept
{
    auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}
<commit_msg>std::mismatch does not need a size check<commit_after>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>

namespace
{
    const auto whitespace = " \t\n";
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count) noexcept
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

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

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index - start_index));
        start_index = std::min(end_index, s.size()) + delim.size();
        ++split_count;
    }

    if(start_index <= s.size())
    {
        result.push_back(s.substr(start_index));
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning) noexcept
{
    return std::mismatch(beginning.begin(), beginning.end(), s.begin(), s.end()).first == beginning.end();
}

bool String::ends_with(const std::string& s, const std::string& ending) noexcept
{
    return std::mismatch(ending.rbegin(), ending.rend(), s.rbegin(), s.rend()).first == ending.rend();
}

std::string String::trim_outer_whitespace(const std::string& s) noexcept
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s) noexcept
{
    std::string s2 = trim_outer_whitespace(s);
    std::replace_if(s2.begin(), s2.end(), [](auto c) { return String::contains(whitespace, c); }, ' ');
    std::string result;
    std::copy_if(s2.begin(), s2.end(), std::back_inserter(result),
                 [&result](auto c)
                 {
                     return  c != ' ' || result.back() != ' ';
                 });

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment) noexcept
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    try
    {
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::extract_delimited_text(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_split = split(str, start, 1);
    if(start_split.size() != 2)
    {
        throw std::invalid_argument("Starting delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    auto start_of_inside = start_split[1];
    auto inside_split = split(start_of_inside, end, 1);
    if(inside_split.size() != 2)
    {
        throw std::invalid_argument("Ending delimiter not found in \"" + str + "\": " + start + " " + end);
    }
    return inside_split[0];
}

std::string String::lowercase(std::string s) noexcept
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}

std::string String::round_to_precision(double x, double precision) noexcept
{
    auto result = std::to_string(std::round(x/precision)*precision);
    auto last_significant_index = result.find_last_not_of('0');
    if(last_significant_index == std::string::npos)
    {
        return result;
    }
    else
    {
        if(result[last_significant_index] == '.')
        {
            if(precision < 1.0)
            {
                // Include ".0"
                return result.substr(0, last_significant_index + 2);
            }
            else
            {
                // Precision is greater than one, so delete fractional part
                return result.substr(0, last_significant_index);
            }
        }
        else
        {
            // Include first non-significant digit
            return result.substr(0, last_significant_index + 1);
        }
    }
}

size_t String::string_to_size_t(const std::string& s)
{
    size_t result;
    std::string remainder;
    std::istringstream(s) >> result >> remainder;
    if( ! String::trim_outer_whitespace(remainder).empty())
    {
        throw std::invalid_argument("Non-numeric characters in string: " + s);
    }
    return result;
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format) noexcept
{
    auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}
<|endoftext|>


<commit_before>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>

namespace String
{
    namespace
    {
        const auto whitespace = " \t\n";
    }
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count)
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

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

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index - start_index));
        start_index = std::min(end_index, s.size()) + delim.size();
        ++split_count;
    }

    if(start_index <= s.size())
    {
        result.push_back(s.substr(start_index));
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning)
{
    return (beginning.size() <= s.size()) && std::equal(beginning.begin(), beginning.end(), s.begin());
}

bool String::ends_with(const std::string& s, const std::string& ending)
{
    return (ending.size() <= s.size()) && std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}

std::string String::trim_outer_whitespace(const std::string& s)
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s)
{
    std::string s2 = trim_outer_whitespace(s);
    std::replace_if(s2.begin(), s2.end(), [](auto c) { return String::contains(whitespace, c); }, ' ');
    std::string result;
    std::copy_if(s2.begin(), s2.end(), std::back_inserter(result),
                 [&result](auto c)
                 {
                     return  c != ' ' || result.back() != ' ';

                 });

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment)
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    try
    {
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::lowercase(std::string s)
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}

std::string String::round_to_precision(double x, double precision)
{
    auto result = std::to_string(std::round(x/precision)*precision);
    auto last_significant_index = result.find_last_not_of('0');
    if(last_significant_index == std::string::npos)
    {
        return result;
    }
    else
    {
        if(result[last_significant_index] == '.')
        {
            if(precision < 1.0)
            {
                // Include ".0"
                return result.substr(0, last_significant_index + 2);
            }
            else
            {
                // Precision is greater than one, so delete fractional part
                return result.substr(0, last_significant_index);
            }
        }
        else
        {
            // Include first non-significant digit
            return result.substr(0, last_significant_index + 1);
        }
    }
}

size_t String::string_to_size_t(const std::string& s)
{
    size_t characters_used;

    auto result =
    #ifdef __linux__
        std::stoul(s, &characters_used);
    #elif defined(_WIN64)
        std::stoull(s, &characters_used);
    #else
        std::stoi(s, &characters_used);
    #endif

    if( ! String::trim_outer_whitespace(s.substr(characters_used)).empty())
    {
        throw std::invalid_argument("Non-numeric characters in string: " + s);
    }

    return result;
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format)
{
    auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}
<commit_msg>Simplify String::string_to_size_t()<commit_after>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <cmath>

namespace String
{
    namespace
    {
        const auto whitespace = " \t\n";
    }
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count)
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

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

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index - start_index));
        start_index = std::min(end_index, s.size()) + delim.size();
        ++split_count;
    }

    if(start_index <= s.size())
    {
        result.push_back(s.substr(start_index));
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning)
{
    return (beginning.size() <= s.size()) && std::equal(beginning.begin(), beginning.end(), s.begin());
}

bool String::ends_with(const std::string& s, const std::string& ending)
{
    return (ending.size() <= s.size()) && std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}

std::string String::trim_outer_whitespace(const std::string& s)
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s)
{
    std::string s2 = trim_outer_whitespace(s);
    std::replace_if(s2.begin(), s2.end(), [](auto c) { return String::contains(whitespace, c); }, ' ');
    std::string result;
    std::copy_if(s2.begin(), s2.end(), std::back_inserter(result),
                 [&result](auto c)
                 {
                     return  c != ' ' || result.back() != ' ';

                 });

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment)
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::invalid_argument("\"" + str + "\" is missing a comment delimiter: " + start + end);
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::invalid_argument("\"" + str + "\" contains bad comment delimiters: " + start + end);
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    try
    {
        return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
    }
    catch(const std::invalid_argument& e)
    {
        throw std::invalid_argument(e.what() + std::string("\nOriginal line: ") + str);
    }
}

std::string String::lowercase(std::string s)
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}

std::string String::round_to_precision(double x, double precision)
{
    auto result = std::to_string(std::round(x/precision)*precision);
    auto last_significant_index = result.find_last_not_of('0');
    if(last_significant_index == std::string::npos)
    {
        return result;
    }
    else
    {
        if(result[last_significant_index] == '.')
        {
            if(precision < 1.0)
            {
                // Include ".0"
                return result.substr(0, last_significant_index + 2);
            }
            else
            {
                // Precision is greater than one, so delete fractional part
                return result.substr(0, last_significant_index);
            }
        }
        else
        {
            // Include first non-significant digit
            return result.substr(0, last_significant_index + 1);
        }
    }
}

size_t String::string_to_size_t(const std::string& s)
{
    size_t result;
    std::string remainder;
    std::istringstream(s) >> result >> remainder;
    if( ! String::trim_outer_whitespace(remainder).empty())
    {
        throw std::invalid_argument("Non-numeric characters in string: " + s);
    }
    return result;
}

std::string String::date_and_time_format(const std::chrono::system_clock::time_point& point_in_time,
                                         const std::string& format)
{
    auto time_c = std::chrono::system_clock::to_time_t(point_in_time);
    std::tm time_out;
#ifdef _WIN32
    localtime_s(&time_out, &time_c);
#elif defined(__linux__)
    localtime_r(&time_c, &time_out);
#endif
    auto ss = std::ostringstream{};
    ss << std::put_time(&time_out, format.c_str());
    return ss.str();
}
<|endoftext|>


<commit_before>#include "Window.h"
#include <cstdio>

#include <iostream> // TODO: remove iostream

#ifdef WINDOWS
LRESULT CALLBACK Jatta::Window::windowProcedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
    LONG_PTR ptr = GetWindowLongPtr(handle, GWLP_USERDATA);
    Window* window = (Window*)ptr;
    if (ptr != 0)
    {
        //window->getOnMessage()();
    }
    switch (message)
    {
        case WM_CLOSE:
        if (window != 0)
        {
            window->close();
        }
        return 0;
        case WM_KEYDOWN:
        window->getInput()->getKeyData()[wParam] = true;
        return 0;
        case WM_KEYUP:
        window->getInput()->getKeyData()[wParam] = false;
        return 0;
        case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(handle, message, wParam, lParam);
}
#endif

#ifdef LINUX
bool Jatta::Window::initialized = false;

static int __jatta_windows_error_handler(Display* display, XErrorEvent* event)
{
    // TODO: something?
}
#endif

Jatta::Window::Window() : input(new Input(this))
{
#   ifdef LINUX
    if (!initialized)
    {
        XSetErrorHandler(__jatta_windows_error_handler);
        initialized = true;
    }
#   endif
}

Jatta::Window::~Window()
{
    close();
}

#ifdef WINDOWS
HWND Jatta::Window::_getHandle()
{
    return handle;
}
#endif

#ifdef LINUX
Display* Jatta::Window::_getDisplay()
{
    return display;
}

::Window Jatta::Window::_getHandle()
{
    return handle;
}
#endif

std::shared_ptr<Jatta::Input> Jatta::Window::getInput()
{
    return input;
}

void Jatta::Window::create(const WindowStyle& style)
{
#   ifdef WINDOWS
    // Generate a unique class name for this window
    static int windowCounter = 0;
    strcpy(className, "JATTA_");
    sprintf(className + 6, "%d", windowCounter++);

    // Create the window class
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = windowProcedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(nullptr);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(style.backgroundColor.r, style.backgroundColor.g, style.backgroundColor.b));
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;
    wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);

    // Register the window class
    if (!RegisterClassEx(&wc))
    {
        // TODO: throw exception
    }

    this->style = WS_OVERLAPPEDWINDOW;

    RECT windowRect = {0, 0, (long)style.width, (long)style.height};
    AdjustWindowRectEx(&windowRect, this->style, false, WS_EX_CLIENTEDGE);

    handle = CreateWindowEx(WS_EX_CLIENTEDGE, className, style.title.c_str(), this->style, CW_USEDEFAULT, CW_USEDEFAULT, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, GetModuleHandle(nullptr), NULL);

    if (handle == NULL)
    {
        // TODO: throw exception
    }

    SetWindowLongPtr(handle, GWLP_USERDATA, (LONG_PTR)this);

    ShowWindow(handle, SW_SHOW);
#   endif

#   ifdef LINUX
    std::cout << (int)style.backgroundColor.r << ", " << (int)style.backgroundColor.g << ", " << (int)style.backgroundColor.b << std::endl;
    this->display = XOpenDisplay(nullptr);
    int screen = DefaultScreen(this->display);
    Visual* visual = DefaultVisual(this->display, screen);
    int depth = DefaultDepth(this->display, screen);
    XSetWindowAttributes attributes;
    attributes.background_pixel = style.backgroundColor.b | (style.backgroundColor.g << 8) | (style.backgroundColor.r << 16);//XWhitePixel(display, screen);
    attributes.border_pixel = XBlackPixel(this->display, screen);
    attributes.override_redirect = 0;
    this->handle = XCreateWindow(this->display, XRootWindow(this->display, screen), 200, 200, style.width, style.height, 5, depth, InputOutput, visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect, &attributes);
    Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", true);
    XSetWMProtocols(this->display, this->handle, &wmDelete, 1);
    XSelectInput(this->display, this->handle, ExposureMask | ButtonPressMask | KeyPressMask);
    XMapWindow(this->display, this->handle);
    XFlush(this->display);
#   endif
}

void Jatta::Window::close()
{
#   ifdef WINDOWS
    if (IsWindow(handle))
    {
        DestroyWindow(handle);
    }
    handle = 0;
#   endif

#   ifdef LINUX
    if (isOpen())
    {
        XDestroyWindow(display, handle);
        XCloseDisplay(display);
    }
    handle = 0;
    display = nullptr;
#   endif
}

void Jatta::Window::update()
{
#   ifdef WINDOWS
    MSG Msg;
    while (PeekMessage(&Msg, handle, 0, 0, PM_REMOVE) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
#   endif

#   ifdef LINUX
    if (display && handle)
    {
        while (XPending(display))
        {
            XEvent event;
            XNextEvent(display, &event);
            switch (event.type)
            {
                case 33:
                    XDestroyWindow(display, handle);
                case DestroyNotify:
                    close();
                    return;
            }
        }
    }
#   endif
}

bool Jatta::Window::isOpen()
{
#   ifdef WINDOWS
    return IsWindow(handle);
#   endif

#   ifdef LINUX
    return display && handle;
#   endif
}

unsigned int Jatta::Window::getWidth()
{
#   ifdef WINDOWS
    RECT rect = {0, 0, 0, 0};
    AdjustWindowRect(&rect, style, false);
    int borders = -rect.left + rect.right;
    GetWindowRect(handle, &rect);
    return rect.right - rect.left - borders;
#   endif
}

unsigned int Jatta::Window::getHeight()
{
#   ifdef WINDOWS
    RECT rect = {0, 0, 0, 0};
    AdjustWindowRect(&rect, style, false);
    int borders = -rect.top + rect.bottom;
    GetWindowRect(handle, &rect);
    return rect.bottom - rect.top - borders;
#   endif
}
<commit_msg>Windowy stuff<commit_after>#include "Window.h"
#include <cstdio>

#include <iostream> // TODO: remove iostream

#ifdef WINDOWS
LRESULT CALLBACK Jatta::Window::windowProcedure(HWND handle, UINT message, WPARAM wParam, LPARAM lParam)
{
    LONG_PTR ptr = GetWindowLongPtr(handle, GWLP_USERDATA);
    Window* window = (Window*)ptr;
    if (ptr != 0)
    {
        //window->getOnMessage()();
    }
    switch (message)
    {
        case WM_CLOSE:
        if (window != 0)
        {
            window->close();
        }
        return 0;
        case WM_KEYDOWN:
        window->getInput()->getKeyData()[wParam] = true;
        return 0;
        case WM_KEYUP:
        window->getInput()->getKeyData()[wParam] = false;
        return 0;
        case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(handle, message, wParam, lParam);
}
#endif

#ifdef LINUX
bool Jatta::Window::initialized = false;

static int __jatta_windows_error_handler(Display* display, XErrorEvent* event)
{
    // TODO: something?
}
#endif

Jatta::Window::Window() : input(new Input(this))
{
#   ifdef LINUX
    if (!initialized)
    {
        XSetErrorHandler(__jatta_windows_error_handler);
        initialized = true;
    }
#   endif
}

Jatta::Window::~Window()
{
    close();
}

#ifdef WINDOWS
HWND Jatta::Window::_getHandle()
{
    return handle;
}
#endif

#ifdef LINUX
Display* Jatta::Window::_getDisplay()
{
    return display;
}

::Window Jatta::Window::_getHandle()
{
    return handle;
}
#endif

std::shared_ptr<Jatta::Input> Jatta::Window::getInput()
{
    return input;
}

void Jatta::Window::create(const WindowStyle& style)
{
#   ifdef WINDOWS
    // Generate a unique class name for this window
    static int windowCounter = 0;
    strcpy(className, "JATTA_");
    sprintf(className + 6, "%d", windowCounter++);

    // Create the window class
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = windowProcedure;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(nullptr);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = CreateSolidBrush(RGB(style.backgroundColor.r, style.backgroundColor.g, style.backgroundColor.b));
    wc.lpszMenuName = NULL;
    wc.lpszClassName = className;
    wc.hIconSm = LoadIcon(nullptr, IDI_APPLICATION);

    // Register the window class
    if (!RegisterClassEx(&wc))
    {
        // TODO: throw exception
    }

    this->style = WS_OVERLAPPEDWINDOW;

    RECT windowRect = {0, 0, (long)style.width, (long)style.height};
    AdjustWindowRectEx(&windowRect, this->style, false, WS_EX_CLIENTEDGE);

    handle = CreateWindowEx(WS_EX_CLIENTEDGE, className, style.title.c_str(), this->style, CW_USEDEFAULT, CW_USEDEFAULT, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, NULL, NULL, GetModuleHandle(nullptr), NULL);

    if (handle == NULL)
    {
        // TODO: throw exception
    }

    SetWindowLongPtr(handle, GWLP_USERDATA, (LONG_PTR)this);

    ShowWindow(handle, SW_SHOW);
#   endif

#   ifdef LINUX
    std::cout << (int)style.backgroundColor.r << ", " << (int)style.backgroundColor.g << ", " << (int)style.backgroundColor.b << std::endl;
    this->display = XOpenDisplay(nullptr);
    int screen = DefaultScreen(this->display);
    Visual* visual = DefaultVisual(this->display, screen);
    int depth = DefaultDepth(this->display, screen);
    XSetWindowAttributes attributes;
    attributes.background_pixel = style.backgroundColor.b | (style.backgroundColor.g << 8) | (style.backgroundColor.r << 16);//XWhitePixel(display, screen);
    attributes.border_pixel = XBlackPixel(this->display, screen);
    attributes.override_redirect = 0;
    this->handle = XCreateWindow(this->display, XRootWindow(this->display, screen), 200, 200, style.width, style.height, 5, depth, InputOutput, visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect, &attributes);
    Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", true);
    XSetWMProtocols(this->display, this->handle, &wmDelete, 1);
    XSelectInput(this->display, this->handle, ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
    XMapWindow(this->display, this->handle);
    XFlush(this->display);
#   endif
}

void Jatta::Window::close()
{
#   ifdef WINDOWS
    if (IsWindow(handle))
    {
        DestroyWindow(handle);
    }
    handle = 0;
#   endif

#   ifdef LINUX
    if (isOpen())
    {
        XDestroyWindow(display, handle);
        XCloseDisplay(display);
    }
    handle = 0;
    display = nullptr;
#   endif
}

void Jatta::Window::update()
{
#   ifdef WINDOWS
    MSG Msg;
    while (PeekMessage(&Msg, handle, 0, 0, PM_REMOVE) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
#   endif

#   ifdef LINUX
    if (display && handle)
    {
        while (XPending(display))
        {
            XEvent event;
            XNextEvent(display, &event);
            switch (event.type)
            {
                case 33:
                    XDestroyWindow(display, handle);
                case DestroyNotify:
                    close();
                    return;
                case KeyPress:
                    getInput()->getKeyData()[getInput()->getKeyFromLayout(event.xkey.keycode)] = true;
                    break;
                case KeyRelease:
                    bool released = true;
                    if (XEventsQueued(display, QueuedAfterReading))
                    {
                        XEvent nextEvent;
                        XPeekEvent(display, &nextEvent);
                        if (nextEvent.type == KeyPress && nextEvent.xkey.time == event.xkey.time && nextEvent.xkey.keycode == event.xkey.keycode)
                        {
                            released = false;
                        }
                    }
                    if (released)
                    {
                        getInput()->getKeyData()[getInput()->getKeyFromLayout(event.xkey.keycode)] = false;
                    }
                    break;
            }
        }
    }
#   endif
}

bool Jatta::Window::isOpen()
{
#   ifdef WINDOWS
    return IsWindow(handle);
#   endif

#   ifdef LINUX
    return display && handle;
#   endif
}

unsigned int Jatta::Window::getWidth()
{
#   ifdef WINDOWS
    RECT rect = {0, 0, 0, 0};
    AdjustWindowRect(&rect, style, false);
    int borders = -rect.left + rect.right;
    GetWindowRect(handle, &rect);
    return rect.right - rect.left - borders;
#   endif

#   ifdef LINUX
    XWindowAttributes attributes;
    XGetWindowAttributes(display, handle, &attributes);
    return attributes.width;
#   endif
}

unsigned int Jatta::Window::getHeight()
{
#   ifdef WINDOWS
    RECT rect = {0, 0, 0, 0};
    AdjustWindowRect(&rect, style, false);
    int borders = -rect.top + rect.bottom;
    GetWindowRect(handle, &rect);
    return rect.bottom - rect.top - borders;
#   endif

#   ifdef LINUX
    XWindowAttributes attributes;
    XGetWindowAttributes(display, handle, &attributes);
    return attributes.height;
#   endif
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * preferences_widgets.cpp : Widgets for preferences displays
 ****************************************************************************
 * Copyright (C) 2000-2005 the VideoLAN team
 * $Id: wxwidgets.cpp 15731 2006-05-25 14:43:53Z zorglub $
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "components/preferences_widgets.hpp"

#include <QLineEdit>
#include <QString>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QVariant>
#include <QComboBox>

ConfigControl::ConfigControl( vlc_object_t *_p_this, module_config_t *p_item,
                              QWidget *_parent ) : QWidget( _parent ),
                              p_this( _p_this ), _name( p_item->psz_name )
{
}

ConfigControl::~ConfigControl() {}

ConfigControl *ConfigControl::createControl( vlc_object_t *p_this,
                                    module_config_t *p_item, QWidget *parent )
{
    ConfigControl *p_control = NULL;
    if( p_item->psz_current ) return NULL;

    switch( p_item->i_type )
    {
    case CONFIG_ITEM_MODULE:
        p_control = new ModuleConfigControl( p_this, p_item, parent, false );
        break;
    case CONFIG_ITEM_MODULE_CAT:
        p_control = new ModuleConfigControl( p_this, p_item, parent, true );
        break;
    case CONFIG_ITEM_STRING:
        if( !p_item->i_list )
            p_control = new StringConfigControl( p_this, p_item, parent,false );
        else
            abort();
        break;
    default:
        break;
    }
    return p_control;
}

/**************************************************************************
 * String-based controls
 *************************************************************************/

/*********** String **************/
StringConfigControl::StringConfigControl( vlc_object_t *_p_this,
                     module_config_t *p_item, QWidget *_parent, bool pwd )
                           : VStringConfigControl( _p_this, p_item, _parent )
{
    QLabel *label = new QLabel( p_item->psz_text );
    text = new QLineEdit( p_item->psz_value );
    text->setToolTip( p_item->psz_longtext );
    label->setToolTip( p_item->psz_longtext );

    QHBoxLayout *layout = new QHBoxLayout();
    layout->addWidget( label ); layout->addWidget( text );
    setLayout( layout );
}

StringConfigControl::~StringConfigControl() {}

QString StringConfigControl::getValue() { return text->text(); };

/********* Module **********/
ModuleConfigControl::ModuleConfigControl( vlc_object_t *_p_this,
                module_config_t *p_item, QWidget *_parent,
                bool bycat ) : VStringConfigControl( _p_this, p_item, _parent )
{
    vlc_list_t *p_list;
    module_t *p_parser;

    QLabel *label = new QLabel( p_item->psz_text );
    combo = new QComboBox();
    combo->setEditable( false );

    /* build a list of available modules */
    p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE );
    combo->addItem( "Default" );
    for( int i_index = 0; i_index < p_list->i_count; i_index++ )
    {
        p_parser = (module_t *)p_list->p_values[i_index].p_object ;

        if( bycat )
        {
            if( !strcmp( p_parser->psz_object_name, "main" ) ) continue;

            module_config_t *p_config = p_parser->p_config;
            if( p_config ) do
            {
                /* Hack: required subcategory is stored in i_min */
                if( p_config->i_type == CONFIG_SUBCATEGORY &&
                    p_config->i_value == p_item->i_min )
                    combo->addItem( p_parser->psz_longname,
                                    QVariant( p_parser->psz_object_name ) );
                if( p_item->psz_value && !strcmp( p_item->psz_value,
                                                  p_parser->psz_object_name) )
                    combo->setCurrentIndex( combo->count() - 1 );
            } while( p_config->i_type != CONFIG_HINT_END && p_config++ );
        }
        else if( !strcmp( p_parser->psz_capability, p_item->psz_type ) )
        {
            combo->addItem( p_parser->psz_longname,
                            QVariant( p_parser->psz_object_name ) );
            if( p_item->psz_value && !strcmp( p_item->psz_value,
                                              p_parser->psz_object_name) )
                combo->setCurrentIndex( combo->count() - 1 );
        }
    }
    vlc_list_release( p_list );
    combo->setToolTip( p_item->psz_longtext );
    label->setToolTip( p_item->psz_longtext );

    QHBoxLayout *layout = new QHBoxLayout();
    layout->addWidget( label ); layout->addWidget( combo );
    setLayout( layout );
}

ModuleConfigControl::~ModuleConfigControl() {};

QString ModuleConfigControl::getValue()
{
    return combo->itemData( combo->currentIndex() ).toString();
}
<commit_msg>Todo for prefs widgets<commit_after>/*****************************************************************************
 * preferences_widgets.cpp : Widgets for preferences displays
 ****************************************************************************
 * Copyright (C) 2000-2005 the VideoLAN team
 * $Id: wxwidgets.cpp 15731 2006-05-25 14:43:53Z zorglub $
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/**
 * Todo:
 *  - Finish implementation (see WX)
 *  - Improvements over WX
 *      - Password field implementation (through "pwd" bool param
 *      - Validator for modulelist
 *  - Implement update stuff using a general Updated signal
 */

#include "components/preferences_widgets.hpp"

#include <QLineEdit>
#include <QString>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QVariant>
#include <QComboBox>

ConfigControl::ConfigControl( vlc_object_t *_p_this, module_config_t *p_item,
                              QWidget *_parent ) : QWidget( _parent ),
                              p_this( _p_this ), _name( p_item->psz_name )
{
}

ConfigControl::~ConfigControl() {}

ConfigControl *ConfigControl::createControl( vlc_object_t *p_this,
                                    module_config_t *p_item, QWidget *parent )
{
    ConfigControl *p_control = NULL;
    if( p_item->psz_current ) return NULL;

    switch( p_item->i_type )
    {
    case CONFIG_ITEM_MODULE:
        p_control = new ModuleConfigControl( p_this, p_item, parent, false );
        break;
    case CONFIG_ITEM_MODULE_CAT:
        p_control = new ModuleConfigControl( p_this, p_item, parent, true );
        break;
    case CONFIG_ITEM_STRING:
        if( !p_item->i_list )
            p_control = new StringConfigControl( p_this, p_item, parent,false );
        else
            abort();
        break;
    default:
        break;
    }
    return p_control;
}

/**************************************************************************
 * String-based controls
 *************************************************************************/

/*********** String **************/
StringConfigControl::StringConfigControl( vlc_object_t *_p_this,
                     module_config_t *p_item, QWidget *_parent, bool pwd )
                           : VStringConfigControl( _p_this, p_item, _parent )
{
    QLabel *label = new QLabel( p_item->psz_text );
    text = new QLineEdit( p_item->psz_value );
    text->setToolTip( p_item->psz_longtext );
    label->setToolTip( p_item->psz_longtext );

    QHBoxLayout *layout = new QHBoxLayout();
    layout->addWidget( label ); layout->addWidget( text );
    setLayout( layout );
}

StringConfigControl::~StringConfigControl() {}

QString StringConfigControl::getValue() { return text->text(); };

/********* Module **********/
ModuleConfigControl::ModuleConfigControl( vlc_object_t *_p_this,
                module_config_t *p_item, QWidget *_parent,
                bool bycat ) : VStringConfigControl( _p_this, p_item, _parent )
{
    vlc_list_t *p_list;
    module_t *p_parser;

    QLabel *label = new QLabel( p_item->psz_text );
    combo = new QComboBox();
    combo->setEditable( false );

    /* build a list of available modules */
    p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE );
    combo->addItem( "Default" );
    for( int i_index = 0; i_index < p_list->i_count; i_index++ )
    {
        p_parser = (module_t *)p_list->p_values[i_index].p_object ;

        if( bycat )
        {
            if( !strcmp( p_parser->psz_object_name, "main" ) ) continue;

            module_config_t *p_config = p_parser->p_config;
            if( p_config ) do
            {
                /* Hack: required subcategory is stored in i_min */
                if( p_config->i_type == CONFIG_SUBCATEGORY &&
                    p_config->i_value == p_item->i_min )
                    combo->addItem( p_parser->psz_longname,
                                    QVariant( p_parser->psz_object_name ) );
                if( p_item->psz_value && !strcmp( p_item->psz_value,
                                                  p_parser->psz_object_name) )
                    combo->setCurrentIndex( combo->count() - 1 );
            } while( p_config->i_type != CONFIG_HINT_END && p_config++ );
        }
        else if( !strcmp( p_parser->psz_capability, p_item->psz_type ) )
        {
            combo->addItem( p_parser->psz_longname,
                            QVariant( p_parser->psz_object_name ) );
            if( p_item->psz_value && !strcmp( p_item->psz_value,
                                              p_parser->psz_object_name) )
                combo->setCurrentIndex( combo->count() - 1 );
        }
    }
    vlc_list_release( p_list );
    combo->setToolTip( p_item->psz_longtext );
    label->setToolTip( p_item->psz_longtext );

    QHBoxLayout *layout = new QHBoxLayout();
    layout->addWidget( label ); layout->addWidget( combo );
    setLayout( layout );
}

ModuleConfigControl::~ModuleConfigControl() {};

QString ModuleConfigControl::getValue()
{
    return combo->itemData( combo->currentIndex() ).toString();
}
<|endoftext|>


<commit_before>#ifndef INTRSPHEREOBB_INL
#define INTRSPHEREOBB_INL
#include <sofa/component/collision/IntrSphereOBB.h>

namespace sofa{
namespace component{
namespace collision{

template <class TDataTypes,class TDataTypes2>
TIntrSphereOBB<TDataTypes,TDataTypes2>::TIntrSphereOBB (const IntrSph& sphere, const Box & box) : _sph(&sphere),mBox(&box){this->_is_colliding = false;}

template <class TDataTypes,class TDataTypes2>
bool TIntrSphereOBB<TDataTypes,TDataTypes2>::Find(){
    _is_colliding = true;

    _pt_on_second = mBox->center();
    _pt_on_first = _sph->center();
    Vec<3,Real> centeredPt = _pt_on_first - _pt_on_second;

    //projecting the center of the sphere on the OBB
    Real coord_i;
    for(int i = 0 ; i < 3 ; ++i){
        coord_i = mBox->axis(i) * centeredPt;//the i-th coordinate of the sphere center in the OBB's local frame

        if(coord_i < -mBox->extent(i)){//if the i-th coordinate is less than -mBox->extent(i), we are outside of the OBB by the negative sens, so the i-th nearest coordinate to the sphere center
                                       //coordinate is -mBox->extent
            _is_colliding = false;
            coord_i = -mBox->extent(i);
        }
        else if(coord_i > mBox->extent(i)){//same idea in the positive sens
            _is_colliding = false;
            coord_i = mBox->extent(i);
        }

        _pt_on_second += coord_i * mBox->axis(i);
    }

    //The normal response which _sep_axis have the same direction than the one which goes from the sphere center to the OBB contact point.
    //If the sphere and the OBB are colliding
    _sep_axis = _pt_on_second - _pt_on_first;
    IntrUtil<Real>::normalize(_sep_axis);

    _pt_on_first += _sph->r() * _sep_axis;

    if((!_is_colliding) && ((_pt_on_second - _pt_on_first) * centeredPt > 0))
        _is_colliding = true;

    return true;
}

template <typename TDataTypes,typename TDataTypes2>
typename TIntrSphereOBB<TDataTypes,TDataTypes2>::Real TIntrSphereOBB<TDataTypes,TDataTypes2>::distance()const{
    return fabs(_sep_axis * (_pt_on_first - _pt_on_second));
}

}
}
}

#endif // INTRSPHEREOBB_INL
<commit_msg>r9647/sofa : Managing deep OBB/Sphere collision<commit_after>#ifndef INTRSPHEREOBB_INL
#define INTRSPHEREOBB_INL
#include <sofa/component/collision/IntrSphereOBB.h>

namespace sofa{
namespace component{
namespace collision{

template <class TDataTypes,class TDataTypes2>
TIntrSphereOBB<TDataTypes,TDataTypes2>::TIntrSphereOBB (const IntrSph& sphere, const Box & box) : _sph(&sphere),mBox(&box){this->_is_colliding = false;}

template <class TDataTypes,class TDataTypes2>
bool TIntrSphereOBB<TDataTypes,TDataTypes2>::Find(){
    _is_colliding = true;

    _pt_on_second = mBox->center();
    _pt_on_first = _sph->center();
    const Vec<3,Real> centeredPt = _pt_on_first - _pt_on_second;

    //projecting the center of the sphere on the OBB
    Real coord_i;
    for(int i = 0 ; i < 3 ; ++i){
        coord_i = mBox->axis(i) * centeredPt;//the i-th coordinate of the sphere center in the OBB's local frame

        if(coord_i < -mBox->extent(i)){//if the i-th coordinate is less than -mBox->extent(i), we are outside of the OBB by the negative sens, so the i-th nearest coordinate to the sphere center
                                       //coordinate is -mBox->extent
            _is_colliding = false;
            coord_i = -mBox->extent(i);
        }
        else if(coord_i > mBox->extent(i)){//same idea in the positive sens
            _is_colliding = false;
            coord_i = mBox->extent(i);
        }

        _pt_on_second += coord_i * mBox->axis(i);
    }

    if(_is_colliding){//need to to replace the obb contact point on its surface when the sphere center is in the OBB
        int num_axis = 0;
        Real alpha = mBox->axis(0) * centeredPt/mBox->extent(0);
        for(int i = 1 ; i < 3 ; ++i){
            double temp = mBox->axis(i) * centeredPt/mBox->extent(i);
            if(fabs(temp) > fabs(alpha)){
                alpha = temp;
                num_axis = i;
            }
        }

        _sep_axis = mBox->axis(num_axis);

        if(_sep_axis * (mBox->center() - _sph->center()) < 0)
            _sep_axis *= (Real)(-1.0);

        _pt_on_first = _sph->center() + _sph->r() * _sep_axis;

        if(alpha > 0){
            _pt_on_second = mBox->center() + mBox->extent(num_axis) * mBox->axis(num_axis) + centeredPt;
        }
        else{
            _pt_on_second = mBox->center() - mBox->extent(num_axis) * mBox->axis(num_axis) + centeredPt;
        }

        _pt_on_second[num_axis] -= centeredPt[num_axis];
    }
    else{
        //The normal response which _sep_axis have the same direction than the one which goes from the sphere center to the OBB contact point.
        //If the sphere and the OBB are colliding
        _sep_axis = _pt_on_second - _pt_on_first;
        IntrUtil<Real>::normalize(_sep_axis);

        _pt_on_first += _sph->r() * _sep_axis;

        if((!_is_colliding) && ((_pt_on_second - _pt_on_first) * centeredPt > 0))
            _is_colliding = true;
    }

    return true;
}

template <typename TDataTypes,typename TDataTypes2>
typename TIntrSphereOBB<TDataTypes,TDataTypes2>::Real TIntrSphereOBB<TDataTypes,TDataTypes2>::distance()const{
    return fabs(_sep_axis * (_pt_on_first - _pt_on_second));
}

}
}
}

#endif // INTRSPHEREOBB_INL
<|endoftext|>


<commit_before>/*
c++ xapiand.cc server.cc threadpool.cc ../../net/length.cc -lev `xapian-config-1.3 --libs` `xapian-config-1.3 --cxxflags` -I../../ -I../../common -DXAPIAN_LIB_BUILD -oxapiand
*/

#include <stdlib.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include "utils.h"
#include "config.h"
#include "server.h"

int bind_http(int http_port)
{
	int optval = 1;
	struct sockaddr_in addr;

	int http_sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(http_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(http_port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(http_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		LOG_CONN((void *)NULL, "ERROR: http bind error (sock=%d): %s\n", http_sock, strerror(errno));
		close(http_sock);
		http_sock = -1;
	} else {
		LOG_CONN((void *)NULL, "Listening http protocol on port %d\n", http_port);
		fcntl(http_sock, F_SETFL, fcntl(http_sock, F_GETFL, 0) | O_NONBLOCK);

		listen(http_sock, 5);
	}

	return http_sock;
}

int bind_binary(int binary_port)
{
	int optval = 1;
	struct sockaddr_in addr;

	int binary_sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(binary_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(binary_port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(binary_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		LOG_CONN((void *)NULL, "ERROR: binary bind error (sock=%d): %s\n", binary_sock, strerror(errno));
		close(binary_sock);
		binary_sock = -1;
	} else {
		LOG_CONN((void *)NULL, "Listening binary protocol on port %d\n", binary_port);
		fcntl(binary_sock, F_SETFL, fcntl(binary_sock, F_GETFL, 0) | O_NONBLOCK);

		listen(binary_sock, 5);
	}

	return binary_sock;
}

int main(int argc, char **argv)
{
	int http_port = XAPIAND_HTTP_PORT_DEFAULT;
	int binary_port = XAPIAND_BINARY_PORT_DEFAULT;

	if (argc > 2) {
		http_port = atoi(argv[1]);
		binary_port = atoi(argv[2]);
	}

	LOG((void *)NULL, "Starting Xapiand.\n");

	int http_sock = bind_http(http_port);
	int binary_sock = bind_binary(binary_port);

	int tasks = 0;

	if (http_sock != -1 && binary_sock != -1) {
		ev::default_loop loop;
		if (tasks) {
			ThreadPool thread_pool(tasks);
			for (int i = 0; i < tasks; i++) {
				thread_pool.addTask(new XapiandServer(NULL, http_sock, binary_sock));
			}

			loop.run();

			LOG_OBJ((void *)NULL, "Waiting for threads...\n");

			thread_pool.finish();
			thread_pool.join();
		} else {
			XapiandServer * server = new XapiandServer(&loop, http_sock, binary_sock);
			server->run();
		}
	}

	if (http_sock != -1) {
		close(http_sock);
	}

	if (binary_sock != -1) {
		close(binary_sock);
	}

	LOG((void *)NULL, "Done with all work!\n");

	return 0;
}
<commit_msg>Bound socket options stolen from memcache server<commit_after>/*
c++ xapiand.cc server.cc threadpool.cc ../../net/length.cc -lev `xapian-config-1.3 --libs` `xapian-config-1.3 --cxxflags` -I../../ -I../../common -DXAPIAN_LIB_BUILD -oxapiand
*/

#include <stdlib.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h> /* for TCP_NODELAY */
#include <sys/socket.h>

#include "utils.h"
#include "config.h"
#include "server.h"

int bind_http(int http_port)
{
	int error;
	int optval = 1;
	struct sockaddr_in addr;
	struct linger ling = {0, 0};

	int http_sock = socket(PF_INET, SOCK_STREAM, 0);

	setsockopt(http_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
	error = setsockopt(http_sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, sizeof(optval));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", http_sock, strerror(errno));

	error = setsockopt(http_sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", http_sock, strerror(errno));

	error = setsockopt(http_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&optval, sizeof(optval));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", http_sock, strerror(errno));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(http_port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(http_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		LOG_CONN((void *)NULL, "ERROR: http bind error (sock=%d): %s\n", http_sock, strerror(errno));
		close(http_sock);
		http_sock = -1;
	} else {
		LOG_CONN((void *)NULL, "Listening http protocol on port %d\n", http_port);
		fcntl(http_sock, F_SETFL, fcntl(http_sock, F_GETFL, 0) | O_NONBLOCK);

		listen(http_sock, 5);
	}

	return http_sock;
}

int bind_binary(int binary_port)
{
	int error;
	int optval = 1;
	struct sockaddr_in addr;
	struct linger ling = {0, 0};

	int binary_sock = socket(PF_INET, SOCK_STREAM, 0);

	setsockopt(binary_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));
	error = setsockopt(binary_sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&optval, sizeof(optval));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", binary_sock, strerror(errno));

	error = setsockopt(binary_sock, SOL_SOCKET, SO_LINGER, (void *)&ling, sizeof(ling));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", binary_sock, strerror(errno));

	error = setsockopt(binary_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&optval, sizeof(optval));
	if (error != 0)
		LOG_CONN((void *)NULL, "ERROR: setsockopt (sock=%d): %s\n", binary_sock, strerror(errno));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(binary_port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(binary_sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		LOG_CONN((void *)NULL, "ERROR: binary bind error (sock=%d): %s\n", binary_sock, strerror(errno));
		close(binary_sock);
		binary_sock = -1;
	} else {
		LOG_CONN((void *)NULL, "Listening binary protocol on port %d\n", binary_port);
		fcntl(binary_sock, F_SETFL, fcntl(binary_sock, F_GETFL, 0) | O_NONBLOCK);

		listen(binary_sock, 5);
	}

	return binary_sock;
}

int main(int argc, char **argv)
{
	int http_port = XAPIAND_HTTP_PORT_DEFAULT;
	int binary_port = XAPIAND_BINARY_PORT_DEFAULT;

	if (argc > 2) {
		http_port = atoi(argv[1]);
		binary_port = atoi(argv[2]);
	}

	LOG((void *)NULL, "Starting Xapiand.\n");

	int http_sock = bind_http(http_port);
	int binary_sock = bind_binary(binary_port);

	int tasks = 0;

	if (http_sock != -1 && binary_sock != -1) {
		ev::default_loop loop;
		if (tasks) {
			ThreadPool thread_pool(tasks);
			for (int i = 0; i < tasks; i++) {
				thread_pool.addTask(new XapiandServer(NULL, http_sock, binary_sock));
			}

			loop.run();

			LOG_OBJ((void *)NULL, "Waiting for threads...\n");

			thread_pool.finish();
			thread_pool.join();
		} else {
			XapiandServer * server = new XapiandServer(&loop, http_sock, binary_sock);
			server->run();
		}
	}

	if (http_sock != -1) {
		close(http_sock);
	}

	if (binary_sock != -1) {
		close(binary_sock);
	}

	LOG((void *)NULL, "Done with all work!\n");

	return 0;
}
<|endoftext|>


<commit_before>/* Copyright (C) 2013 Codership Oy <info@codership.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#include "wsrep_binlog.h"
#include "wsrep_priv.h"

/*
  Write the contents of a cache to a memory buffer.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.
 */
int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
{
  *buf= NULL;
  *buf_len= 0;

  my_off_t const saved_pos(my_b_tell(cache));

  if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
  {
    WSREP_ERROR("failed to initialize io-cache");
    return ER_ERROR_ON_WRITE;
  }

  uint length = my_b_bytes_in_cache(cache);
  if (unlikely(0 == length)) length = my_b_fill(cache);

  size_t total_length = 0;

  if (likely(length > 0)) do
  {
      total_length += length;
      /*
        Bail out if buffer grows too large.
        A temporary fix to avoid allocating indefinitely large buffer,
        not a real limit on a writeset size which includes other things
        like header and keys.
      */
      if (total_length > wsrep_max_ws_size)
      {
          WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                     wsrep_max_ws_size, total_length);
          goto error;
      }
      uchar* tmp = (uchar *)my_realloc(*buf, total_length,
                                       MYF(MY_ALLOW_ZERO_PTR));
      if (!tmp)
      {
          WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
                      *buf_len, length);
          goto error;
      }
      *buf = tmp;

      memcpy(*buf + *buf_len, cache->read_pos, length);
      *buf_len = total_length;
      cache->read_pos = cache->read_end;
  } while ((cache->file >= 0) && (length = my_b_fill(cache)));

  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_WARN("failed to initialize io-cache");
    goto cleanup;
  }

  return 0;

error:
  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_WARN("failed to initialize io-cache");
  }
cleanup:
  my_free(*buf);
  *buf= NULL;
  *buf_len= 0;
  return ER_ERROR_ON_WRITE;
}

#define STACK_SIZE 4096 /* 4K - for buffer preallocated on the stack:
                         * many transactions would fit in there
                         * so there is no need to reach for the heap */

/* Returns minimum multiple of HEAP_PAGE_SIZE that is >= length */
static inline size_t
heap_size(size_t length)
{
    return (length + HEAP_PAGE_SIZE - 1)/HEAP_PAGE_SIZE*HEAP_PAGE_SIZE;
}

/* append data to writeset */
static inline wsrep_status_t
wsrep_append_data(wsrep_t*           const wsrep,
                  wsrep_ws_handle_t* const ws,
                  const void*        const data,
                  size_t             const len)
{
    struct wsrep_buf const buff = { data, len };
    wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1,
                                               WSREP_DATA_ORDERED, true));
    if (rc != WSREP_OK)
    {
        WSREP_WARN("append_data() returned %d", rc);
    }

    return rc;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.

  This version reads all of cache into single buffer and then appends to a
  writeset at once.
 */
static int wsrep_write_cache_once(wsrep_t*  const wsrep,
                                  THD*      const thd,
                                  IO_CACHE* const cache,
                                  size_t*   const len)
{
    my_off_t const saved_pos(my_b_tell(cache));

    if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
    {
        WSREP_ERROR("failed to initialize io-cache");
        return ER_ERROR_ON_WRITE;
    }

    int err(WSREP_OK);

    size_t total_length(0);
    uchar  stack_buf[STACK_SIZE]; /* to avoid dynamic allocations for few data*/
    uchar* heap_buf(NULL);
    uchar* buf(stack_buf);
    size_t allocated(sizeof(stack_buf));
    size_t used(0);

    uint length(my_b_bytes_in_cache(cache));
    if (unlikely(0 == length)) length = my_b_fill(cache);

    if (likely(length > 0)) do
    {
        total_length += length;
        /*
          Bail out if buffer grows too large.
          A temporary fix to avoid allocating indefinitely large buffer,
          not a real limit on a writeset size which includes other things
          like header and keys.
        */
        if (unlikely(total_length > wsrep_max_ws_size))
        {
            WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                       wsrep_max_ws_size, total_length);
	    err = WSREP_TRX_SIZE_EXCEEDED;
            goto cleanup;
        }

        if (total_length > allocated)
        {
            size_t const new_size(heap_size(total_length));
            uchar* tmp = (uchar *)my_realloc(heap_buf, new_size,
                                             MYF(MY_ALLOW_ZERO_PTR));
            if (!tmp)
            {
                WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
                            allocated, length);
                err = WSREP_TRX_SIZE_EXCEEDED;
                goto cleanup;
            }

            heap_buf = tmp;
            buf = heap_buf;
            allocated = new_size;

            if (used <= STACK_SIZE && used > 0) // there's data in stack_buf
            {
                DBUG_ASSERT(buf == stack_buf);
                memcpy(heap_buf, stack_buf, used);
            }
        }

        memcpy(buf + used, cache->read_pos, length);
        used = total_length;
        cache->read_pos = cache->read_end;
    } while ((cache->file >= 0) && (length = my_b_fill(cache)));

    if (used > 0)
        err = wsrep_append_data(wsrep, &thd->wsrep_ws_handle, buf, used);

    if (WSREP_OK == err) *len = total_length;

cleanup:
    if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
    {
        WSREP_ERROR("failed to reinitialize io-cache");
    }

    if (unlikely(WSREP_OK != err)) wsrep_dump_rbr_buf(thd, buf, used);

    my_free(heap_buf);
    return err;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.

  This version uses incremental data appending as it reads it from cache.
 */
static int wsrep_write_cache_inc(wsrep_t*  const wsrep,
                                 THD*      const thd,
                                 IO_CACHE* const cache,
                                 size_t*   const len)
{
    my_off_t const saved_pos(my_b_tell(cache));

    if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
    {
      WSREP_ERROR("failed to initialize io-cache");
      return WSREP_TRX_ERROR;
    }

    int err(WSREP_OK);

    size_t total_length(0);

    uint length(my_b_bytes_in_cache(cache));
    if (unlikely(0 == length)) length = my_b_fill(cache);

    if (likely(length > 0)) do
    {
        total_length += length;
        /* bail out if buffer grows too large
           not a real limit on a writeset size which includes other things
           like header and keys.
        */
        if (unlikely(total_length > wsrep_max_ws_size))
        {
            WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                       wsrep_max_ws_size, total_length);
            err = WSREP_TRX_SIZE_EXCEEDED;
            goto cleanup;
        }

        if(WSREP_OK != (err=wsrep_append_data(wsrep, &thd->wsrep_ws_handle,
                                              cache->read_pos, length)))
                goto cleanup;

        cache->read_pos = cache->read_end;
    } while ((cache->file >= 0) && (length = my_b_fill(cache)));

    if (WSREP_OK == err) *len = total_length;

cleanup:
    if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
    {
        WSREP_ERROR("failed to reinitialize io-cache");
    }

    return err;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.
 */
int wsrep_write_cache(wsrep_t*  const wsrep,
                      THD*      const thd,
                      IO_CACHE* const cache,
                      size_t*   const len)
{
    if (wsrep_incremental_data_collection) {
        return wsrep_write_cache_inc(wsrep, thd, cache, len);
    }
    else {
        return wsrep_write_cache_once(wsrep, thd, cache, len);
    }
}

void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
{
  char filename[PATH_MAX]= {0};
  int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
                    wsrep_data_home_dir, thd->thread_id,
                    (long long)wsrep_thd_trx_seqno(thd));
  if (len >= PATH_MAX)
  {
    WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
    return;
  }

  FILE *of= fopen(filename, "wb");
  if (of)
  {
    fwrite (rbr_buf, buf_len, 1, of);
    fclose(of);
  }
  else
  {
    WSREP_ERROR("Failed to open file '%s': %d (%s)",
                filename, errno, strerror(errno));
  }
}
extern handlerton *binlog_hton;

/*
  wsrep exploits binlog's caches even if binlogging itself is not
  activated. In such case connection close needs calling
  actual binlog's method.
  Todo: split binlog hton from its caches to use ones by wsrep
  without referring to binlog's stuff.
*/
int wsrep_binlog_close_connection(THD* thd)
{
  DBUG_ENTER("wsrep_binlog_close_connection");
  if (thd_get_ha_data(thd, binlog_hton) != NULL)
    binlog_hton->close_connection (binlog_hton, thd);
  DBUG_RETURN(0);
}

int wsrep_binlog_savepoint_set(THD *thd,  void *sv)
{
  if (!wsrep_emulate_bin_log) return 0;
  int rcode = binlog_hton->savepoint_set(binlog_hton, thd, sv);
  return rcode;
}

int wsrep_binlog_savepoint_rollback(THD *thd, void *sv)
{
  if (!wsrep_emulate_bin_log) return 0;
  int rcode = binlog_hton->savepoint_rollback(binlog_hton, thd, sv);
  return rcode;
}

void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
{
  char filename[PATH_MAX]= {0};
  int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
                    wsrep_data_home_dir, thd->thread_id,
                    (long long)wsrep_thd_trx_seqno(thd));
  size_t bytes_in_cache = 0;
  // check path
  if (len >= PATH_MAX)
  {
    WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
    return ;
  }
  // init cache
  my_off_t const saved_pos(my_b_tell(cache));
  if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
  {
    WSREP_ERROR("failed to initialize io-cache");
    return ;
  }
  // open file
  FILE* of = fopen(filename, "wb");
  if (!of)
  {
    WSREP_ERROR("Failed to open file '%s': %d (%s)",
                filename, errno, strerror(errno));
    goto cleanup;
  }
  // ready to write
  bytes_in_cache= my_b_bytes_in_cache(cache);
  if (unlikely(bytes_in_cache == 0)) bytes_in_cache = my_b_fill(cache);
  if (likely(bytes_in_cache > 0)) do
  {
    if (my_fwrite(of, cache->read_pos, bytes_in_cache,
                  MYF(MY_WME | MY_NABP)) == (size_t) -1)
    {
      WSREP_ERROR("Failed to write file '%s'", filename);
      goto cleanup;
    }
    cache->read_pos= cache->read_end;
  } while ((cache->file >= 0) && (bytes_in_cache= my_b_fill(cache)));
  if(cache->error == -1)
  {
    WSREP_ERROR("RBR inconsistent");
    goto cleanup;
  }
cleanup:
  // init back
  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_ERROR("failed to reinitialize io-cache");
  }
  // close file
  if (of) fclose(of);
}
<commit_msg>fix warning "ignoring return value" of fwrite.<commit_after>/* Copyright (C) 2013 Codership Oy <info@codership.com>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program 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 General Public License for more details.

   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */

#include "wsrep_binlog.h"
#include "wsrep_priv.h"

/*
  Write the contents of a cache to a memory buffer.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.
 */
int wsrep_write_cache_buf(IO_CACHE *cache, uchar **buf, size_t *buf_len)
{
  *buf= NULL;
  *buf_len= 0;

  my_off_t const saved_pos(my_b_tell(cache));

  if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
  {
    WSREP_ERROR("failed to initialize io-cache");
    return ER_ERROR_ON_WRITE;
  }

  uint length = my_b_bytes_in_cache(cache);
  if (unlikely(0 == length)) length = my_b_fill(cache);

  size_t total_length = 0;

  if (likely(length > 0)) do
  {
      total_length += length;
      /*
        Bail out if buffer grows too large.
        A temporary fix to avoid allocating indefinitely large buffer,
        not a real limit on a writeset size which includes other things
        like header and keys.
      */
      if (total_length > wsrep_max_ws_size)
      {
          WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                     wsrep_max_ws_size, total_length);
          goto error;
      }
      uchar* tmp = (uchar *)my_realloc(*buf, total_length,
                                       MYF(MY_ALLOW_ZERO_PTR));
      if (!tmp)
      {
          WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
                      *buf_len, length);
          goto error;
      }
      *buf = tmp;

      memcpy(*buf + *buf_len, cache->read_pos, length);
      *buf_len = total_length;
      cache->read_pos = cache->read_end;
  } while ((cache->file >= 0) && (length = my_b_fill(cache)));

  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_WARN("failed to initialize io-cache");
    goto cleanup;
  }

  return 0;

error:
  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_WARN("failed to initialize io-cache");
  }
cleanup:
  my_free(*buf);
  *buf= NULL;
  *buf_len= 0;
  return ER_ERROR_ON_WRITE;
}

#define STACK_SIZE 4096 /* 4K - for buffer preallocated on the stack:
                         * many transactions would fit in there
                         * so there is no need to reach for the heap */

/* Returns minimum multiple of HEAP_PAGE_SIZE that is >= length */
static inline size_t
heap_size(size_t length)
{
    return (length + HEAP_PAGE_SIZE - 1)/HEAP_PAGE_SIZE*HEAP_PAGE_SIZE;
}

/* append data to writeset */
static inline wsrep_status_t
wsrep_append_data(wsrep_t*           const wsrep,
                  wsrep_ws_handle_t* const ws,
                  const void*        const data,
                  size_t             const len)
{
    struct wsrep_buf const buff = { data, len };
    wsrep_status_t const rc(wsrep->append_data(wsrep, ws, &buff, 1,
                                               WSREP_DATA_ORDERED, true));
    if (rc != WSREP_OK)
    {
        WSREP_WARN("append_data() returned %d", rc);
    }

    return rc;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.

  This version reads all of cache into single buffer and then appends to a
  writeset at once.
 */
static int wsrep_write_cache_once(wsrep_t*  const wsrep,
                                  THD*      const thd,
                                  IO_CACHE* const cache,
                                  size_t*   const len)
{
    my_off_t const saved_pos(my_b_tell(cache));

    if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
    {
        WSREP_ERROR("failed to initialize io-cache");
        return ER_ERROR_ON_WRITE;
    }

    int err(WSREP_OK);

    size_t total_length(0);
    uchar  stack_buf[STACK_SIZE]; /* to avoid dynamic allocations for few data*/
    uchar* heap_buf(NULL);
    uchar* buf(stack_buf);
    size_t allocated(sizeof(stack_buf));
    size_t used(0);

    uint length(my_b_bytes_in_cache(cache));
    if (unlikely(0 == length)) length = my_b_fill(cache);

    if (likely(length > 0)) do
    {
        total_length += length;
        /*
          Bail out if buffer grows too large.
          A temporary fix to avoid allocating indefinitely large buffer,
          not a real limit on a writeset size which includes other things
          like header and keys.
        */
        if (unlikely(total_length > wsrep_max_ws_size))
        {
            WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                       wsrep_max_ws_size, total_length);
	    err = WSREP_TRX_SIZE_EXCEEDED;
            goto cleanup;
        }

        if (total_length > allocated)
        {
            size_t const new_size(heap_size(total_length));
            uchar* tmp = (uchar *)my_realloc(heap_buf, new_size,
                                             MYF(MY_ALLOW_ZERO_PTR));
            if (!tmp)
            {
                WSREP_ERROR("could not (re)allocate buffer: %zu + %u",
                            allocated, length);
                err = WSREP_TRX_SIZE_EXCEEDED;
                goto cleanup;
            }

            heap_buf = tmp;
            buf = heap_buf;
            allocated = new_size;

            if (used <= STACK_SIZE && used > 0) // there's data in stack_buf
            {
                DBUG_ASSERT(buf == stack_buf);
                memcpy(heap_buf, stack_buf, used);
            }
        }

        memcpy(buf + used, cache->read_pos, length);
        used = total_length;
        cache->read_pos = cache->read_end;
    } while ((cache->file >= 0) && (length = my_b_fill(cache)));

    if (used > 0)
        err = wsrep_append_data(wsrep, &thd->wsrep_ws_handle, buf, used);

    if (WSREP_OK == err) *len = total_length;

cleanup:
    if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
    {
        WSREP_ERROR("failed to reinitialize io-cache");
    }

    if (unlikely(WSREP_OK != err)) wsrep_dump_rbr_buf(thd, buf, used);

    my_free(heap_buf);
    return err;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.

  This version uses incremental data appending as it reads it from cache.
 */
static int wsrep_write_cache_inc(wsrep_t*  const wsrep,
                                 THD*      const thd,
                                 IO_CACHE* const cache,
                                 size_t*   const len)
{
    my_off_t const saved_pos(my_b_tell(cache));

    if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
    {
      WSREP_ERROR("failed to initialize io-cache");
      return WSREP_TRX_ERROR;
    }

    int err(WSREP_OK);

    size_t total_length(0);

    uint length(my_b_bytes_in_cache(cache));
    if (unlikely(0 == length)) length = my_b_fill(cache);

    if (likely(length > 0)) do
    {
        total_length += length;
        /* bail out if buffer grows too large
           not a real limit on a writeset size which includes other things
           like header and keys.
        */
        if (unlikely(total_length > wsrep_max_ws_size))
        {
            WSREP_WARN("transaction size limit (%lu) exceeded: %zu",
                       wsrep_max_ws_size, total_length);
            err = WSREP_TRX_SIZE_EXCEEDED;
            goto cleanup;
        }

        if(WSREP_OK != (err=wsrep_append_data(wsrep, &thd->wsrep_ws_handle,
                                              cache->read_pos, length)))
                goto cleanup;

        cache->read_pos = cache->read_end;
    } while ((cache->file >= 0) && (length = my_b_fill(cache)));

    if (WSREP_OK == err) *len = total_length;

cleanup:
    if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
    {
        WSREP_ERROR("failed to reinitialize io-cache");
    }

    return err;
}

/*
  Write the contents of a cache to wsrep provider.

  This function quite the same as MYSQL_BIN_LOG::write_cache(),
  with the exception that here we write in buffer instead of log file.
 */
int wsrep_write_cache(wsrep_t*  const wsrep,
                      THD*      const thd,
                      IO_CACHE* const cache,
                      size_t*   const len)
{
    if (wsrep_incremental_data_collection) {
        return wsrep_write_cache_inc(wsrep, thd, cache, len);
    }
    else {
        return wsrep_write_cache_once(wsrep, thd, cache, len);
    }
}

void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
{
  char filename[PATH_MAX]= {0};
  int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
                    wsrep_data_home_dir, thd->thread_id,
                    (long long)wsrep_thd_trx_seqno(thd));
  if (len >= PATH_MAX)
  {
    WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
    return;
  }

  FILE *of= fopen(filename, "wb");

  if (of)
  {
    if (fwrite(rbr_buf, buf_len, 1, of) == 0)
       WSREP_ERROR("Failed to write buffer of length %llu to '%s'",
                   (unsigned long long)buf_len, filename);

    fclose(of);
  }
  else
  {
    WSREP_ERROR("Failed to open file '%s': %d (%s)",
                filename, errno, strerror(errno));
  }
}
extern handlerton *binlog_hton;

/*
  wsrep exploits binlog's caches even if binlogging itself is not
  activated. In such case connection close needs calling
  actual binlog's method.
  Todo: split binlog hton from its caches to use ones by wsrep
  without referring to binlog's stuff.
*/
int wsrep_binlog_close_connection(THD* thd)
{
  DBUG_ENTER("wsrep_binlog_close_connection");
  if (thd_get_ha_data(thd, binlog_hton) != NULL)
    binlog_hton->close_connection (binlog_hton, thd);
  DBUG_RETURN(0);
}

int wsrep_binlog_savepoint_set(THD *thd,  void *sv)
{
  if (!wsrep_emulate_bin_log) return 0;
  int rcode = binlog_hton->savepoint_set(binlog_hton, thd, sv);
  return rcode;
}

int wsrep_binlog_savepoint_rollback(THD *thd, void *sv)
{
  if (!wsrep_emulate_bin_log) return 0;
  int rcode = binlog_hton->savepoint_rollback(binlog_hton, thd, sv);
  return rcode;
}

void wsrep_dump_rbr_direct(THD* thd, IO_CACHE* cache)
{
  char filename[PATH_MAX]= {0};
  int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
                    wsrep_data_home_dir, thd->thread_id,
                    (long long)wsrep_thd_trx_seqno(thd));
  size_t bytes_in_cache = 0;
  // check path
  if (len >= PATH_MAX)
  {
    WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
    return ;
  }
  // init cache
  my_off_t const saved_pos(my_b_tell(cache));
  if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0))
  {
    WSREP_ERROR("failed to initialize io-cache");
    return ;
  }
  // open file
  FILE* of = fopen(filename, "wb");
  if (!of)
  {
    WSREP_ERROR("Failed to open file '%s': %d (%s)",
                filename, errno, strerror(errno));
    goto cleanup;
  }
  // ready to write
  bytes_in_cache= my_b_bytes_in_cache(cache);
  if (unlikely(bytes_in_cache == 0)) bytes_in_cache = my_b_fill(cache);
  if (likely(bytes_in_cache > 0)) do
  {
    if (my_fwrite(of, cache->read_pos, bytes_in_cache,
                  MYF(MY_WME | MY_NABP)) == (size_t) -1)
    {
      WSREP_ERROR("Failed to write file '%s'", filename);
      goto cleanup;
    }
    cache->read_pos= cache->read_end;
  } while ((cache->file >= 0) && (bytes_in_cache= my_b_fill(cache)));
  if(cache->error == -1)
  {
    WSREP_ERROR("RBR inconsistent");
    goto cleanup;
  }
cleanup:
  // init back
  if (reinit_io_cache(cache, WRITE_CACHE, saved_pos, 0, 0))
  {
    WSREP_ERROR("failed to reinitialize io-cache");
  }
  // close file
  if (of) fclose(of);
}
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "idealstateoperation.h"
#include <vespa/storage/distributor/idealstatemanager.h>
#include <vespa/storage/distributor/pendingmessagetracker.h>
#include <vespa/storage/distributor/idealstatemetricsset.h>
#include <vespa/storage/distributor/pendingmessagetracker.h>
#include <vespa/storageapi/messageapi/maintenancecommand.h>

#include <vespa/log/log.h>
LOG_SETUP(".distributor.operation");

using namespace storage;
using namespace storage::distributor;
using document::BucketSpace;

const uint32_t IdealStateOperation::MAINTENANCE_MESSAGE_TYPES[] =
{
    api::MessageType::CREATEBUCKET_ID,
    api::MessageType::MERGEBUCKET_ID,
    api::MessageType::DELETEBUCKET_ID,
    api::MessageType::SPLITBUCKET_ID,
    api::MessageType::JOINBUCKETS_ID,
    api::MessageType::SETBUCKETSTATE_ID,
    0
};

IdealStateOperation::IdealStateOperation(const BucketAndNodes& bucketAndNodes)
        : _manager(NULL),
          _bucketAndNodes(bucketAndNodes),
          _ok(true),
          _priority(255)
{
}

IdealStateOperation::~IdealStateOperation()
{
}

BucketAndNodes::BucketAndNodes(const document::Bucket &bucket, uint16_t node)
    : _bucket(bucket)
{
    _nodes.push_back(node);
}

BucketAndNodes::BucketAndNodes(const document::Bucket &bucket,
                               const std::vector<uint16_t>& nodes)
    : _bucket(bucket),
      _nodes(nodes)
{
    assert(!nodes.empty());
    std::sort(_nodes.begin(), _nodes.end());
}

void
BucketAndNodes::setBucketId(const document::BucketId &id)
{
    document::Bucket newBucket(_bucket.getBucketSpace(), id);
    _bucket = newBucket;
}

std::string
BucketAndNodes::toString() const
{
    std::ostringstream ost;

    ost << "[";

    for (uint32_t i = 0; i < _nodes.size(); i++) {
        if (i != 0) {
            ost << ",";
        }
        ost << _nodes[i];
    }

    ost <<  "] ";
    ost <<  _bucket.toString();
    return ost.str();
}

void
IdealStateOperation::done()
{
    if (_manager != NULL) {
        if (ok()) {
            _manager->getMetrics().operations[getType()]->ok.inc(1);
        } else {
            _manager->getMetrics().operations[getType()]->failed.inc(1);
        }
    }
}

uint32_t
IdealStateOperation::memorySize() const
{
   return sizeof(*this) + _detailedReason.size();
}

void
IdealStateOperation::setCommandMeta(api::MaintenanceCommand& cmd) const
{
    cmd.setPriority(_priority);
    cmd.setReason(_detailedReason);
    cmd.setLoadType(
            (*_manager->getLoadTypes())["maintenance"]);
}

std::string
IdealStateOperation::toXML(framework::Clock& clock) const
{
    std::ostringstream ost;

    ost << "<operation bucketid=\"" << getBucketId()
        << "\" reason=\"" << _detailedReason << "\" operations=\"";

    ost << getName() << "[";
    for (uint32_t j = 0; j < getNodes().size(); j++) {
        if (j != 0) {
            ost << ",";
        }
        ost << getNodes()[j];
    }
    ost << "]";

    if (getStartTime().isSet()) {
        uint64_t timeSpent(
                (clock.getTimeInMillis() - getStartTime()).getTime());
        ost << "\" runtime_secs=\"" << timeSpent << "\"";
    } else {
        ost << "\"";
    }

    ost << "/>";
    return ost.str();
}

namespace {

class IdealStateOpChecker : public PendingMessageTracker::Checker
{
public:
    bool blocked;
    const IdealStateOperation& op;

    IdealStateOpChecker(const IdealStateOperation& o)
        : blocked(false), op(o)
    {
    }

    bool check(uint32_t messageType, uint16_t node, uint8_t priority) override
    {
        (void) node;
        if (op.shouldBlockThisOperation(messageType, priority)) {
            blocked = true;
            return false;
        }

        return true;
    }
};

class RequestBucketInfoChecker : public PendingMessageTracker::Checker
{
public:
    bool blocked;

    RequestBucketInfoChecker()
        : blocked(false)
    {
    }

    bool check(uint32_t messageType, uint16_t node, uint8_t priority) override
    {
        (void) node;
        (void) priority;
        // Always block for RequestBucketInfo pending to a node involved
        // in the ideal state operation.
        if (messageType == api::MessageType::REQUESTBUCKETINFO_ID) {
            blocked = true;
            return false;
        }
        return true;
    }
};

}

bool
IdealStateOperation::checkBlock(const document::Bucket &bucket,
                                const PendingMessageTracker& tracker) const
{
    IdealStateOpChecker ichk(*this);
    RequestBucketInfoChecker rchk;
    const std::vector<uint16_t>& nodes(getNodes());
    for (size_t i = 0; i < nodes.size(); ++i) {
        tracker.checkPendingMessages(nodes[i], bucket, ichk);
        if (ichk.blocked) {
            return true;
        }
        // Check messages sent to null-bucket (i.e. any bucket) for the node.
        document::Bucket nullBucket(bucket.getBucketSpace(), document::BucketId());
        tracker.checkPendingMessages(nodes[i], nullBucket, rchk);
        if (rchk.blocked) {
            return true;
        }
        if (bucket.getBucketSpace() != BucketSpace::placeHolder()) {
            nullBucket = document::Bucket(BucketSpace::placeHolder(), document::BucketId());
            tracker.checkPendingMessages(nodes[i], nullBucket, rchk);
            if (rchk.blocked) {
                return true;
            }
        }
    }
    return false;
}

bool
IdealStateOperation::checkBlockForAllNodes(
        const document::Bucket &bucket,
        const PendingMessageTracker& tracker) const
{
    IdealStateOpChecker ichk(*this);
    // Check messages sent to _any node_ for _this_ particular bucket.
    tracker.checkPendingMessages(bucket, ichk);
    if (ichk.blocked) {
        return true;
    }
    RequestBucketInfoChecker rchk;
    // Check messages sent to null-bucket (i.e. _any bucket_) for the node.
    const std::vector<uint16_t>& nodes(getNodes());
    for (size_t i = 0; i < nodes.size(); ++i) {
        document::Bucket nullBucket(bucket.getBucketSpace(), document::BucketId());
        tracker.checkPendingMessages(nodes[i], nullBucket, rchk);
        if (rchk.blocked) {
            return true;
        }
        if (bucket.getBucketSpace() != BucketSpace::placeHolder()) {
            nullBucket = document::Bucket(BucketSpace::placeHolder(), document::BucketId());
            tracker.checkPendingMessages(nodes[i], nullBucket, rchk);
            if (rchk.blocked) {
                return true;
            }
        }
    }
    return false;
}

bool
IdealStateOperation::isBlocked(const PendingMessageTracker& tracker) const
{
    return checkBlock(getBucket(), tracker);
}

std::string
IdealStateOperation::toString() const
{
    std::ostringstream ost;
    ost << getName() << " to " << _bucketAndNodes.toString()
        << " (pri " << (int)_priority << ")";

    return ost.str();
}

bool
IdealStateOperation::shouldBlockThisOperation(uint32_t messageType,
                                              uint8_t) const
{
    for (uint32_t i = 0; MAINTENANCE_MESSAGE_TYPES[i] != 0; ++i) {
        if (messageType == MAINTENANCE_MESSAGE_TYPES[i]) {
            return true;
        }
    }

    return false;
}
<commit_msg>Factor out common code.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include "idealstateoperation.h"
#include <vespa/storage/distributor/idealstatemanager.h>
#include <vespa/storage/distributor/pendingmessagetracker.h>
#include <vespa/storage/distributor/idealstatemetricsset.h>
#include <vespa/storage/distributor/pendingmessagetracker.h>
#include <vespa/storageapi/messageapi/maintenancecommand.h>

#include <vespa/log/log.h>
LOG_SETUP(".distributor.operation");

using namespace storage;
using namespace storage::distributor;
using document::BucketSpace;

const uint32_t IdealStateOperation::MAINTENANCE_MESSAGE_TYPES[] =
{
    api::MessageType::CREATEBUCKET_ID,
    api::MessageType::MERGEBUCKET_ID,
    api::MessageType::DELETEBUCKET_ID,
    api::MessageType::SPLITBUCKET_ID,
    api::MessageType::JOINBUCKETS_ID,
    api::MessageType::SETBUCKETSTATE_ID,
    0
};

IdealStateOperation::IdealStateOperation(const BucketAndNodes& bucketAndNodes)
        : _manager(NULL),
          _bucketAndNodes(bucketAndNodes),
          _ok(true),
          _priority(255)
{
}

IdealStateOperation::~IdealStateOperation()
{
}

BucketAndNodes::BucketAndNodes(const document::Bucket &bucket, uint16_t node)
    : _bucket(bucket)
{
    _nodes.push_back(node);
}

BucketAndNodes::BucketAndNodes(const document::Bucket &bucket,
                               const std::vector<uint16_t>& nodes)
    : _bucket(bucket),
      _nodes(nodes)
{
    assert(!nodes.empty());
    std::sort(_nodes.begin(), _nodes.end());
}

void
BucketAndNodes::setBucketId(const document::BucketId &id)
{
    document::Bucket newBucket(_bucket.getBucketSpace(), id);
    _bucket = newBucket;
}

std::string
BucketAndNodes::toString() const
{
    std::ostringstream ost;

    ost << "[";

    for (uint32_t i = 0; i < _nodes.size(); i++) {
        if (i != 0) {
            ost << ",";
        }
        ost << _nodes[i];
    }

    ost <<  "] ";
    ost <<  _bucket.toString();
    return ost.str();
}

void
IdealStateOperation::done()
{
    if (_manager != NULL) {
        if (ok()) {
            _manager->getMetrics().operations[getType()]->ok.inc(1);
        } else {
            _manager->getMetrics().operations[getType()]->failed.inc(1);
        }
    }
}

uint32_t
IdealStateOperation::memorySize() const
{
   return sizeof(*this) + _detailedReason.size();
}

void
IdealStateOperation::setCommandMeta(api::MaintenanceCommand& cmd) const
{
    cmd.setPriority(_priority);
    cmd.setReason(_detailedReason);
    cmd.setLoadType(
            (*_manager->getLoadTypes())["maintenance"]);
}

std::string
IdealStateOperation::toXML(framework::Clock& clock) const
{
    std::ostringstream ost;

    ost << "<operation bucketid=\"" << getBucketId()
        << "\" reason=\"" << _detailedReason << "\" operations=\"";

    ost << getName() << "[";
    for (uint32_t j = 0; j < getNodes().size(); j++) {
        if (j != 0) {
            ost << ",";
        }
        ost << getNodes()[j];
    }
    ost << "]";

    if (getStartTime().isSet()) {
        uint64_t timeSpent(
                (clock.getTimeInMillis() - getStartTime()).getTime());
        ost << "\" runtime_secs=\"" << timeSpent << "\"";
    } else {
        ost << "\"";
    }

    ost << "/>";
    return ost.str();
}

namespace {

class IdealStateOpChecker : public PendingMessageTracker::Checker
{
public:
    bool blocked;
    const IdealStateOperation& op;

    IdealStateOpChecker(const IdealStateOperation& o)
        : blocked(false), op(o)
    {
    }

    bool check(uint32_t messageType, uint16_t node, uint8_t priority) override
    {
        (void) node;
        if (op.shouldBlockThisOperation(messageType, priority)) {
            blocked = true;
            return false;
        }

        return true;
    }
};

class RequestBucketInfoChecker : public PendingMessageTracker::Checker
{
public:
    bool blocked;

    RequestBucketInfoChecker()
        : blocked(false)
    {
    }

    bool check(uint32_t messageType, uint16_t node, uint8_t priority) override
    {
        (void) node;
        (void) priority;
        // Always block for RequestBucketInfo pending to a node involved
        // in the ideal state operation.
        if (messageType == api::MessageType::REQUESTBUCKETINFO_ID) {
            blocked = true;
            return false;
        }
        return true;
    }
};

bool
checkNullBucketRequestBucketInfoMessage(uint16_t node,
                                        document::BucketSpace bucketSpace,
                                        const PendingMessageTracker& tracker)
{
    RequestBucketInfoChecker rchk;
    for (;;) {
        // Check messages sent to null-bucket (i.e. any bucket) for the node.
        document::Bucket nullBucket(bucketSpace, document::BucketId());
        tracker.checkPendingMessages(node, nullBucket, rchk);
        if (rchk.blocked) {
            return true;
        }
        if (bucketSpace == BucketSpace::placeHolder()) {
            break;
        }
        bucketSpace = BucketSpace::placeHolder();
    }
    return false;
}

}

bool
IdealStateOperation::checkBlock(const document::Bucket &bucket,
                                const PendingMessageTracker& tracker) const
{
    IdealStateOpChecker ichk(*this);
    const std::vector<uint16_t>& nodes(getNodes());
    for (auto node : nodes) {
        tracker.checkPendingMessages(node, bucket, ichk);
        if (ichk.blocked) {
            return true;
        }
        if (checkNullBucketRequestBucketInfoMessage(node, bucket.getBucketSpace(), tracker)) {
            return true;
        }
    }
    return false;
}

bool
IdealStateOperation::checkBlockForAllNodes(
        const document::Bucket &bucket,
        const PendingMessageTracker& tracker) const
{
    IdealStateOpChecker ichk(*this);
    // Check messages sent to _any node_ for _this_ particular bucket.
    tracker.checkPendingMessages(bucket, ichk);
    if (ichk.blocked) {
        return true;
    }
    const std::vector<uint16_t>& nodes(getNodes());
    for (auto node : nodes) {
        if (checkNullBucketRequestBucketInfoMessage(node, bucket.getBucketSpace(), tracker)) {
            return true;
        }
    }
    return false;
}

bool
IdealStateOperation::isBlocked(const PendingMessageTracker& tracker) const
{
    return checkBlock(getBucket(), tracker);
}

std::string
IdealStateOperation::toString() const
{
    std::ostringstream ost;
    ost << getName() << " to " << _bucketAndNodes.toString()
        << " (pri " << (int)_priority << ")";

    return ost.str();
}

bool
IdealStateOperation::shouldBlockThisOperation(uint32_t messageType,
                                              uint8_t) const
{
    for (uint32_t i = 0; MAINTENANCE_MESSAGE_TYPES[i] != 0; ++i) {
        if (messageType == MAINTENANCE_MESSAGE_TYPES[i]) {
            return true;
        }
    }

    return false;
}
<|endoftext|>