diff --git a/Jupiter/Algorithm.h b/Jupiter/Algorithm.h new file mode 100644 index 0000000..54b25f1 --- /dev/null +++ b/Jupiter/Algorithm.h @@ -0,0 +1,222 @@ +/** + * Copyright (C) 2017 Jessica James. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Written by Jessica James + */ + +#if !defined _ALGORITHM_H_HEADER +#define _ALGORITHM_H_HEADER + +/** + * @file Algorithm.h + * @brief Provides various common algorithm implementations. + */ + +namespace Jupiter +{ + namespace Algorithm + { + /** + * @brief Sorts an array of data in-place using merge sort + * + * @param in_data Data to sort + * @param in_data_length Length of the array of data to sort + */ + template void merge_sort_serial(T *in_data, size_t in_data_length); + + /** + * @brief Sorts an array of data in-place using merge sort across multiple threads + * + * @param in_data Data to sort + * @param in_data_length Length of the array of data to sort + * @param in_max_threads Maximum number of threads to use + * @param in_use_max_threads True to force the number of threads to in_max_threads + */ + template void merge_sort_parallel(T *in_data, size_t in_data_length, unsigned int in_max_threads, bool in_force_max_threads = false); + + /** + * @brief Sorts an array of data in-place using merge sort across multiple threads + * + * @param in_data Data to sort + * @param in_data_length Length of the array of data to sort + */ + template void merge_sort_parallel(T *in_data, size_t in_data_length); + + /** + * @brief Sorts an array of data in-place using merge sort across multiple threads + * + * @param in_data Data to sort + * @param in_data_length Length of the array of data to sort + */ + template void merge_sort(T *in_data, size_t in_data_length); + + /** Internal helper templates */ + namespace Internal + { + template void merge_sort_internal(T *in_source, size_t in_length, T *out_buffer, unsigned int unused_threads); + } + } +} + +/** +* IMPLEMENTATION: +* Merge Sort +*/ + +template void Jupiter::Algorithm::merge_sort(T *in_data, size_t in_data_length) +{ + merge_sort_parallel(in_data, in_data_length); +} + +template void Jupiter::Algorithm::merge_sort_parallel(T *in_data, size_t in_data_length) +{ + merge_sort_parallel(in_data, in_data_length); +} + +template void Jupiter::Algorithm::merge_sort_parallel(T *in_data, size_t in_data_length, unsigned int in_max_threads, bool in_force_max_threads) +{ + constexpr size_t MINIMUM_ELEMENTS_PER_THREAD = 0x3FFF; + + unsigned int thread_count = static_cast(in_data_length) / MINIMUM_ELEMENTS_PER_THREAD; + + if (thread_count > in_max_threads || in_force_max_threads) + thread_count = in_max_threads; + + if (thread_count <= 1) + { + merge_sort_serial(in_data, in_data_length); + return; + } + + T *buffer = new T[in_data_length]; + + // copy base into buffer + for (size_t index = 0; index != in_data_length; ++index) + buffer[index] = in_data[index]; + + // mergesort into buffer + Internal::merge_sort_internal(in_data, in_data_length, buffer, thread_count - 1); + + // mergesort is complete; free buffer + delete[] buffer; +} + +template void Jupiter::Algorithm::merge_sort_serial(T *in_data, size_t in_data_length) +{ + if (in_data_length <= 1) + return; + + T *buffer = new T[in_data_length]; + + // copy base into buffer + for (size_t index = 0; index != in_data_length; ++index) + buffer[index] = in_data[index]; + + // mergesort into buffer + Internal::merge_sort_internal(in_data, in_data_length, buffer, 0); + + // mergesort is complete; free buffer + delete[] buffer; +} + +/** Internals */ + +template void Jupiter::Algorithm::Internal::merge_sort_internal(T *in_source, size_t in_length, T *out_buffer, unsigned int unused_threads) +{ + if (in_length <= 1) // Nothing to sort + return; + + /** Split */ + + T *left_buffer = out_buffer; + size_t left_length = in_length / 2; + + T *right_buffer = out_buffer + left_length; + size_t right_length = in_length - left_length; + + if (unused_threads == 0) // serial + { + // sort left + merge_sort_internal(left_buffer, left_length, in_source, 0); + + // sort right + merge_sort_internal(right_buffer, right_length, in_source + left_length, 0); + } + else // parallel + { + --unused_threads; + + unsigned int left_thread_count = unused_threads / 2; + + // sort left + std::thread left_sort_thread(merge_sort_internal, left_buffer, left_length, in_source, left_thread_count); + + // sort right + merge_sort_internal(right_buffer, right_length, in_source + left_length, unused_threads - left_thread_count); + + left_sort_thread.join(); + } + + // merge left & right + while (true) + { + if (*right_buffer < *left_buffer) + { + *in_source = *right_buffer; + + ++in_source; + ++right_buffer; + + if (--right_length == 0) + { + // Only values on the left side remain + while (left_length != 0) + { + *in_source = *left_buffer; + + ++in_source; + ++left_buffer; + --left_length; + } + + return; + } + } + else + { + *in_source = *left_buffer; + + ++in_source; + ++left_buffer; + + if (--left_length == 0) + { + // Only values on the right side remain + while (right_length != 0) + { + *in_source = *right_buffer; + + ++in_source; + ++right_buffer; + --right_length; + } + + return; + } + } + } +} + +#endif // _ALGORITHM_H_HEADER diff --git a/Jupiter/IRC_Client.cpp b/Jupiter/IRC_Client.cpp index f11ed3e..5943867 100644 --- a/Jupiter/IRC_Client.cpp +++ b/Jupiter/IRC_Client.cpp @@ -1553,27 +1553,37 @@ size_t Jupiter::IRC::Client::User::getChannelCount() const Jupiter::IRC::Client::Channel::Channel(const Jupiter::ReadableString &in_name, Jupiter::IRC::Client *in_parent) { + auto to_lower = [&in_name]() + { + Jupiter::String result(in_name.size()); + const char *itr = in_name.ptr(); + const char *end = itr + in_name.size(); + + while (itr != end) + { + result += tolower(*itr); + ++itr; + } + + return result; + }; + m_name = in_name; m_parent = in_parent; m_type = m_parent->getDefaultChanType(); - auto read_type = [&in_name](Jupiter::Config &in_config, int default_type) + Jupiter::String name = to_lower(); + + auto read_type = [&name](Jupiter::Config &in_config, int default_type) { - return in_config["Channels"_jrs][in_name].get("Type"_jrs, default_type); + return in_config["Channels"_jrs][name].get("Type"_jrs, default_type); }; - if (m_parent->getPrimaryConfigSection() == nullptr) - { - if (m_parent->getSecondaryConfigSection() != nullptr) - m_type = read_type(*m_parent->getSecondaryConfigSection(), m_type); - } - else if (m_parent->getSecondaryConfigSection() == nullptr) + if (m_parent->getSecondaryConfigSection() != nullptr) + m_type = read_type(*m_parent->getSecondaryConfigSection(), m_type); + + if (m_parent->getPrimaryConfigSection() != nullptr) m_type = read_type(*m_parent->getPrimaryConfigSection(), m_type); - else - { - m_type = read_type(*m_parent->getPrimaryConfigSection(), - read_type(*m_parent->getSecondaryConfigSection(), m_type)); - } } Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::addUser(Jupiter::IRC::Client::User *user) diff --git a/Jupiter/Jupiter.vcxproj b/Jupiter/Jupiter.vcxproj index 9ecb7e9..a05d185 100644 --- a/Jupiter/Jupiter.vcxproj +++ b/Jupiter/Jupiter.vcxproj @@ -202,6 +202,7 @@ + diff --git a/Jupiter/Jupiter.vcxproj.filters b/Jupiter/Jupiter.vcxproj.filters index 173037a..0bb565a 100644 --- a/Jupiter/Jupiter.vcxproj.filters +++ b/Jupiter/Jupiter.vcxproj.filters @@ -272,6 +272,9 @@ Header Files\Files\Configs + + Header Files +