+++ /dev/null
-// Copyright (c) 2023, 2025-2026 Egor
-// SPDX-License-Identifier: GPL-2.0-or-later
-#include "common.hpp"
-#include "map.hpp"
-#include "ircdata.hpp"
-
-template<typename A, typename B>
-class Comparisons {
- friend constexpr bool operator==(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs == 0; }
- friend constexpr bool operator!=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs != 0; }
- friend constexpr bool operator< (const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs < 0; }
- friend constexpr bool operator<=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs <= 0; }
- friend constexpr bool operator> (const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs > 0; }
- friend constexpr bool operator>=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs >= 0; }
-
- friend constexpr bool operator==(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs == 0; }
- friend constexpr bool operator!=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs != 0; }
- friend constexpr bool operator< (const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs < 0; }
- friend constexpr bool operator<=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs <= 0; }
- friend constexpr bool operator> (const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs > 0; }
- friend constexpr bool operator>=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs >= 0; }
-};
-
-class LowercasingView : Comparisons<std::string_view, LowercasingView> {
- std::string_view sv;
-public:
- explicit constexpr LowercasingView(std::string_view sv) :sv(sv) {}
- friend constexpr std::strong_ordering operator<=>(std::string_view lhs, LowercasingView rhs) noexcept {
- size_t len = std::min(lhs.size(), rhs.sv.size());
- const char *a = lhs.data();
- const char *b = rhs.sv.data();
- unsigned char cha, chb;
- while (len--) {
- cha = *a++;
- chb = *b++;
- if (chb >= 'A' && chb <= 'Z')
- chb |= 0x20;
- if (auto diff = cha <=> chb; diff != 0)
- return diff;
- }
- return lhs.size() <=> rhs.sv.size();
- }
- friend constexpr std::strong_ordering operator<=>(LowercasingView lhs, std::string_view rhs) noexcept {
- return rhs <=> lhs;
- }
-};
-
-static size_t ent_find(map_base *root, auto key, int *found)
-{
- auto it = std::partition_point(root->ptr, root->ptr + root->len,
- [key](const auto &p){ return std::string_view(p.key) < key; });
- *found = it != root->ptr + root->len && std::string_view(it->key) == key;
- return it - root->ptr;
-}
-void *map_base::_get(const char *key)
-{
- int found;
- size_t i = ent_find(this, std::string_view(key), &found);
- return found ? ptr[i].value : 0;
-}
-void *map_base::_iget(const char *key)
-{
- int found;
- size_t i = ent_find(this, LowercasingView(key), &found);
- return found ? ptr[i].value : 0;
-}
-void map_base::_set(const char *key, const void *value, void **oldvalue)
-{
- int found;
- size_t i = ent_find(this, std::string_view(key), &found);
- if (found) {
- if (oldvalue)
- *oldvalue = ptr[i].value;
- if (!value) {
- delete[] ptr[i].key;
- std::copy(ptr+i+1, ptr+len, ptr+i);
- len--;
- } else {
- ptr[i].value = const_cast<void *>(value);
- }
- } else {
- if (oldvalue)
- *oldvalue = 0;
- if (value) {
- if (len == cap) {
- size_t ncap = cap ? cap*2 : 2;
- map_value *nptr = new map_value[ncap];
- memcpy(nptr, ptr, cap*sizeof(map_value));
- ptr = nptr;
- cap = ncap;
- }
- size_t slen = strlen(key)+1;
- char *nkey = new char[slen];
- memcpy(nkey, key, slen);
- std::copy_backward(ptr+i, ptr+len, ptr+len+1);
- len++;
- ptr[i].key = nkey;
- ptr[i].value = const_cast<void *>(value);
- }
- }
-}
-void map_base::clear() {
- while (len) {
- delete[] ptr[--len].key;
- }
- delete[] ptr;
- ptr = 0;
- len = cap = 0;
-}
-map_base::~map_base() {
- clear();
-}
#define MAP_H_
#include "util.hpp"
+template<typename A, typename B>
+class Comparisons {
+ friend constexpr bool operator==(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs == 0; }
+ friend constexpr bool operator!=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs != 0; }
+ friend constexpr bool operator< (const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs < 0; }
+ friend constexpr bool operator<=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs <= 0; }
+ friend constexpr bool operator> (const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs > 0; }
+ friend constexpr bool operator>=(const A& lhs, const B& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs >= 0; }
+
+ friend constexpr bool operator==(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs == 0; }
+ friend constexpr bool operator!=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs != 0; }
+ friend constexpr bool operator< (const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs < 0; }
+ friend constexpr bool operator<=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs <= 0; }
+ friend constexpr bool operator> (const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs > 0; }
+ friend constexpr bool operator>=(const B& lhs, const A& rhs) noexcept(noexcept(lhs <=> rhs)) { return lhs <=> rhs >= 0; }
+};
+
+class LowercasingView : Comparisons<std::string_view, LowercasingView> {
+ std::string_view sv;
+public:
+ explicit constexpr LowercasingView(std::string_view sv) :sv(sv) {}
+ friend constexpr std::strong_ordering operator<=>(std::string_view lhs, LowercasingView rhs) noexcept {
+ size_t len = std::min(lhs.size(), rhs.sv.size());
+ const char *a = lhs.data();
+ const char *b = rhs.sv.data();
+ unsigned char cha, chb;
+ while (len--) {
+ cha = *a++;
+ chb = *b++;
+ if (chb >= 'A' && chb <= 'Z')
+ chb |= 0x20;
+ if (auto diff = cha <=> chb; diff != 0)
+ return diff;
+ }
+ return lhs.size() <=> rhs.sv.size();
+ }
+ friend constexpr std::strong_ordering operator<=>(LowercasingView lhs, std::string_view rhs) noexcept {
+ return 0 <=> (rhs <=> lhs);
+ }
+};
+
+// TODO: use std::flat_set once clang is new enough
+template<typename T>
+concept is_transparent = requires { typename T::is_transparent; };
+template<typename Key, typename Compare=std::less<Key>, typename KeyContainer=std::vector<Key>>
+class FlatSet {
+ KeyContainer m_vec;
+ [[no_unique_address]] Compare cmp;
+public:
+ using iterator = KeyContainer::iterator;
+ auto begin() noexcept { return m_vec.begin(); }
+ auto end() noexcept { return m_vec.end(); }
+
+ auto lower_bound(const Key& key) {
+ return std::partition_point(begin(), end(), [&](const auto &e){ return cmp(e, key); });
+ }
+ template<typename K> requires is_transparent<Compare>
+ auto lower_bound(const K& key) {
+ return std::partition_point(begin(), end(), [&](const auto &e){ return cmp(e, key); });
+ }
+
+ auto find(const Key& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it))
+ return it;
+ else
+ return end();
+ }
+ template<typename K> requires is_transparent<Compare>
+ auto find(const K& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it))
+ return it;
+ else
+ return end();
+ }
+
+ auto insert(const Key& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it)) {
+ return std::make_pair(it, false);
+ } else {
+ return std::make_pair(m_vec.insert(it, key), true);
+ }
+ }
+ auto insert(Key&& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it)) {
+ return std::make_pair(it, false);
+ } else {
+ return std::make_pair(m_vec.insert(it, std::move(key)), true);
+ }
+ }
+
+ auto erase(typename KeyContainer::iterator it) {
+ return m_vec.erase(it);
+ }
+ size_t erase(const Key& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it)) {
+ erase(it);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ template<typename K> requires is_transparent<Compare>
+ size_t erase(const K& key) {
+ auto it = lower_bound(key);
+ if (it != end() && !cmp(key, *it)) {
+ erase(it);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ void clear() noexcept {
+ m_vec.clear();
+ }
+ auto size() noexcept {
+ return m_vec.size();
+ }
+
+ FlatSet& operator=(FlatSet&&) = delete;
+};
+
/* A map abstract data type with following properties:
* - keys are lexicographically-sorted unique NUL-terminated strings
* - values are non-NULL pointers
* A map is created by allocating and zero-initializing an object of type
* map_t. All map functions take a pointer to map_t.
*/
+template<typename T>
+struct MapValueLess {
+ using is_transparent = void;
+ constexpr std::string_view to_key(const T &v) {
+ return v.key;
+ }
+ constexpr bool operator()(const T& lhs, const T& rhs) noexcept {
+ return to_key(lhs) < to_key(rhs);
+ }
+ constexpr bool operator()(const T& lhs, std::string_view rhs) noexcept {
+ return to_key(lhs) < rhs;
+ }
+ constexpr bool operator()(std::string_view lhs, const T& rhs) noexcept {
+ return lhs < to_key(rhs);
+ }
+ constexpr bool operator()(const T& lhs, LowercasingView rhs) noexcept {
+ return to_key(lhs) < rhs;
+ }
+ constexpr bool operator()(LowercasingView lhs, const T& rhs) noexcept {
+ return lhs < to_key(rhs);
+ }
+};
struct map_base {
struct map_value {
char *key;
void *value;
};
- map_value *ptr = 0;
- size_t len = 0;
- size_t cap = 0;
+ FlatSet<map_value, MapValueLess<map_value>> m;
map_base& operator=(map_base&&) = delete;
- ~map_base();
+ ~map_base() {
+ clear();
+ }
/* Return the associated value for a given key.
* Dynarr complexity: O(log n) */
- void *_get(const char *key);
+ void *_get(const char *key) {
+ auto it = m.find(std::string_view(key));
+ return it != m.end() ? it->value : nullptr;
+ }
/* _get() but lowercase the key */
- void *_iget(const char *key);
+ void *_iget(const char *key) {
+ auto it = m.find(LowercasingView(key));
+ return it != m.end() ? it->value : nullptr;
+ }
/* Associate a new value with a given key.
* If oldvalue is not NULL, it receives the old value of key.
* No allocation failures on value = NULL.
* Dynarr complexity: O(n) */
- void _set(const char *key, const void *value, void **oldvalue);
+ void _set(const char *key, const void *value, void **oldvalue) {
+ if (value) {
+ map_value mv;
+ mv.key = const_cast<char *>(key);
+ mv.value = const_cast<void *>(value);
+ auto [it, inserted] = m.insert(mv);
+ if (inserted) {
+ if (oldvalue)
+ *oldvalue = nullptr;
+ it->key = mystrdup(it->key);
+ } else {
+ if (oldvalue)
+ *oldvalue = it->value;
+ it->value = mv.value;
+ }
+ } else {
+ auto it = m.find(std::string_view(key));
+ if (oldvalue)
+ *oldvalue = it != m.end() ? it->value : nullptr;
+ if (it != m.end()) {
+ delete[] it->key;
+ m.erase(it);
+ }
+ }
+ }
/* Removes all keys from the map and frees any associated memory.
* The map_t is reset to zero-initialized state and can be used again.
* Dynarr complexity: O(n) */
- void clear();
+ void clear() {
+ for (auto &mv : m)
+ delete[] mv.key;
+ m.clear();
+ }
/* Iterate over a map.
* Do not modify the map while iterating.
*/
size_t index;
auto next() {
map_value kv{0, 0};
- if (index < root->len)
- kv = root->ptr[index++];
+ auto it = root->m.begin() + index;
+ if (it < root->m.end()) {
+ kv = *it;
+ index++;
+ }
return std::make_pair(kv.key, kv.value);
}
};
iterator &operator--() { --ptr; return *this; }
iterator operator--(int) { auto it = *this; --ptr; return it; }
bool operator==(const iterator& other) const = default;
- reference operator*() const { return T::to_value(ptr); }
- map_value *ptr;
+ reference operator*() const { return T::to_value(&*ptr); }
+ decltype(m)::iterator ptr;
};
static_assert(std::bidirectional_iterator<iterator>);
- auto begin() { return iterator{ptr}; }
- auto end() { return iterator{ptr+len}; }
+ auto begin() { return iterator{m.begin()}; }
+ auto end() { return iterator{m.end()}; }
struct reverse {
map_base2 &obj;
auto begin() { return std::make_reverse_iterator(obj.end()); }
return reverse{*this};
}
size_t size() {
- return len;
+ return m.size();
}
protected:
static const char *to_key(V v) {