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