diff --git a/CMakeLists.txt b/CMakeLists.txt index bc483c1..afaa7ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,8 @@ set(SOURCE_FILES src/protocol/ringbuffer.cpp src/protocol/AcknowledgeManager.cpp src/protocol/PacketLossCalculator.cpp + + src/lookup/ip.cpp ) set(HEADER_FILES diff --git a/src/lookup/ip.cpp b/src/lookup/ip.cpp new file mode 100644 index 0000000..6eeea85 --- /dev/null +++ b/src/lookup/ip.cpp @@ -0,0 +1,23 @@ +// +// Created by WolverinDEV on 01/08/2020. +// + +#include "ipv4.h" +#include "ipv6.h" + +lookup::ip_v4 storage_4{}; +lookup::ip_v6 loop6{}; + +int test() { + sockaddr_in addr_4{}; + storage_4.insert(addr_4, nullptr); + (void) storage_4.lookup(addr_4); + storage_4.remove(addr_4); + + + sockaddr_in6 addr_6{}; + loop6.insert(addr_6, nullptr); + (void) loop6.lookup(addr_6); + loop6.remove(addr_6); + return 0; +} \ No newline at end of file diff --git a/src/lookup/ip.h b/src/lookup/ip.h new file mode 100644 index 0000000..bdc2f9a --- /dev/null +++ b/src/lookup/ip.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include "../misc/spin_mutex.h" + +namespace lookup { + template + struct ip_vx { + constexpr static auto kBukkitListSize{128}; /* must be a power of two */ + constexpr static auto kBukkitSize{32}; + + struct bucket_entry { + addr_storage_t address{}; + std::shared_ptr entry{}; + }; + + struct bucket_t { + std::array entries{}; + + uint8_t entry_count{0}; + bucket_t* next{nullptr}; + }; + + public: + inline void insert(const addr_t& address, const std::shared_ptr& value) { + auto hash = (uint8_t) hash_fn{}(address); + hash &= (uint8_t) (kBukkitListSize - 1); + + bucket_t* bucket = &this->buckets[hash]; + + std::lock_guard lock{this->bucket_locks[hash]}; + while(bucket->entry_count == kBukkitSize && bucket->next) + bucket = bucket->next; + + if(bucket->entry_count == kBukkitSize) + bucket = (bucket->next = new bucket_t{}); + + auto& entry = bucket->entries[bucket->entry_count++]; + addr_converter{}(entry.address, address); + entry.entry = value; + } + + inline std::shared_ptr remove(const addr_t& address) { + auto hash = (uint8_t) hash_fn{}(address); + hash &= (uint8_t) (kBukkitListSize - 1); + + bucket_t *bucket = &this->buckets[hash], *next_bucket; + + addr_storage_t addr{}; + addr_converter{}(addr, address); + + addr_cmp cmp{}; + + std::lock_guard lock{this->bucket_locks[hash]}; + + size_t entry_index; + do { + for(entry_index = 0; entry_index < bucket->entry_count; entry_index++) + if(auto& entry{bucket->entries[entry_index]}; cmp(entry.address, addr)) + goto entry_found; + } while((bucket = bucket->next)); + + /* entry hasn't been found */ + return nullptr; + + entry_found: + + next_bucket = bucket; + while(next_bucket->next && next_bucket->next->entry_count > 0) + next_bucket = next_bucket->next; + + /* swap the entry with the last entry and just remove the value */ + next_bucket->entry_count--; + std::exchange(bucket->entries[entry_index], next_bucket->entries[next_bucket->entry_count]); + return std::exchange(next_bucket->entries[next_bucket->entry_count].entry, nullptr); + } + + inline void cleanup() { + for(size_t bucket_index{0}; bucket_index < kBukkitListSize; bucket_index++) + cleanup_bucket(bucket_index); + } + + inline void cleanup_bucket(size_t index) { + bucket_t* delete_head; + { + std::lock_guard lock{this->bucket_locks[index]}; + auto& bucket = this->buckets[index]; + + bucket_t* prev{nullptr}, curr{&bucket}; + while(curr.entry_count > 0 && curr.next) + prev = std::exchange(curr, curr.next); + + if(curr.entry_count == 0) { + prev->next = nullptr; + delete_head = curr; + } else { + return; + } + } + + while(delete_head) { + auto next = delete_head->next; + delete delete_head; + delete_head = next; + } + } + + [[nodiscard]] inline std::shared_ptr lookup(const addr_t& address) const { + auto hash = (uint8_t) hash_fn{}(address); + hash &= (uint8_t) (kBukkitListSize - 1); + + const bucket_t* bucket = &this->buckets[hash], next_bucket; + + addr_storage_t addr{}; + addr_converter{}(addr, address); + + addr_cmp cmp{}; + + { + std::lock_guard lock{this->bucket_locks[hash]}; + do { + for(size_t index{0}; index < bucket->entry_count; index++) + if(auto& entry{bucket->entries[index]}; cmp(entry.address, addr)) + return entry.entry; + } while((bucket = bucket->next)); + } + + return nullptr; + } + private: + mutable std::array bucket_locks{}; + std::array buckets{}; + }; +} \ No newline at end of file diff --git a/src/lookup/ipv4.h b/src/lookup/ipv4.h new file mode 100644 index 0000000..ca51ad9 --- /dev/null +++ b/src/lookup/ipv4.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include "./ip.h" + +namespace lookup { + namespace ipv4_impl { + union uaddress_t { + struct { + uint32_t address{0}; + uint16_t port{0}; + }; + + uint64_t value; + }; + + struct converter { + constexpr inline void operator()(uaddress_t& result, const sockaddr_in& addr) { + result.address = addr.sin_addr.s_addr; + result.port = addr.sin_port; + } + }; + + struct comparator { + constexpr inline bool operator()(const uaddress_t& a, const uaddress_t& b) { + return a.value == b.value; + } + }; + + struct hash { + constexpr inline uint8_t operator()(const sockaddr_in& address) { + return (address.sin_addr.s_addr & 0xFFU) ^ (address.sin_port); + } + }; + } + + template + using ip_v4 = ip_vx< + T, + sockaddr_in, + ipv4_impl::uaddress_t, + ipv4_impl::converter, + ipv4_impl::comparator, + ipv4_impl::hash + >; +} \ No newline at end of file diff --git a/src/lookup/ipv6.h b/src/lookup/ipv6.h new file mode 100644 index 0000000..611cdc6 --- /dev/null +++ b/src/lookup/ipv6.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include + +#include "./ip.h" + +namespace lookup { + namespace ipv6_impl { + struct address_t { + union { + uint64_t address_u64[ 2]; + }; + + uint16_t port; + }; + + struct converter { + constexpr inline void operator()(address_t& result, const sockaddr_in6& addr) { + auto addr_ptr = (uint64_t*) &addr.sin6_addr; + + result.address_u64[0] = addr_ptr[0]; + result.address_u64[1] = addr_ptr[1]; + + result.port = addr.sin6_port; + } + }; + + struct comparator { + constexpr inline bool operator()(const address_t& a, const address_t& b) { + return a.address_u64[0] == b.address_u64[0] && a.address_u64[1] == b.address_u64[1] && a.port == b.port; + } + }; + + struct hash { + constexpr inline uint8_t operator()(const sockaddr_in6& address) { + auto addr_ptr = (uint8_t*) &address.sin6_addr; + + return (uint8_t) (addr_ptr[8] ^ addr_ptr[9]) ^ (uint8_t) (addr_ptr[15] ^ address.sin6_port); + } + }; + } + + template + using ip_v6 = ip_vx< + T, + sockaddr_in6, + ipv6_impl::address_t, + ipv6_impl::converter, + ipv6_impl::comparator, + ipv6_impl::hash + >; +} \ No newline at end of file