Skip to content

Commit

Permalink
Use std::string_view as key for collection::Collection
Browse files Browse the repository at this point in the history
- Introduced transparent comparator & hash to be able to lookup for
  std::string_view in std::unordered_map where the key is a std::string,
  which avoids creating unnecessary std::string instances (with a
  heap-allocation and copy)
  - This is a C++20 feature, which introduced support for transparent
    comparisons in std::unordered_map.
  • Loading branch information
eduar-hte committed Aug 23, 2024
1 parent 269d9d2 commit 3be6d9a
Show file tree
Hide file tree
Showing 11 changed files with 117 additions and 122 deletions.
14 changes: 10 additions & 4 deletions headers/modsecurity/anchored_set_variable.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ class KeyExclusions;


struct MyEqual {
bool operator()(const std::string& Left, const std::string& Right) const {
using is_transparent = void;

template<typename T, typename U>
bool operator()(const T& Left, const U& Right) const {
return Left.size() == Right.size()
&& std::equal(Left.begin(), Left.end(), Right.begin(),
[](char a, char b) {
Expand All @@ -57,7 +60,10 @@ struct MyEqual {
};

struct MyHash{
size_t operator()(const std::string& Keyval) const {
using is_transparent = void;

template<typename T>
size_t operator()(const T& Keyval) const {
// You might need a better hash function than this
size_t h = 0;
std::for_each(Keyval.begin(), Keyval.end(), [&](char c) {
Expand Down Expand Up @@ -88,7 +94,7 @@ class AnchoredSetVariable : public std::unordered_multimap<std::string,
void resolve(std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke);

void resolve(const std::string &key,
void resolve(std::string_view key,
std::vector<const VariableValue *> &l);

void resolveRegularExpression(Utils::Regex *r,
Expand All @@ -98,7 +104,7 @@ class AnchoredSetVariable : public std::unordered_multimap<std::string,
std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke);

std::unique_ptr<std::string> resolveFirst(const std::string &key);
std::unique_ptr<std::string> resolveFirst(std::string_view key);

Transaction *m_transaction;
std::string m_name;
Expand Down
4 changes: 2 additions & 2 deletions headers/modsecurity/anchored_set_variable_translation_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class AnchoredSetVariableTranslationProxy {
translate(l);
}

void resolve(const std::string &key,
void resolve(std::string_view key,
std::vector<const VariableValue *> &l) {
m_fount->resolve(key, l);
translate(l);
Expand All @@ -75,7 +75,7 @@ class AnchoredSetVariableTranslationProxy {
translate(l);
};

std::unique_ptr<std::string> resolveFirst(const std::string &key) {
std::unique_ptr<std::string> resolveFirst(std::string_view key) {
std::vector<const VariableValue *> l;
resolve(l);

Expand Down
16 changes: 8 additions & 8 deletions headers/modsecurity/collection/collection.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,25 +45,25 @@ class Collection {
explicit Collection(std::string_view a) : m_name(a) { }
virtual ~Collection() { }

virtual bool storeOrUpdateFirst(const std::string& key,
virtual bool storeOrUpdateFirst(std::string_view key,
std::string_view value) = 0;

virtual bool updateFirst(const std::string& key,
virtual bool updateFirst(std::string_view key,
std::string_view value) = 0;

virtual void del(const std::string& key) = 0;
virtual void del(std::string_view key) = 0;

virtual void setExpiry(const std::string& key, int32_t expiry_seconds) = 0;
virtual void setExpiry(std::string_view key, int32_t expiry_seconds) = 0;

virtual std::unique_ptr<std::string> resolveFirst(
const std::string& var) = 0;
std::string_view var) = 0;

virtual void resolveSingleMatch(const std::string& var,
virtual void resolveSingleMatch(std::string_view var,
std::vector<const VariableValue *> &l) = 0;
virtual void resolveMultiMatches(const std::string& var,
virtual void resolveMultiMatches(std::string_view var,
std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke) = 0;
virtual void resolveRegularExpression(const std::string& var,
virtual void resolveRegularExpression(std::string_view var,
std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke) = 0;

Expand Down
5 changes: 3 additions & 2 deletions src/anchored_set_variable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ void AnchoredSetVariable::resolve(
}


void AnchoredSetVariable::resolve(const std::string &key,
void AnchoredSetVariable::resolve(std::string_view key,
std::vector<const VariableValue *> &l) {

auto range = this->equal_range(key);
for (auto it = range.first; it != range.second; ++it) {
l.push_back(new VariableValue(*it->second));
Expand All @@ -99,7 +100,7 @@ void AnchoredSetVariable::resolve(const std::string &key,


std::unique_ptr<std::string> AnchoredSetVariable::resolveFirst(
const std::string &key) {
std::string_view key) {

if (auto search = this->find(key); search != this->end()) {
return std::make_unique<std::string>(search->second->getValue());
Expand Down
44 changes: 21 additions & 23 deletions src/collection/backend/in_memory-per_process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@
#include "src/utils/string.h"


namespace modsecurity {
namespace collection {
namespace backend {
namespace modsecurity::collection::backend {


InMemoryPerProcess::InMemoryPerProcess(std::string_view name) :
Expand All @@ -46,7 +44,7 @@ InMemoryPerProcess::~InMemoryPerProcess() {


template<typename Map>
inline void __store(Map &map, const std::string& key, std::string_view value) {
inline void __store(Map &map, std::string_view key, std::string_view value) {
// NOTE: should be called with write-lock previously acquired

map.emplace(key, value);
Expand All @@ -55,7 +53,7 @@ inline void __store(Map &map, const std::string& key, std::string_view value) {

template<typename Map>
inline bool __updateFirst(Map &map,
const std::string& key,
std::string_view key,
std::string_view value) {
// NOTE: should be called with write-lock previously acquired

Expand All @@ -68,13 +66,13 @@ inline bool __updateFirst(Map &map,
}


void InMemoryPerProcess::store(const std::string& key, std::string_view value) {
void InMemoryPerProcess::store(std::string_view key, std::string_view value) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)
__store(m_map, key, value);
}


bool InMemoryPerProcess::storeOrUpdateFirst(const std::string& key,
bool InMemoryPerProcess::storeOrUpdateFirst(std::string_view key,
std::string_view value) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)
if (__updateFirst(m_map, key, value) == false) {
Expand All @@ -84,29 +82,31 @@ bool InMemoryPerProcess::storeOrUpdateFirst(const std::string& key,
}


bool InMemoryPerProcess::updateFirst(const std::string& key,
bool InMemoryPerProcess::updateFirst(std::string_view key,
std::string_view value) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)
return __updateFirst(m_map, key, value);
}


void InMemoryPerProcess::del(const std::string& key) {
void InMemoryPerProcess::del(std::string_view key) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)
m_map.erase(key);
const auto range = m_map.equal_range(key);
m_map.erase(range.first, range.second);
}

void InMemoryPerProcess::delIfExpired(const std::string& key) {
void InMemoryPerProcess::delIfExpired(std::string_view key) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)
const auto range = m_map.equal_range(key);
// Double check the status while within the mutex
const auto iter = std::find_if(m_map.begin(), m_map.end(),
[&key](const auto &x) { return x.first == key && x.second.isExpired(); });
if (iter != m_map.end()) {
m_map.erase(key);
const auto iter = std::find_if(range.first, range.second,
[](const auto &x) { return x.second.isExpired(); });
if (iter != range.second) {
m_map.erase(range.first, range.second);
}
}

void InMemoryPerProcess::setExpiry(const std::string& key, int32_t expiry_seconds) {
void InMemoryPerProcess::setExpiry(std::string_view key, int32_t expiry_seconds) {
const std::lock_guard lock(m_mutex); // write lock (exclusive access)

if (const auto search = m_map.find(key); search != m_map.end()) {
Expand All @@ -120,7 +120,7 @@ void InMemoryPerProcess::setExpiry(const std::string& key, int32_t expiry_second
}


void InMemoryPerProcess::resolveSingleMatch(const std::string& var,
void InMemoryPerProcess::resolveSingleMatch(std::string_view var,
std::vector<const VariableValue *> &l) {
std::list<std::string> expiredVars;

Expand All @@ -145,7 +145,7 @@ void InMemoryPerProcess::resolveSingleMatch(const std::string& var,
}


void InMemoryPerProcess::resolveMultiMatches(const std::string& var,
void InMemoryPerProcess::resolveMultiMatches(std::string_view var,
std::vector<const VariableValue *> &l, variables::KeyExclusions &ke) {
const auto keySize = var.size();
l.reserve(15);
Expand Down Expand Up @@ -192,7 +192,7 @@ void InMemoryPerProcess::resolveMultiMatches(const std::string& var,
}


void InMemoryPerProcess::resolveRegularExpression(const std::string& var,
void InMemoryPerProcess::resolveRegularExpression(std::string_view var,
std::vector<const VariableValue *> &l, variables::KeyExclusions &ke) {
Utils::Regex r(var, true);
std::list<std::string> expiredVars;
Expand Down Expand Up @@ -225,7 +225,7 @@ void InMemoryPerProcess::resolveRegularExpression(const std::string& var,


std::unique_ptr<std::string> InMemoryPerProcess::resolveFirst(
const std::string& var) {
std::string_view var) {
std::unique_ptr<std::string> ret;
std::list<std::string> expiredVars;

Expand All @@ -252,6 +252,4 @@ std::unique_ptr<std::string> InMemoryPerProcess::resolveFirst(
}


} // namespace backend
} // namespace collection
} // namespace modsecurity
} // namespace modsecurity::collection::backend
49 changes: 23 additions & 26 deletions src/collection/backend/in_memory-per_process.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
*/


#ifdef __cplusplus
#ifndef SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_
#define SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_

#include <string>
#include <iostream>
#include <unordered_map>
Expand All @@ -24,21 +26,13 @@
#include <algorithm>
#include <memory>
#include <shared_mutex>
#endif


#include "modsecurity/variable_value.h"
#include "modsecurity/collection/collection.h"
#include "src/collection/backend/collection_data.h"
#include "src/variables/variable.h"

#ifndef SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_
#define SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_

#ifdef __cplusplus
namespace modsecurity {
namespace collection {
namespace backend {
namespace modsecurity::collection::backend {

/*
* FIXME:
Expand All @@ -50,7 +44,10 @@ namespace backend {
*
*/
struct MyEqual {
bool operator()(const std::string& Left, const std::string& Right) const {
using is_transparent = void;

template<typename T, typename U>
bool operator()(const T& Left, const U& Right) const {
return Left.size() == Right.size()
&& std::equal(Left.begin(), Left.end(), Right.begin(),
[](char a, char b) {
Expand All @@ -60,7 +57,10 @@ struct MyEqual {
};

struct MyHash{
size_t operator()(const std::string& Keyval) const {
using is_transparent = void;

template<typename T>
size_t operator()(const T& Keyval) const {
// You might need a better hash function than this
size_t h = 0;
std::for_each(Keyval.begin(), Keyval.end(), [&](char c) {
Expand All @@ -75,28 +75,28 @@ class InMemoryPerProcess :
public:
explicit InMemoryPerProcess(std::string_view name);
~InMemoryPerProcess() override;
void store(const std::string& key, std::string_view value);
void store(std::string_view key, std::string_view value);

bool storeOrUpdateFirst(const std::string& key,
bool storeOrUpdateFirst(std::string_view key,
std::string_view value) override;

bool updateFirst(const std::string& key,
bool updateFirst(std::string_view key,
std::string_view value) override;

void del(const std::string& key) override;
void del(std::string_view key) override;

void delIfExpired(const std::string& key);
void delIfExpired(std::string_view key);

void setExpiry(const std::string& key, int32_t expiry_seconds) override;
void setExpiry(std::string_view key, int32_t expiry_seconds) override;

std::unique_ptr<std::string> resolveFirst(const std::string& var) override;
std::unique_ptr<std::string> resolveFirst(std::string_view var) override;

void resolveSingleMatch(const std::string& var,
void resolveSingleMatch(std::string_view var,
std::vector<const VariableValue *> &l) override;
void resolveMultiMatches(const std::string& var,
void resolveMultiMatches(std::string_view var,
std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke) override;
void resolveRegularExpression(const std::string& var,
void resolveRegularExpression(std::string_view var,
std::vector<const VariableValue *> &l,
variables::KeyExclusions &ke) override;

Expand All @@ -117,10 +117,7 @@ class InMemoryPerProcess :
std::shared_mutex m_mutex;
};

} // namespace backend
} // namespace collection
} // namespace modsecurity
#endif
} // namespace modsecurity::collection::backend


#endif // SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_
Loading

0 comments on commit 3be6d9a

Please sign in to comment.