diff --git a/src/misc/ip_router.cpp b/src/misc/ip_router.cpp index 532326e..2cf643a 100644 --- a/src/misc/ip_router.cpp +++ b/src/misc/ip_router.cpp @@ -1,415 +1,415 @@ -// -// Created by WolverinDEV on 08/03/2020. -// - -#include -#include -#include -#include "ip_router.h" - -using namespace ts::network; - - -constexpr static ip_router::route_entry generate_empty_end_node(void*) { - ip_router::route_entry result{}; - for(auto& ptr : result.data) - ptr = nullptr; - return result; -} - -template -constexpr std::array generate_default_table() noexcept { - std::array result{}; - - for(size_t index{0}; index < result.size(); index++) { - result[index].use_count = ip_router::route_entry::const_flag_mask | 0xFFU; - result[index].deep = index + 1; - } - - return result; -} - -std::array ip_router::recursive_ends = generate_default_table<16>(); - -struct sockaddr_storage_info { - size_t address_offset{0}; - size_t address_length{0}; - size_t chunk_offset{0}; -}; - -constexpr std::array generate_storage_info() noexcept { - std::array result{}; - for(size_t type{0}; type < result.size(); type++) { - if(type == AF_INET) { - result[type].address_length = 4; - result[type].chunk_offset = 12; - - sockaddr_in address{}; - result[type].address_offset = (uintptr_t) &address.sin_addr.s_addr - (uintptr_t) &address; - } else if(type == AF_INET6) { - result[type].address_length = 16; - result[type].chunk_offset = 0; - - sockaddr_in6 address{}; - result[type].address_offset = (uintptr_t) &address.sin6_addr.__in6_u.__u6_addr8 - (uintptr_t) &address; - } - } - - return result; -} - -std::array storage_infos = generate_storage_info(); - -inline void address_to_chunks(uint8_t* chunks, const sockaddr_storage &address) { - if constexpr (AF_INET < 16 && AF_INET6 < 16) { - /* converter without branches (only one within the memcpy) */ - const auto& info = storage_infos[address.ss_family & 0xFU]; - - memset(chunks, 0, 16); - memcpy(chunks + info.chunk_offset, ((uint8_t*) &address) + info.address_offset, info.address_length); /* we could do this memcpy more performant */ - } else { - /* converter with branches */ - if(address.ss_family == AF_INET) { - auto address4 = (sockaddr_in*) &address; - - memset(chunks, 0, 12); - memcpy(chunks + 12, &address4->sin_addr.s_addr, 4); - } else if(address.ss_family == AF_INET6) { - auto address6 = (sockaddr_in6*) &address; - memcpy(chunks, address6->sin6_addr.__in6_u.__u6_addr8, 16); - } else { - memset(chunks, 0, 16); - } - } -} - -ip_router::ip_router() { - for(auto& data : this->root_entry.data) - data = &ip_router::recursive_ends[14]; - this->root_entry.deep = 1; - this->root_entry.use_count = ip_router::route_entry::const_flag_mask | 0xFFU; -} - -inline void delete_route_entry(ip_router::route_entry* entry, size_t level) { - level -= entry->deep; - if(level != 0) { - for(auto& data : entry->data) { - auto e = (ip_router::route_entry*) data; - if(e->is_const_entry()) continue; - - delete_route_entry(e, level); - } - } - delete entry; -} - -ip_router::~ip_router() { - for(auto& entry : this->unused_nodes) - delete entry; - - for(auto& data : this->root_entry.data) { - auto entry = (ip_router::route_entry*) data; - if(entry->is_const_entry()) continue; - - delete_route_entry(entry, 16 - this->root_entry.deep); - } -} - -/* - * Because we're only reading memory here, and that even quite fast we do not need to lock the register lock. - * Even if a block gets changed, it will not be deleted immediately. So we should finish reading first before that memory get freed. - */ -void* ip_router::resolve(const sockaddr_storage &address) const { - uint8_t address_chunks[16]; - address_to_chunks(address_chunks, address); - const ip_router::route_entry* current_chunk = &this->root_entry; - - std::lock_guard lock{this->entry_lock}; - //std::shared_lock lock{this->entry_lock}; - - size_t byte_index{0}; - while(true) { - byte_index += current_chunk->deep; - if(byte_index == 16) break; - assert(byte_index < 16); - - current_chunk = (ip_router::route_entry*) current_chunk->data[address_chunks[byte_index - 1]]; - }; - - if(memcmp(address_chunks, current_chunk->previous_chunks, 15) != 0) - return nullptr; /* route does not match */ - - return current_chunk->data[address_chunks[15]]; -} - -bool ip_router::register_route(const sockaddr_storage &address, void *target, void ** old_target) { - uint8_t address_chunks[16]; - address_to_chunks(address_chunks, address); - - void* _temp_old_target{}; - if(!old_target) - old_target = &_temp_old_target; - - ip_router::route_entry* current_chunk = &this->root_entry; - std::lock_guard rlock{this->register_lock}; - - size_t byte_index{0}; - while(true) { - byte_index += current_chunk->deep; - if(byte_index == 16) break; - assert(byte_index < 16); - - /* for the first iteration no previous_chunks check for "current_chunk" is needed because it will always match! */ - auto& next_chunk = (ip_router::route_entry*&) current_chunk->data[address_chunks[byte_index - 1]]; - if(next_chunk->is_const_entry()) { - /* perfect, lets allocate our own end and we're done */ - //assert(next_chunk == &ip_rounter::recursive_ends[15 - index - 1]); - - auto allocated_entry = this->create_8bit_entry(byte_index, true); - if(!allocated_entry) return false; - - memcpy(allocated_entry->previous_chunks, address_chunks, 15); - - /* no lock needed here, just a pointer exchange */ - next_chunk = allocated_entry; - current_chunk->use_count++; - current_chunk = next_chunk; - break; /* end chunk now */ - } else if(next_chunk->deep > 1) { - ssize_t unmatch_index{-1}; - for(size_t i{0}; i < next_chunk->deep - 1; i++) { - if(next_chunk->previous_chunks[byte_index + i] != address_chunks[byte_index + i]) { - unmatch_index = i; - break; - } - } - - if(unmatch_index >= 0) { - auto allocated_entry = this->create_8bit_entry(byte_index + unmatch_index, false); - if(!allocated_entry) return false; - - allocated_entry->deep = unmatch_index + 1; - allocated_entry->use_count++; - allocated_entry->data[next_chunk->previous_chunks[byte_index + unmatch_index]] = next_chunk; - memcpy(allocated_entry->previous_chunks, address_chunks, 15); - - { - std::lock_guard elock{this->entry_lock}; - next_chunk->deep = next_chunk->deep - unmatch_index - 1; - next_chunk = allocated_entry; - } - current_chunk = next_chunk; - continue; - } else { - /* every bit matched we also have this nice jump */ - } - } - - current_chunk = next_chunk; - } - - *old_target = std::exchange(current_chunk->data[address_chunks[15]], target); - if(!*old_target) current_chunk->use_count++; - return true; -} - -ip_router::route_entry *ip_router::create_8bit_entry(size_t level, bool end_entry) { - ip_router::route_entry *result; - - if(this->unused_nodes.empty()) - result = new ip_router::route_entry{}; - else { - result = this->unused_nodes.front(); - this->unused_nodes.pop_front(); - } - - result->use_count = 0; - if(end_entry) { - /* this is an end chunk now */ - result->deep = 16 - level; - for(auto& data : result->data) - data = nullptr; - } else { - assert(level <= 14); - result->deep = 1; - - auto pointer = &ip_router::recursive_ends[15 - level - 1]; - for(auto& data : result->data) - data = pointer; - } - - return result; -} - -void *ip_router::reset_route(const sockaddr_storage &address) { - uint8_t address_chunks[16]; - address_to_chunks(address_chunks, address); - - ip_router::route_entry* current_chunk{&this->root_entry}; - std::lock_guard rlock{this->register_lock}; - - { - size_t byte_index{0}; - while(true) { - byte_index += current_chunk->deep; - if(byte_index == 16) break; - assert(byte_index < 16); - - current_chunk = (ip_router::route_entry*) current_chunk->data[address_chunks[byte_index - 1]]; - if(current_chunk->is_const_entry()) return nullptr; - }; - - if(memcmp(address_chunks, current_chunk->previous_chunks, 15) != 0) - return nullptr; /* route does not match */ - } - - auto old = current_chunk->data[address_chunks[15]]; - if(!old) return nullptr; - - if(--current_chunk->use_count == 0) { - while(true) { - size_t byte_index{0}; - - current_chunk = &this->root_entry; - while(true) { - byte_index += current_chunk->deep; - if(byte_index == 16) break; - assert(byte_index < 16); - - auto& next_chunk = (ip_router::route_entry*&) current_chunk->data[address_chunks[byte_index - 1]]; - if(next_chunk->deep + byte_index == 16) { - assert(next_chunk->use_count == 0); - this->unused_nodes.push_back(next_chunk); - - /* this is the last chunk */ - next_chunk = &ip_router::recursive_ends[15 - byte_index]; - if(--current_chunk->use_count > 0) goto exit_delete_loop; - } - - current_chunk = next_chunk; - } - } - - exit_delete_loop:; - } - return old; -} - -void ip_router::cleanup_cache() { - std::lock_guard rlock{this->register_lock}; - for(auto node : this->unused_nodes) - delete node; - this->unused_nodes.clear(); -} - -bool ip_router::validate_chunk_entry(const ip_router::route_entry* current_entry, size_t level) const { - if(level == 0) - return true; - - if(current_entry->is_const_entry() && level != 16) - return level == current_entry->deep; - - auto default_pointer = &ip_router::recursive_ends[level - 1]; - for(const auto& data_ptr : current_entry->data) { - if(data_ptr == default_pointer) - continue; - - if(!this->validate_chunk_entry((const ip_router::route_entry*) data_ptr, level - current_entry->deep)) - return false; - } - - return true; -} - -bool ip_router::validate_tree() const { - std::lock_guard rlock{this->register_lock}; - - /* first lets validate all const chunks */ - for(size_t index{0}; index < 16; index++) { - if(!ip_router::recursive_ends[index].is_const_entry()) - return false; - - if(ip_router::recursive_ends[index].deep != index + 1) - return false; - - for(const auto& data_ptr : ip_router::recursive_ends[index].data) - if(data_ptr) - return false; - } - - /* not lets check our tree */ - return this->validate_chunk_entry(&this->root_entry, 16); -} - -size_t ip_router::chunk_memory(const ip_router::route_entry *current_entry, size_t level) const { - size_t result{sizeof(ip_router::route_entry)}; - - level -= current_entry->deep; - if(level > 0) { - for(const auto& data_ptr : current_entry->data) { - auto entry = (const ip_router::route_entry*) data_ptr; - if(entry->is_const_entry()) continue; - - result += chunk_memory(entry, level); - } - } - - return result; -} - -size_t ip_router::used_memory() const { - return this->chunk_memory(&this->root_entry, 16); -} - -std::string ip_router::print_as_string() const { - std::string result{}; - this->print_as_string(result, "", &this->root_entry, 16); - result += "Memory used: " + std::to_string(this->used_memory() / 1024) + "kb"; - return result; -} - -template -std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) { - if(w == 0) return "0x0"; - static const char* digits = "0123456789ABCDEF"; - std::string rc(hex_len,'0'); - for (size_t i=0, j=(hex_len-1)*4 ; i> j) & 0x0f]; - size_t lz{0}; - for(;lz < rc.length() && rc[lz] == '0'; lz++); - return "0x" + rc.substr(lz); -} - -template -inline std::string padded_num(T value) { - auto result = std::to_string(value); - return result.length() > 3 ? "" : std::string(3 - result.length(), '0') + result; -} - -void ip_router::print_as_string(std::string& result, const std::string& indent, const ip_router::route_entry *current_entry, size_t level) const { - level -= current_entry->deep; - - size_t range_begin{0}; - for(size_t i = 0; i <= 0xFF; i++) { - auto entry = (const ip_router::route_entry*) current_entry->data[i]; - if(level == 0 ? !entry : entry->is_const_entry()) continue; - - if(i > 0) { - if(range_begin < i - 1) - result += indent + padded_num(range_begin) + ".." + padded_num(i - 1) + ": empty\n"; - else if(range_begin == i - 1) - result += indent + padded_num(range_begin) + ": empty\n"; - } - - if(level == 0) { - result += indent + padded_num(i) + ": " + n2hexstr((uintptr_t) entry) + "\n"; - } else { - result += indent + padded_num(i) + ": " + n2hexstr((uintptr_t) entry) + " (used by: " + std::to_string(entry->use_count) + ", deph: " + std::to_string(entry->deep) + ")\n"; - this->print_as_string(result, indent + " ", entry, level); - } - range_begin = i + 1; - } - - if(range_begin < 0xFF) - result += indent + padded_num(range_begin) + "..255: empty\n"; +// +// Created by WolverinDEV on 08/03/2020. +// + +#include +#include +#include +#include "ip_router.h" + +using namespace ts::network; + + +constexpr static ip_router::route_entry generate_empty_end_node(void*) { + ip_router::route_entry result{}; + for(auto& ptr : result.data) + ptr = nullptr; + return result; +} + +template +constexpr std::array generate_default_table() noexcept { + std::array result{}; + + for(size_t index{0}; index < result.size(); index++) { + result[index].use_count = ip_router::route_entry::const_flag_mask | 0xFFU; + result[index].deep = index + 1; + } + + return result; +} + +std::array ip_router::recursive_ends = generate_default_table<16>(); + +struct sockaddr_storage_info { + size_t address_offset{0}; + size_t address_length{0}; + size_t chunk_offset{0}; +}; + +constexpr std::array generate_storage_info() noexcept { + std::array result{}; + for(size_t type{0}; type < result.size(); type++) { + if(type == AF_INET) { + result[type].address_length = 4; + result[type].chunk_offset = 12; + + sockaddr_in address{}; + result[type].address_offset = (uintptr_t) &address.sin_addr.s_addr - (uintptr_t) &address; + } else if(type == AF_INET6) { + result[type].address_length = 16; + result[type].chunk_offset = 0; + + sockaddr_in6 address{}; + result[type].address_offset = (uintptr_t) &address.sin6_addr.__in6_u.__u6_addr8 - (uintptr_t) &address; + } + } + + return result; +} + +std::array storage_infos = generate_storage_info(); + +inline void address_to_chunks(uint8_t* chunks, const sockaddr_storage &address) { + if constexpr (AF_INET < 16 && AF_INET6 < 16) { + /* converter without branches (only one within the memcpy) */ + const auto& info = storage_infos[address.ss_family & 0xFU]; + + memset(chunks, 0, 16); + memcpy(chunks + info.chunk_offset, ((uint8_t*) &address) + info.address_offset, info.address_length); /* we could do this memcpy more performant */ + } else { + /* converter with branches */ + if(address.ss_family == AF_INET) { + auto address4 = (sockaddr_in*) &address; + + memset(chunks, 0, 12); + memcpy(chunks + 12, &address4->sin_addr.s_addr, 4); + } else if(address.ss_family == AF_INET6) { + auto address6 = (sockaddr_in6*) &address; + memcpy(chunks, address6->sin6_addr.__in6_u.__u6_addr8, 16); + } else { + memset(chunks, 0, 16); + } + } +} + +ip_router::ip_router() { + for(auto& data : this->root_entry.data) + data = &ip_router::recursive_ends[14]; + this->root_entry.deep = 1; + this->root_entry.use_count = ip_router::route_entry::const_flag_mask | 0xFFU; +} + +inline void delete_route_entry(ip_router::route_entry* entry, size_t level) { + level -= entry->deep; + if(level != 0) { + for(auto& data : entry->data) { + auto e = (ip_router::route_entry*) data; + if(e->is_const_entry()) continue; + + delete_route_entry(e, level); + } + } + delete entry; +} + +ip_router::~ip_router() { + for(auto& entry : this->unused_nodes) + delete entry; + + for(auto& data : this->root_entry.data) { + auto entry = (ip_router::route_entry*) data; + if(entry->is_const_entry()) continue; + + delete_route_entry(entry, 16 - this->root_entry.deep); + } +} + +/* + * Because we're only reading memory here, and that even quite fast we do not need to lock the register lock. + * Even if a block gets changed, it will not be deleted immediately. So we should finish reading first before that memory get freed. + */ +void* ip_router::resolve(const sockaddr_storage &address) const { + uint8_t address_chunks[16]; + address_to_chunks(address_chunks, address); + const ip_router::route_entry* current_chunk = &this->root_entry; + + std::lock_guard lock{this->entry_lock}; + //std::shared_lock lock{this->entry_lock}; + + size_t byte_index{0}; + while(true) { + byte_index += current_chunk->deep; + if(byte_index == 16) break; + assert(byte_index < 16); + + current_chunk = (ip_router::route_entry*) current_chunk->data[address_chunks[byte_index - 1]]; + }; + + if(memcmp(address_chunks, current_chunk->previous_chunks, 15) != 0) + return nullptr; /* route does not match */ + + return current_chunk->data[address_chunks[15]]; +} + +bool ip_router::register_route(const sockaddr_storage &address, void *target, void ** old_target) { + uint8_t address_chunks[16]; + address_to_chunks(address_chunks, address); + + void* _temp_old_target{}; + if(!old_target) + old_target = &_temp_old_target; + + ip_router::route_entry* current_chunk = &this->root_entry; + std::lock_guard rlock{this->register_lock}; + + size_t byte_index{0}; + while(true) { + byte_index += current_chunk->deep; + if(byte_index == 16) break; + assert(byte_index < 16); + + /* for the first iteration no previous_chunks check for "current_chunk" is needed because it will always match! */ + auto& next_chunk = (ip_router::route_entry*&) current_chunk->data[address_chunks[byte_index - 1]]; + if(next_chunk->is_const_entry()) { + /* perfect, lets allocate our own end and we're done */ + //assert(next_chunk == &ip_rounter::recursive_ends[15 - index - 1]); + + auto allocated_entry = this->create_8bit_entry(byte_index, true); + if(!allocated_entry) return false; + + memcpy(allocated_entry->previous_chunks, address_chunks, 15); + + /* no lock needed here, just a pointer exchange */ + next_chunk = allocated_entry; + current_chunk->use_count++; + current_chunk = next_chunk; + break; /* end chunk now */ + } else if(next_chunk->deep > 1) { + ssize_t unmatch_index{-1}; + for(size_t i{0}; i < next_chunk->deep - 1; i++) { + if(next_chunk->previous_chunks[byte_index + i] != address_chunks[byte_index + i]) { + unmatch_index = i; + break; + } + } + + if(unmatch_index >= 0) { + auto allocated_entry = this->create_8bit_entry(byte_index + unmatch_index, false); + if(!allocated_entry) return false; + + allocated_entry->deep = unmatch_index + 1; + allocated_entry->use_count++; + allocated_entry->data[next_chunk->previous_chunks[byte_index + unmatch_index]] = next_chunk; + memcpy(allocated_entry->previous_chunks, address_chunks, 15); + + { + std::lock_guard elock{this->entry_lock}; + next_chunk->deep = next_chunk->deep - unmatch_index - 1; + next_chunk = allocated_entry; + } + current_chunk = next_chunk; + continue; + } else { + /* every bit matched we also have this nice jump */ + } + } + + current_chunk = next_chunk; + } + + *old_target = std::exchange(current_chunk->data[address_chunks[15]], target); + if(!*old_target) current_chunk->use_count++; + return true; +} + +ip_router::route_entry *ip_router::create_8bit_entry(size_t level, bool end_entry) { + ip_router::route_entry *result; + + if(this->unused_nodes.empty()) + result = new ip_router::route_entry{}; + else { + result = this->unused_nodes.front(); + this->unused_nodes.pop_front(); + } + + result->use_count = 0; + if(end_entry) { + /* this is an end chunk now */ + result->deep = 16 - level; + for(auto& data : result->data) + data = nullptr; + } else { + assert(level <= 14); + result->deep = 1; + + auto pointer = &ip_router::recursive_ends[15 - level - 1]; + for(auto& data : result->data) + data = pointer; + } + + return result; +} + +void *ip_router::reset_route(const sockaddr_storage &address) { + uint8_t address_chunks[16]; + address_to_chunks(address_chunks, address); + + ip_router::route_entry* current_chunk{&this->root_entry}; + std::lock_guard rlock{this->register_lock}; + + { + size_t byte_index{0}; + while(true) { + byte_index += current_chunk->deep; + if(byte_index == 16) break; + assert(byte_index < 16); + + current_chunk = (ip_router::route_entry*) current_chunk->data[address_chunks[byte_index - 1]]; + if(current_chunk->is_const_entry()) return nullptr; + }; + + if(memcmp(address_chunks, current_chunk->previous_chunks, 15) != 0) + return nullptr; /* route does not match */ + } + + auto old = current_chunk->data[address_chunks[15]]; + if(!old) return nullptr; + + if(--current_chunk->use_count == 0) { + while(true) { + size_t byte_index{0}; + + current_chunk = &this->root_entry; + while(true) { + byte_index += current_chunk->deep; + if(byte_index == 16) break; + assert(byte_index < 16); + + auto& next_chunk = (ip_router::route_entry*&) current_chunk->data[address_chunks[byte_index - 1]]; + if(next_chunk->deep + byte_index == 16) { + assert(next_chunk->use_count == 0); + this->unused_nodes.push_back(next_chunk); + + /* this is the last chunk */ + next_chunk = &ip_router::recursive_ends[15 - byte_index]; + if(--current_chunk->use_count > 0) goto exit_delete_loop; + } + + current_chunk = next_chunk; + } + } + + exit_delete_loop:; + } + return old; +} + +void ip_router::cleanup_cache() { + std::lock_guard rlock{this->register_lock}; + for(auto node : this->unused_nodes) + delete node; + this->unused_nodes.clear(); +} + +bool ip_router::validate_chunk_entry(const ip_router::route_entry* current_entry, size_t level) const { + if(level == 0) + return true; + + if(current_entry->is_const_entry() && level != 16) + return level == current_entry->deep; + + auto default_pointer = &ip_router::recursive_ends[level - 1]; + for(const auto& data_ptr : current_entry->data) { + if(data_ptr == default_pointer) + continue; + + if(!this->validate_chunk_entry((const ip_router::route_entry*) data_ptr, level - current_entry->deep)) + return false; + } + + return true; +} + +bool ip_router::validate_tree() const { + std::lock_guard rlock{this->register_lock}; + + /* first lets validate all const chunks */ + for(size_t index{0}; index < 16; index++) { + if(!ip_router::recursive_ends[index].is_const_entry()) + return false; + + if(ip_router::recursive_ends[index].deep != index + 1) + return false; + + for(const auto& data_ptr : ip_router::recursive_ends[index].data) + if(data_ptr) + return false; + } + + /* not lets check our tree */ + return this->validate_chunk_entry(&this->root_entry, 16); +} + +size_t ip_router::chunk_memory(const ip_router::route_entry *current_entry, size_t level) const { + size_t result{sizeof(ip_router::route_entry)}; + + level -= current_entry->deep; + if(level > 0) { + for(const auto& data_ptr : current_entry->data) { + auto entry = (const ip_router::route_entry*) data_ptr; + if(entry->is_const_entry()) continue; + + result += chunk_memory(entry, level); + } + } + + return result; +} + +size_t ip_router::used_memory() const { + return this->chunk_memory(&this->root_entry, 16); +} + +std::string ip_router::print_as_string() const { + std::string result{}; + this->print_as_string(result, "", &this->root_entry, 16); + result += "Memory used: " + std::to_string(this->used_memory() / 1024) + "kb"; + return result; +} + +template +std::string n2hexstr(I w, size_t hex_len = sizeof(I)<<1) { + if(w == 0) return "0x0"; + static const char* digits = "0123456789ABCDEF"; + std::string rc(hex_len,'0'); + for (size_t i=0, j=(hex_len-1)*4 ; i> j) & 0x0f]; + size_t lz{0}; + for(;lz < rc.length() && rc[lz] == '0'; lz++); + return "0x" + rc.substr(lz); +} + +template +inline std::string padded_num(T value) { + auto result = std::to_string(value); + return result.length() > 3 ? "" : std::string(3 - result.length(), '0') + result; +} + +void ip_router::print_as_string(std::string& result, const std::string& indent, const ip_router::route_entry *current_entry, size_t level) const { + level -= current_entry->deep; + + size_t range_begin{0}; + for(size_t i = 0; i <= 0xFF; i++) { + auto entry = (const ip_router::route_entry*) current_entry->data[i]; + if(level == 0 ? !entry : entry->is_const_entry()) continue; + + if(i > 0) { + if(range_begin < i - 1) + result += indent + padded_num(range_begin) + ".." + padded_num(i - 1) + ": empty\n"; + else if(range_begin == i - 1) + result += indent + padded_num(range_begin) + ": empty\n"; + } + + if(level == 0) { + result += indent + padded_num(i) + ": " + n2hexstr((uintptr_t) entry) + "\n"; + } else { + result += indent + padded_num(i) + ": " + n2hexstr((uintptr_t) entry) + " (used by: " + std::to_string(entry->use_count) + ", deph: " + std::to_string(entry->deep) + ")\n"; + this->print_as_string(result, indent + " ", entry, level); + } + range_begin = i + 1; + } + + if(range_begin < 0xFF) + result += indent + padded_num(range_begin) + "..255: empty\n"; } \ No newline at end of file diff --git a/src/misc/ip_router.h b/src/misc/ip_router.h index bb295dd..9c2bc01 100644 --- a/src/misc/ip_router.h +++ b/src/misc/ip_router.h @@ -1,75 +1,75 @@ -#pragma once - -#include -#include -#include -#include -#include "./spin_lock.h" -#include - -namespace ts::network { - class ip_router { - /* currently its not possible to change this! */ - constexpr static auto bits_per_entry{8U}; /* must be a multiple of 2! */ - constexpr static auto total_bits{128U}; - public: - struct route_entry { - constexpr static auto const_flag_mask = 0x80000000ULL; - route_entry() noexcept = default; - ~route_entry() = default; - - struct { - uint8_t deep; - uint8_t previous_chunks[(total_bits - bits_per_entry) / 8]; /* subtract the last entry because we do not need a special check there */ - uint32_t use_count; /* could be size_t as well :) */ - } __attribute__((packed)); - - void* data[1U << (bits_per_entry + 1)]; - - [[nodiscard]] inline bool is_const_entry() const { return (this->use_count & const_flag_mask) > 0; } - }; - - static_assert(std::is_trivially_destructible::value); - static_assert(std::is_trivially_constructible::value); - - - ip_router(); - ~ip_router(); - - void cleanup_cache(); - [[nodiscard]] bool validate_tree() const; - [[nodiscard]] size_t used_memory() const; - [[nodiscard]] std::string print_as_string() const; - /** - * @return Whatever the route register succeeded to initialize - */ - bool register_route(const sockaddr_storage& /* address */, void* /* target */, void** /* old route */ = nullptr); - - /** - * @return The old pointer from the route - */ - void* reset_route(const sockaddr_storage& /* address */); - - [[nodiscard]] void* resolve(const sockaddr_storage& /* address */) const; - private: - /** - * Value 0 will be an empty end - * Value 1 will points with all pointers to value 0 - * Value 2 will points with all pointers to value 1 - * ... and so on - */ - static std::array recursive_ends; - - std::mutex register_lock{}; - std::deque unused_nodes{}; - - //std::shared_mutex entry_lock{}; - spin_lock entry_lock{}; - route_entry root_entry{}; - - route_entry* create_8bit_entry(size_t /* level */, bool /* as end entry */); - bool validate_chunk_entry(const ip_router::route_entry* /* current entry */, size_t /* level */) const; - size_t chunk_memory(const ip_router::route_entry* /* current entry */, size_t /* level */) const; - void print_as_string(std::string& /* output */, const std::string& /* indent */, const ip_router::route_entry* /* current entry */, size_t /* level */) const; - }; +#pragma once + +#include +#include +#include +#include +#include "./spin_lock.h" +#include + +namespace ts::network { + class ip_router { + /* currently its not possible to change this! */ + constexpr static auto bits_per_entry{8U}; /* must be a multiple of 2! */ + constexpr static auto total_bits{128U}; + public: + struct route_entry { + constexpr static auto const_flag_mask = 0x80000000ULL; + route_entry() noexcept = default; + ~route_entry() = default; + + struct { + uint8_t deep; + uint8_t previous_chunks[(total_bits - bits_per_entry) / 8]; /* subtract the last entry because we do not need a special check there */ + uint32_t use_count; /* could be size_t as well :) */ + } __attribute__((packed)); + + void* data[1U << (bits_per_entry + 1)]; + + [[nodiscard]] inline bool is_const_entry() const { return (this->use_count & const_flag_mask) > 0; } + }; + + static_assert(std::is_trivially_destructible::value); + static_assert(std::is_trivially_constructible::value); + + + ip_router(); + ~ip_router(); + + void cleanup_cache(); + [[nodiscard]] bool validate_tree() const; + [[nodiscard]] size_t used_memory() const; + [[nodiscard]] std::string print_as_string() const; + /** + * @return Whatever the route register succeeded to initialize + */ + bool register_route(const sockaddr_storage& /* address */, void* /* target */, void** /* old route */ = nullptr); + + /** + * @return The old pointer from the route + */ + void* reset_route(const sockaddr_storage& /* address */); + + [[nodiscard]] void* resolve(const sockaddr_storage& /* address */) const; + private: + /** + * Value 0 will be an empty end + * Value 1 will points with all pointers to value 0 + * Value 2 will points with all pointers to value 1 + * ... and so on + */ + static std::array recursive_ends; + + std::mutex register_lock{}; + std::deque unused_nodes{}; + + //std::shared_mutex entry_lock{}; + spin_lock entry_lock{}; + route_entry root_entry{}; + + route_entry* create_8bit_entry(size_t /* level */, bool /* as end entry */); + bool validate_chunk_entry(const ip_router::route_entry* /* current entry */, size_t /* level */) const; + size_t chunk_memory(const ip_router::route_entry* /* current entry */, size_t /* level */) const; + void print_as_string(std::string& /* output */, const std::string& /* indent */, const ip_router::route_entry* /* current entry */, size_t /* level */) const; + }; } \ No newline at end of file diff --git a/test/ip_router.cpp b/test/ip_router.cpp index d05d6a7..0cc99bd 100644 --- a/test/ip_router.cpp +++ b/test/ip_router.cpp @@ -1,173 +1,173 @@ -#include -#include -#include -#include -#include "src/misc/ip_router.h" - -using namespace ts::network; - -inline sockaddr_storage address(const std::string& address) { - sockaddr_storage result{}; - memset(&result, 0, sizeof(result)); - net::resolve_address(address, result); - return result; -} - -inline void resolve(ip_router& router, const std::string& address_str) { - std::cout << address_str << " -> " << router.resolve(address(address_str)) << std::endl; -} - -/* -IPv6 & 15000 iterations -0% misses: -Time per resolve: 608ns -50% misses: -Time per resolve: 448ns -100% misses: -Time per resolve: 30ns - -IPv6 & 15000 iterations & shared mutex -0% misses: -Time per resolve: 848ns -50% misses: -Time per resolve: 602ns -100% misses: -Time per resolve: 39ns - -IPv6 & 15000 iterations & spin lock -0% misses: -Time per resolve: 743ns -50% misses: -Time per resolve: 510ns -100% misses: -Time per resolve: 29ns - - -New system: -0% misses: -Time per resolve: 149ns -Used memory: 67.155kb -50% misses: -Time per resolve: 71ns -Used memory: 32.501kb -100% misses: -Time per resolve: 14ns -Used memory: 4kb - */ - -inline void benchmark_resolve() { - const auto iterations = 500; - - std::vector addresses{}; - - /* generate addresses */ - { - const auto rnd = []{ - return (uint32_t) ((uint8_t) ::rand()); - }; - - for(int i = 0; i < iterations; i++) { - auto& address = addresses.emplace_back(); - address.ss_family = AF_INET; - - auto address4 = (sockaddr_in*) &address; - address4->sin_addr.s_addr = (rnd() << 24U) | (rnd() << 16U) | (rnd() << 8U) | (rnd() << 0U); - } -#if 0 - for(int i = 0; i < iterations; i++) { - auto& address = addresses.emplace_back(); - address.ss_family = AF_INET6; - - auto address6 = (sockaddr_in6*) &address; - for(auto& part : address6->sin6_addr.__in6_u.__u6_addr8) - part = rand(); - } -#endif - - } - - for(auto missPercentage : {0, 50, 100}) { - std::cout << missPercentage << "% misses:\n"; - - ip_router rounter{}; - for(auto& address : addresses) { - if((::rand() % 100) < missPercentage) continue; - - rounter.register_route(address, &address, nullptr); - } - - /* resolve */ - { - auto begin = std::chrono::system_clock::now(); - size_t index{0}; - for(auto& address : addresses) { - index++; - auto result = rounter.resolve(address); - (void) result; - //if(missPercentage == 0 && memcmp(result, &address, sizeof(address)) != 0) - // __asm__("nop"); - } - auto end = std::chrono::system_clock::now(); - std::cout << "Total time: " << std::chrono::ceil(end - begin).count() << "us\n"; - std::cout << "Time per resolve: " << std::chrono::ceil(end - begin).count() / iterations << "ns\n"; - std::cout << "Used memory: " << rounter.used_memory() / 1024 << "kb" << std::endl; - __asm__("nop"); - - if(missPercentage == 0) - ;//std::cout << "Tree:\n" << rounter.print_as_string() << "\n"; - } - } - - - - /* resolve */ - { - std::cout << "List iterate:\n"; - auto begin = std::chrono::system_clock::now(); - for(auto& address : addresses) { - for(auto& addressB : addresses) - if(memcmp(&address, &addressB, sizeof(sockaddr_storage)) == 0) - break; - } - auto end = std::chrono::system_clock::now(); - std::cout << "Total time: " << std::chrono::ceil(end - begin).count() << "us\n"; - std::cout << "Time per resolve: " << std::chrono::ceil(end - begin).count() / iterations << "ns\n"; - } -} - -int main() { -#if 0 - ip_router rounter{}; - assert(rounter.validate_tree()); - - resolve(rounter, "127.0.0.1"); - - rounter.register_route(address("127.0.0.1"), (void*) 0x127001); - assert(rounter.validate_tree()); - resolve(rounter, "127.0.0.1"); - - rounter.register_route(address("127.0.0.2"), (void*) 0x127002); - assert(rounter.validate_tree()); - resolve(rounter, "127.0.0.2"); - resolve(rounter, "127.0.1.2"); - - rounter.register_route(address("127.0.1.2"), (void*) 0x127012); - resolve(rounter, "127.0.0.2"); - resolve(rounter, "127.0.1.2"); - - std::cout << "Tree:\n" << rounter.print_as_string() << "\n"; - - rounter.reset_route(address("127.0.0.1")); - assert(rounter.validate_tree()); - resolve(rounter, "127.0.0.1"); - resolve(rounter, "127.0.0.2"); - - rounter.reset_route(address("127.0.0.2")); - assert(rounter.validate_tree()); - resolve(rounter, "127.0.0.1"); - resolve(rounter, "127.0.0.2"); -#endif - - benchmark_resolve(); - return 0; +#include +#include +#include +#include +#include "src/misc/ip_router.h" + +using namespace ts::network; + +inline sockaddr_storage address(const std::string& address) { + sockaddr_storage result{}; + memset(&result, 0, sizeof(result)); + net::resolve_address(address, result); + return result; +} + +inline void resolve(ip_router& router, const std::string& address_str) { + std::cout << address_str << " -> " << router.resolve(address(address_str)) << std::endl; +} + +/* +IPv6 & 15000 iterations +0% misses: +Time per resolve: 608ns +50% misses: +Time per resolve: 448ns +100% misses: +Time per resolve: 30ns + +IPv6 & 15000 iterations & shared mutex +0% misses: +Time per resolve: 848ns +50% misses: +Time per resolve: 602ns +100% misses: +Time per resolve: 39ns + +IPv6 & 15000 iterations & spin lock +0% misses: +Time per resolve: 743ns +50% misses: +Time per resolve: 510ns +100% misses: +Time per resolve: 29ns + + +New system: +0% misses: +Time per resolve: 149ns +Used memory: 67.155kb +50% misses: +Time per resolve: 71ns +Used memory: 32.501kb +100% misses: +Time per resolve: 14ns +Used memory: 4kb + */ + +inline void benchmark_resolve() { + const auto iterations = 500; + + std::vector addresses{}; + + /* generate addresses */ + { + const auto rnd = []{ + return (uint32_t) ((uint8_t) ::rand()); + }; + + for(int i = 0; i < iterations; i++) { + auto& address = addresses.emplace_back(); + address.ss_family = AF_INET; + + auto address4 = (sockaddr_in*) &address; + address4->sin_addr.s_addr = (rnd() << 24U) | (rnd() << 16U) | (rnd() << 8U) | (rnd() << 0U); + } +#if 0 + for(int i = 0; i < iterations; i++) { + auto& address = addresses.emplace_back(); + address.ss_family = AF_INET6; + + auto address6 = (sockaddr_in6*) &address; + for(auto& part : address6->sin6_addr.__in6_u.__u6_addr8) + part = rand(); + } +#endif + + } + + for(auto missPercentage : {0, 50, 100}) { + std::cout << missPercentage << "% misses:\n"; + + ip_router rounter{}; + for(auto& address : addresses) { + if((::rand() % 100) < missPercentage) continue; + + rounter.register_route(address, &address, nullptr); + } + + /* resolve */ + { + auto begin = std::chrono::system_clock::now(); + size_t index{0}; + for(auto& address : addresses) { + index++; + auto result = rounter.resolve(address); + (void) result; + //if(missPercentage == 0 && memcmp(result, &address, sizeof(address)) != 0) + // __asm__("nop"); + } + auto end = std::chrono::system_clock::now(); + std::cout << "Total time: " << std::chrono::ceil(end - begin).count() << "us\n"; + std::cout << "Time per resolve: " << std::chrono::ceil(end - begin).count() / iterations << "ns\n"; + std::cout << "Used memory: " << rounter.used_memory() / 1024 << "kb" << std::endl; + __asm__("nop"); + + if(missPercentage == 0) + ;//std::cout << "Tree:\n" << rounter.print_as_string() << "\n"; + } + } + + + + /* resolve */ + { + std::cout << "List iterate:\n"; + auto begin = std::chrono::system_clock::now(); + for(auto& address : addresses) { + for(auto& addressB : addresses) + if(memcmp(&address, &addressB, sizeof(sockaddr_storage)) == 0) + break; + } + auto end = std::chrono::system_clock::now(); + std::cout << "Total time: " << std::chrono::ceil(end - begin).count() << "us\n"; + std::cout << "Time per resolve: " << std::chrono::ceil(end - begin).count() / iterations << "ns\n"; + } +} + +int main() { +#if 0 + ip_router rounter{}; + assert(rounter.validate_tree()); + + resolve(rounter, "127.0.0.1"); + + rounter.register_route(address("127.0.0.1"), (void*) 0x127001); + assert(rounter.validate_tree()); + resolve(rounter, "127.0.0.1"); + + rounter.register_route(address("127.0.0.2"), (void*) 0x127002); + assert(rounter.validate_tree()); + resolve(rounter, "127.0.0.2"); + resolve(rounter, "127.0.1.2"); + + rounter.register_route(address("127.0.1.2"), (void*) 0x127012); + resolve(rounter, "127.0.0.2"); + resolve(rounter, "127.0.1.2"); + + std::cout << "Tree:\n" << rounter.print_as_string() << "\n"; + + rounter.reset_route(address("127.0.0.1")); + assert(rounter.validate_tree()); + resolve(rounter, "127.0.0.1"); + resolve(rounter, "127.0.0.2"); + + rounter.reset_route(address("127.0.0.2")); + assert(rounter.validate_tree()); + resolve(rounter, "127.0.0.1"); + resolve(rounter, "127.0.0.2"); +#endif + + benchmark_resolve(); + return 0; } \ No newline at end of file