mirror of https://github.com/infosecn1nja/C3.git
150 lines
6.3 KiB
C++
150 lines
6.3 KiB
C++
#pragma once
|
|
|
|
namespace FSecure
|
|
{
|
|
/// Container with multi-threaded synchronization.
|
|
template<typename T>
|
|
struct SafeSmartPointerContainer
|
|
{
|
|
/// Enumerates over elements.
|
|
/// @param comparator function that returns true to keep iterate or false to stop.
|
|
void For(std::function<bool(T const&)> comparator) const
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
for (auto element : m_Container)
|
|
if (!comparator(element))
|
|
return;
|
|
}
|
|
|
|
/// Finds Element using provided comparator.
|
|
/// @param comparator function that returns true for requested Element.
|
|
/// @return Element if existed, otherwise null.
|
|
T Find(std::function<bool(T const&)> comparator) const
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
auto it = std::find_if(m_Container.begin(), m_Container.end(), comparator); // Find specified Element...
|
|
return it == std::end(m_Container) ? T{} : *it; // ...and return it (or null if it couldn't be found).
|
|
}
|
|
|
|
/// Adds a new Element.
|
|
/// @tparam Args constructor arguments.
|
|
/// @tparam args constructor arguments.
|
|
/// @return newly added Element.
|
|
template <typename... Args>
|
|
T Add(Args&& ... args)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
m_Container.emplace_back(args...); // Add it at the end.
|
|
return m_Container.back();
|
|
}
|
|
|
|
/// Adds an Element, but throws if Element is already in the container.
|
|
/// @tparam Args constructor arguments.
|
|
/// @param comparator function that returns true for requested Element.
|
|
/// @tparam args constructor arguments.
|
|
/// @return newly added Element.
|
|
/// @throw std::invalid_argument if Element is already stored.
|
|
template <typename... Args>
|
|
T TryAdd(std::function<bool(T const&)> comparator, Args&& ... args)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
if (auto it = std::find_if(m_Container.begin(), m_Container.end(), comparator); it != m_Container.end()) // Find element...
|
|
throw std::invalid_argument{ OBF("Tried to add an existing Element to the container.") }; // ...and throw.
|
|
|
|
m_Container.emplace_back(std::forward<Args>(args)...); // Otherwise add it at the end.
|
|
return m_Container.back();
|
|
}
|
|
|
|
/// Finds provided Element or adds it if not found.
|
|
/// @tparam Args constructor arguments.
|
|
/// @param comparator function that returns true for requested Element.
|
|
/// @tparam args constructor arguments.
|
|
/// @param element an element to be added.
|
|
template <typename... Args>
|
|
T Ensure(std::function<bool(T const&)> comparator, Args&& ... args)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
if (auto it = std::find(m_Container.begin(), m_Container.end(), element); it != m_Container.end()) // Find element...
|
|
return *it; // ...and return it.
|
|
|
|
m_Container.emplace_back(std::forward<Args>(args)...); // Otherwise add it at the end.
|
|
return m_Container.back();
|
|
}
|
|
|
|
/// Removes an Elements.
|
|
/// @param element what to remove.
|
|
/// @throw std::invalid_argument on an attempt of removal of a non-existent Element.
|
|
void Remove(T const& element)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
if (auto it = std::find(m_Container.begin(), m_Container.end(), element); it != m_Container.end()) // Find element...
|
|
m_Container.erase(it); // ...and remove it.
|
|
else
|
|
throw std::invalid_argument{ OBF("Attempted to remove a non-existent Element.") };
|
|
}
|
|
|
|
/// Removes an Element using provided comparator.
|
|
/// @param comparator function that returns true for requested Element.
|
|
/// @throw std::invalid_argument on an attempt of removal of a non-existent Element.
|
|
void Remove(std::function<bool(T const&)> comparator)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
if (auto it = std::find_if(m_Container.begin(), m_Container.end(), comparator); it != m_Container.end()) // Find specified Element...
|
|
m_Container.erase(it); // ...and remove it.
|
|
else
|
|
throw std::invalid_argument{ OBF("Attempted to remove a non-existent Element.") };
|
|
}
|
|
|
|
/// Removes all elements matching the predicate
|
|
/// @param predicate function that returns true for requested Element.
|
|
template<typename Predicate>
|
|
void RemoveIf(Predicate predicate)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex);
|
|
m_Container.erase(std::remove_if(begin(m_Container), end(m_Container), predicate), end(m_Container));
|
|
}
|
|
|
|
/// Same as Remove, but returns the element removed from the container.
|
|
/// @param comparator function that returns true for requested Element.
|
|
/// @return Copy of the element that was requested to be removed.
|
|
/// @throw std::invalid_argument on an attempt of removal of a non-existent Element.
|
|
T Retrieve(std::function<bool(T const&)> comparator)
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex); // Request access to the underlying Container.
|
|
if (auto it = std::find_if(m_Container.begin(), m_Container.end(), comparator); it != m_Container.end()) // Find specified Element...
|
|
{
|
|
auto element = std::move(*it); // ...move it...
|
|
m_Container.erase(it); // ...remove...
|
|
return std::move(element); // ...and return it.
|
|
}
|
|
else
|
|
throw std::invalid_argument{ OBF("Attempted to remove a non-existent Element.") };
|
|
}
|
|
|
|
/// Gets element quantity.
|
|
/// @return number of elements in the container.
|
|
size_t GetSize() const
|
|
{
|
|
return m_Container.size();
|
|
}
|
|
|
|
/// Checks if the container has any elements.
|
|
/// @return true if the container is empty.
|
|
bool IsEmpty() const
|
|
{
|
|
return m_Container.empty();
|
|
}
|
|
|
|
/// Clear whole container.
|
|
void Clear()
|
|
{
|
|
std::scoped_lock<std::mutex> lock(m_AccessMutex);
|
|
m_Container.clear();
|
|
}
|
|
|
|
private:
|
|
mutable std::mutex m_AccessMutex; ///< Mutex for synchronization.
|
|
std::vector<T> m_Container; ///< Table of all Elements.
|
|
};
|
|
}
|