diff --git a/libraries/http_server/http_server/dynamic_routing_table.hh b/libraries/http_server/http_server/dynamic_routing_table.hh index 783c262..7448371 100644 --- a/libraries/http_server/http_server/dynamic_routing_table.hh +++ b/libraries/http_server/http_server/dynamic_routing_table.hh @@ -5,26 +5,41 @@ #include #include #include +#include namespace li { namespace internal { -template struct drt_node { +/** + * A memory pool for drt_node objects that manages node allocation and lifetime + */ +template struct drt_node_pool { + template T* allocate(Args&&... args) noexcept { + auto new_node = std::make_unique(std::forward(args)...); + T* ptr = new_node.get(); + pool_.emplace_back(std::move(new_node)); + return ptr; + } + + std::vector> pool_; +}; - drt_node() : v_{0, nullptr} {} +template struct drt_node { + drt_node() noexcept : pool_(nullptr), v_{0, nullptr} {} + drt_node(drt_node_pool& pool) noexcept : pool_(pool), v_{0, nullptr} {} struct iterator { const drt_node* ptr; std::string_view first; V second; - auto operator->() { return this; } - bool operator==(const iterator& b) const { return this->ptr == b.ptr; } - bool operator!=(const iterator& b) const { return this->ptr != b.ptr; } + auto operator->() noexcept { return this; } + bool operator==(const iterator& b) const noexcept { return this->ptr == b.ptr; } + bool operator!=(const iterator& b) const noexcept { return this->ptr != b.ptr; } }; - auto end() const { return iterator{nullptr, std::string_view(), V()}; } + auto end() const noexcept { return iterator{nullptr, std::string_view(), V()}; } auto& find_or_create(std::string_view r, unsigned int c) { if (c == r.size()) @@ -37,17 +52,10 @@ template struct drt_node { c++; std::string_view k = r.substr(s, c - s); - auto it = children_.find(k); - if (it != children_.end()) - return children_[k]->find_or_create(r, c); - else { - auto new_node = std::make_shared(); - children_shared_pointers_.push_back(new_node); - children_.insert({k, new_node.get()}); - return new_node->find_or_create(r, c); + if (children_.find(k) == children_.end()) { + children_[k] = pool_.allocate(pool_); } - - return v_; + return children_[k]->find_or_create(r, c); } template void for_all_routes(F f, std::string prefix = "") const { @@ -56,13 +64,13 @@ template struct drt_node { else { if (prefix.size() && prefix.back() != '/') prefix += '/'; - for (auto pair : children_) - pair.second->for_all_routes(f, prefix + std::string(pair.first)); + for (const auto& kv : children_) + kv.second->for_all_routes(f, prefix + std::string(kv.first)); } } // Find a route. - iterator find(const std::string_view& r, unsigned int c) const { + iterator find(const std::string_view& r, unsigned int c) const noexcept { // We found the route r. if ((c == r.size() and v_.handler != nullptr) or (children_.size() == 0)) return iterator{this, r, v_}; @@ -94,7 +102,7 @@ template struct drt_node { { // if one child is a url param {{param_name}}, choose it - for (auto& kv : children_) { + for (const auto& kv : children_) { auto name = kv.first; if (name.size() > 4 and name[0] == '{' and name[1] == '{' and name[name.size() - 2] == '}' and name[name.size() - 1] == '}') @@ -106,32 +114,46 @@ template struct drt_node { V v_; std::unordered_map children_; - std::vector> children_shared_pointers_; + drt_node_pool& pool_; +}; + +template struct dynamic_routing_table_data { + dynamic_routing_table_data() noexcept : root(pool_) {} + + std::unordered_set paths; + drt_node root; + +private: + drt_node_pool> pool_; }; } // namespace internal +/** + * A dynamic routing table that supports route registration and lookup. + */ template struct dynamic_routing_table { + dynamic_routing_table() noexcept + : data_(std::make_shared>()) {} + dynamic_routing_table(const dynamic_routing_table& other) noexcept : data_(other.data_) {} - // Find a route and return reference to a procedure. - auto& operator[](const std::string_view& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); - } - auto& operator[](const std::string& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); + dynamic_routing_table& operator=(const dynamic_routing_table& other) noexcept { + if (this != &other) { + data_ = other.data_; + } + return *this; } - // Find a route and return an iterator. - auto find(const std::string_view& r) const { return root.find(r, 0); } - - template void for_all_routes(F f) const { root.for_all_routes(f); } - auto end() const { return root.end(); } + auto& operator[](const std::string_view& r) { return this->operator[](std::string(r)); } + auto& operator[](const std::string& s) { + auto [itr, is_inserted] = data_->paths.emplace(s); + return data_->root.find_or_create(*itr, 0); + } + auto find(const std::string_view& r) const noexcept { return data_->root.find(r, 0); } + template void for_all_routes(F f) const { data_->root.for_all_routes(f); } + auto end() const noexcept { return data_->root.end(); } - std::vector> strings; - internal::drt_node root; +private: + std::shared_ptr> data_; }; } // namespace li diff --git a/single_headers/lithium.hh b/single_headers/lithium.hh index 69acc41..5ad0b83 100644 --- a/single_headers/lithium.hh +++ b/single_headers/lithium.hh @@ -73,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -6513,21 +6514,35 @@ namespace li { namespace internal { -template struct drt_node { +/** + * A memory pool for drt_node objects that manages node allocation and lifetime + */ +template struct drt_node_pool { + template T* allocate(Args&&... args) noexcept { + auto new_node = std::make_unique(std::forward(args)...); + T* ptr = new_node.get(); + pool_.emplace_back(std::move(new_node)); + return ptr; + } + + std::vector> pool_; +}; - drt_node() : v_{0, nullptr} {} +template struct drt_node { + drt_node() noexcept : pool_(nullptr), v_{0, nullptr} {} + drt_node(drt_node_pool& pool) noexcept : pool_(pool), v_{0, nullptr} {} struct iterator { const drt_node* ptr; std::string_view first; V second; - auto operator->() { return this; } - bool operator==(const iterator& b) const { return this->ptr == b.ptr; } - bool operator!=(const iterator& b) const { return this->ptr != b.ptr; } + auto operator->() noexcept { return this; } + bool operator==(const iterator& b) const noexcept { return this->ptr == b.ptr; } + bool operator!=(const iterator& b) const noexcept { return this->ptr != b.ptr; } }; - auto end() const { return iterator{nullptr, std::string_view(), V()}; } + auto end() const noexcept { return iterator{nullptr, std::string_view(), V()}; } auto& find_or_create(std::string_view r, unsigned int c) { if (c == r.size()) @@ -6540,17 +6555,10 @@ template struct drt_node { c++; std::string_view k = r.substr(s, c - s); - auto it = children_.find(k); - if (it != children_.end()) - return children_[k]->find_or_create(r, c); - else { - auto new_node = std::make_shared(); - children_shared_pointers_.push_back(new_node); - children_.insert({k, new_node.get()}); - return new_node->find_or_create(r, c); + if (children_.find(k) == children_.end()) { + children_[k] = pool_.allocate(pool_); } - - return v_; + return children_[k]->find_or_create(r, c); } template void for_all_routes(F f, std::string prefix = "") const { @@ -6559,13 +6567,13 @@ template struct drt_node { else { if (prefix.size() && prefix.back() != '/') prefix += '/'; - for (auto pair : children_) - pair.second->for_all_routes(f, prefix + std::string(pair.first)); + for (const auto& kv : children_) + kv.second->for_all_routes(f, prefix + std::string(kv.first)); } } // Find a route. - iterator find(const std::string_view& r, unsigned int c) const { + iterator find(const std::string_view& r, unsigned int c) const noexcept { // We found the route r. if ((c == r.size() and v_.handler != nullptr) or (children_.size() == 0)) return iterator{this, r, v_}; @@ -6597,7 +6605,7 @@ template struct drt_node { { // if one child is a url param {{param_name}}, choose it - for (auto& kv : children_) { + for (const auto& kv : children_) { auto name = kv.first; if (name.size() > 4 and name[0] == '{' and name[1] == '{' and name[name.size() - 2] == '}' and name[name.size() - 1] == '}') @@ -6609,32 +6617,46 @@ template struct drt_node { V v_; std::unordered_map children_; - std::vector> children_shared_pointers_; + drt_node_pool& pool_; +}; + +template struct dynamic_routing_table_data { + dynamic_routing_table_data() noexcept : root(pool_) {} + + std::unordered_set paths; + drt_node root; + +private: + drt_node_pool> pool_; }; } // namespace internal +/** + * A dynamic routing table that supports route registration and lookup. + */ template struct dynamic_routing_table { + dynamic_routing_table() noexcept + : data_(std::make_shared>()) {} + dynamic_routing_table(const dynamic_routing_table& other) noexcept : data_(other.data_) {} - // Find a route and return reference to a procedure. - auto& operator[](const std::string_view& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); - } - auto& operator[](const std::string& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); + dynamic_routing_table& operator=(const dynamic_routing_table& other) noexcept { + if (this != &other) { + data_ = other.data_; + } + return *this; } - // Find a route and return an iterator. - auto find(const std::string_view& r) const { return root.find(r, 0); } - - template void for_all_routes(F f) const { root.for_all_routes(f); } - auto end() const { return root.end(); } + auto& operator[](const std::string_view& r) { return this->operator[](std::string(r)); } + auto& operator[](const std::string& s) { + auto [itr, is_inserted] = data_->paths.emplace(s); + return data_->root.find_or_create(*itr, 0); + } + auto find(const std::string_view& r) const noexcept { return data_->root.find(r, 0); } + template void for_all_routes(F f) const { data_->root.for_all_routes(f); } + auto end() const noexcept { return data_->root.end(); } - std::vector> strings; - internal::drt_node root; +private: + std::shared_ptr> data_; }; } // namespace li diff --git a/single_headers/lithium_http_server.hh b/single_headers/lithium_http_server.hh index e475f45..0f0bb11 100644 --- a/single_headers/lithium_http_server.hh +++ b/single_headers/lithium_http_server.hh @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -3330,21 +3331,35 @@ namespace li { namespace internal { -template struct drt_node { +/** + * A memory pool for drt_node objects that manages node allocation and lifetime + */ +template struct drt_node_pool { + template T* allocate(Args&&... args) noexcept { + auto new_node = std::make_unique(std::forward(args)...); + T* ptr = new_node.get(); + pool_.emplace_back(std::move(new_node)); + return ptr; + } - drt_node() : v_{0, nullptr} {} + std::vector> pool_; +}; + +template struct drt_node { + drt_node() noexcept : pool_(nullptr), v_{0, nullptr} {} + drt_node(drt_node_pool& pool) noexcept : pool_(pool), v_{0, nullptr} {} struct iterator { const drt_node* ptr; std::string_view first; V second; - auto operator->() { return this; } - bool operator==(const iterator& b) const { return this->ptr == b.ptr; } - bool operator!=(const iterator& b) const { return this->ptr != b.ptr; } + auto operator->() noexcept { return this; } + bool operator==(const iterator& b) const noexcept { return this->ptr == b.ptr; } + bool operator!=(const iterator& b) const noexcept { return this->ptr != b.ptr; } }; - auto end() const { return iterator{nullptr, std::string_view(), V()}; } + auto end() const noexcept { return iterator{nullptr, std::string_view(), V()}; } auto& find_or_create(std::string_view r, unsigned int c) { if (c == r.size()) @@ -3357,17 +3372,10 @@ template struct drt_node { c++; std::string_view k = r.substr(s, c - s); - auto it = children_.find(k); - if (it != children_.end()) - return children_[k]->find_or_create(r, c); - else { - auto new_node = std::make_shared(); - children_shared_pointers_.push_back(new_node); - children_.insert({k, new_node.get()}); - return new_node->find_or_create(r, c); + if (children_.find(k) == children_.end()) { + children_[k] = pool_.allocate(pool_); } - - return v_; + return children_[k]->find_or_create(r, c); } template void for_all_routes(F f, std::string prefix = "") const { @@ -3376,13 +3384,13 @@ template struct drt_node { else { if (prefix.size() && prefix.back() != '/') prefix += '/'; - for (auto pair : children_) - pair.second->for_all_routes(f, prefix + std::string(pair.first)); + for (const auto& kv : children_) + kv.second->for_all_routes(f, prefix + std::string(kv.first)); } } // Find a route. - iterator find(const std::string_view& r, unsigned int c) const { + iterator find(const std::string_view& r, unsigned int c) const noexcept { // We found the route r. if ((c == r.size() and v_.handler != nullptr) or (children_.size() == 0)) return iterator{this, r, v_}; @@ -3414,7 +3422,7 @@ template struct drt_node { { // if one child is a url param {{param_name}}, choose it - for (auto& kv : children_) { + for (const auto& kv : children_) { auto name = kv.first; if (name.size() > 4 and name[0] == '{' and name[1] == '{' and name[name.size() - 2] == '}' and name[name.size() - 1] == '}') @@ -3426,32 +3434,46 @@ template struct drt_node { V v_; std::unordered_map children_; - std::vector> children_shared_pointers_; + drt_node_pool& pool_; +}; + +template struct dynamic_routing_table_data { + dynamic_routing_table_data() noexcept : root(pool_) {} + + std::unordered_set paths; + drt_node root; + +private: + drt_node_pool> pool_; }; } // namespace internal +/** + * A dynamic routing table that supports route registration and lookup. + */ template struct dynamic_routing_table { + dynamic_routing_table() noexcept + : data_(std::make_shared>()) {} + dynamic_routing_table(const dynamic_routing_table& other) noexcept : data_(other.data_) {} - // Find a route and return reference to a procedure. - auto& operator[](const std::string_view& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); - } - auto& operator[](const std::string& r) { - strings.push_back(std::make_shared(r)); - std::string_view r2(*strings.back()); - return root.find_or_create(r2, 0); + dynamic_routing_table& operator=(const dynamic_routing_table& other) noexcept { + if (this != &other) { + data_ = other.data_; + } + return *this; } - // Find a route and return an iterator. - auto find(const std::string_view& r) const { return root.find(r, 0); } - - template void for_all_routes(F f) const { root.for_all_routes(f); } - auto end() const { return root.end(); } + auto& operator[](const std::string_view& r) { return this->operator[](std::string(r)); } + auto& operator[](const std::string& s) { + auto [itr, is_inserted] = data_->paths.emplace(s); + return data_->root.find_or_create(*itr, 0); + } + auto find(const std::string_view& r) const noexcept { return data_->root.find(r, 0); } + template void for_all_routes(F f) const { data_->root.for_all_routes(f); } + auto end() const noexcept { return data_->root.end(); } - std::vector> strings; - internal::drt_node root; +private: + std::shared_ptr> data_; }; } // namespace li