From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#pragma once
#include <iosfwd>
#include <panda/string.h>
#include <range/v3/core.hpp>
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
#include <boost/container/small_vector.hpp>
namespace panda { namespace protocol { namespace http {
inline bool iequals (string_view a, string_view b) {
auto sz = a.length();
if (sz != b.length()) return false;
const char* ap = a.data();
const char* bp = b.data();
size_t l = sz / 8;
const char* e = ap + l*8;
for (; ap != e; ap += 8, bp += 8) {
uint64_t av, bv;
memcpy(&av, ap, 8);
memcpy(&bv, bp, 8);
if ((av|0x2020202020202020ULL) != (bv|0x2020202020202020ULL)) return false;
}
auto left = sz - l*8;
if (left & 4) {
unsigned int av, bv;
memcpy(&av, ap, 4);
memcpy(&bv, bp, 4);
if ((av|0x20202020) != (bv|0x20202020)) return false;
ap += 4;
bp += 4;
}
if (left & 2) {
unsigned short av, bv;
memcpy(&av, ap, 2);
memcpy(&bv, bp, 2);
if ((av|0x2020) != (bv|0x2020)) return false;
ap += 2;
bp += 2;
}
if (left & 1) return (*ap|0x20) == (*bp|0x20);
return true;
}
template <class T, bool CASE_SENSITIVE, size_t PRERESERVE>
struct Fields {
struct Field {
string name;
T value;
Field (const string& k, const T& v) : name(k), value(v) {}
bool matches (string_view key) const {
return CASE_SENSITIVE ? (this->name == key) : iequals(this->name, key);
}
Field (const Field&) = default;
Field (Field&&) = default;
Field& operator= (const Field&) = default;
Field& operator= (Field&&) = default;
bool operator== (const Field& oth) const { return matches(oth.name) && value == oth.value; }
};
using Container = boost::container::small_vector<Field, PRERESERVE>;
Container fields;
Fields () {}
Fields (const Fields& fields) = default;
Fields (Fields&& fields) = default;
Fields (const std::initializer_list<Field>& l) {
for (auto& f : l) fields.emplace_back(f.name, f.value);
}
Fields (std::initializer_list<Field>&& l) {
for (auto& f : l) fields.emplace_back(std::move(f.name), std::move(f.value));
}
Fields& operator= (const Fields&) = default;
Fields& operator= (Fields&&) = default;
bool has (string_view key) const {
for (const auto& f : fields) if (f.matches(key)) return true;
return false;
}
const T& get (string_view key, const T& defval = T()) const {
auto it = find(key);
return it == fields.cend() ? defval : it->value;
}
void add (const string& key, const T& value) {
fields.emplace_back(key, value);
}
void set (const string& key, const T& value) {
bool replaced = false;
for (auto it = fields.begin(); it != fields.end();) {
if (it->matches(key)) {
if (replaced) it = fields.erase(it);
else {
replaced = true;
it->name = key;
it->value = value;
++it;
}
}
else ++it;
}
if (!replaced) add(key, value);
}
void remove (string_view key) {
for (auto it = fields.cbegin(); it != fields.cend();) {
if (it->matches(key)) it = fields.erase(it);
else ++it;
}
}
bool empty () const { return fields.empty(); }
size_t size () const { return fields.size(); }
void clear () { fields.clear(); }
typename Container::const_iterator find (string_view key) const {
auto end = fields.crend();
for (auto it = fields.crbegin(); it != end; ++it) {
if (it->matches(key)) return it.base()-1;
}
return fields.cend();
}
typename Container::iterator find (string_view key) {
auto end = fields.rend();
for (auto it = fields.rbegin(); it != end; ++it) {
if (it->matches(key)) return it.base()-1;
}
return fields.end();
}
auto get_multi (const string_view& key) const {
return fields | ::ranges::views::filter([key](const Field& f) {return f.matches(key);})
| ::ranges::views::transform([](const Field& f) -> const string& {return f.value; });
}
typename Container::iterator begin () { return fields.begin(); }
typename Container::const_iterator begin () const { return fields.cbegin(); }
typename Container::const_iterator cbegin () const { return fields.cbegin(); }
typename Container::iterator end () { return fields.end(); }
typename Container::const_iterator end () const { return fields.cend(); }
typename Container::const_iterator cend () const { return fields.cend(); }
};
}}}