/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2022 Sadie Powell * * This file is part of InspIRCd. InspIRCd 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. * * 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, see . */ #pragma once namespace insp { /** Checks whether an element with the given value is in a container. * @param container The ontainer to find the element in. * @param value The value of the element to look for. * @return True if the element was found in the container; otherwise, false. */ template inline bool contains(const Container& container, const Value& value) { return std::find(container.begin(), container.end(), value) != container.end(); } template inline std::remove_pointer_t* find_value(const Container& container, const Key& key) { auto it = container.find(key); if (it == container.end()) return nullptr; return insp::to_ptr(it->second); } /** Combines a sequence of elements into a string. * @param sequence A sequence of elements to combine. * @param separator The value to separate the elements with. Defaults to ' ' (space). */ template inline std::string join(const Collection& sequence, Separator separator = ' ') { std::string joined; if (sequence.empty()) return joined; const std::string separatorstr = ConvToStr(separator); for (const auto& element : sequence) joined.append(ConvToStr(element)).append(separatorstr); joined.erase(joined.end() - separatorstr.length()); joined.shrink_to_fit(); return joined; } /** Compares two maps and returns a list of the differences. * @param first The first map to compare. * @param second The second map to compare. * @param out The map to store the differences in. */ template typename Map> void map_difference(const Map& first, const Map& second, Map, std::optional>, Compare>& out) { auto fiter = first.cbegin(); auto siter = second.cbegin(); while (fiter != first.end()) { if (siter == second.end()) { // Store the remaining from the first. while (fiter != first.end()) { out[fiter->first] = { fiter->second, std::nullopt }; fiter++; } return; } if (fiter->first < siter->first) { // The key in first is not in second. out[fiter->first] = { fiter->second, std::nullopt }; fiter++; } else if (siter->first < fiter->first) { // The key in second is not in first. out[siter->first] = { std::nullopt, siter->second }; siter++; } else if (fiter->second != siter->second) { // The key exists in both but the value differs. out[fiter->first] = { fiter->second, siter->second }; fiter++; siter++; } else { // The key/value is identical in both. fiter++; siter++; } } // Store the remaining from the second. while (siter != second.end()) { out[siter->first] = { std::nullopt, siter->second }; siter++; } } /** Erase a single element from a container by overwriting it with a copy of the last element, * which is then removed. This, in contrast to rase(), does not result in all elements after * the erased element being moved. All iterators, references and pointers to the erased element * and the last element are invalidated * @param container The container to remove the element from. * @param it An iterator to the element to remove. */ template inline void swap_erase(Container& container, const typename Container::iterator& it) { *it = std::move(container.back()); container.pop_back(); } /** Find and erase a single element from a vector by overwriting it with a copy of the last * element, which is then removed. This, in contrast to erase(), does not result in all elements * after the erased element being moved. If the given value occurs multiple times, the one with * the lowest index is removed. If the element is found then all iterators, references and * pointers to the erased element and the last element are invalidated. * @param container The container to remove the element from. * @param value The value to look for and remove. * @return True if the element was found and removed; otherwise, false. */ template inline bool swap_erase(Container& container, const typename Container::value_type& value) { auto it = std::find(container.begin(), container.end(), value); if (it != container.end()) { swap_erase(container, it); return true; } return false; } }