From dd0a6d6144550ff75a7c66d267351bc98d2e3f39 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 23 Jul 2026 19:35:52 +0300 Subject: [PATCH 01/35] implement hook --- runtime-common/core/std/intrusive-list.h | 83 ++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 runtime-common/core/std/intrusive-list.h diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h new file mode 100644 index 0000000000..61f5c072b6 --- /dev/null +++ b/runtime-common/core/std/intrusive-list.h @@ -0,0 +1,83 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include +#include +#include + +namespace kphp::stl::intrusive_list { + +namespace details { + +class hook_base { + hook_base* m_prev{this}; + hook_base* m_next{this}; + + // correct usage supposes that this->is_linked() == false + void move_hook(hook_base&& other) noexcept { + if (other.is_linked()) { + m_prev = std::exchange(other.m_prev, std::addressof(other)); + m_next = std::exchange(other.m_next, std::addressof(other)); + + m_prev->m_next = this; + m_next->m_prev = this; + } + } + +public: + hook_base() noexcept = default; + + hook_base(const hook_base& /*unused*/) noexcept : hook_base() {} + + hook_base(hook_base&& other) noexcept { + move_hook(std::move(other)); + } + + hook_base& operator=(const hook_base& /*unused*/) noexcept { + return *this; + } + + hook_base& operator=(hook_base&& other) noexcept { + if (this == std::addressof(other)) { + return *this; + } + + unlink(); + move_hook(std::move(other)); + + return *this; + } + + ~hook_base() { + unlink(); + } + + bool is_linked() const noexcept { + return m_prev != this; + } + + void unlink() noexcept { + m_prev->m_next = m_next; + m_next->m_prev = m_prev; + + m_prev = this; + m_next = this; + } +}; + +} // namespace details + +struct default_tag {}; + +template +class hook : private details::hook_base {}; + +template +class intrusive_list final { + static_assert(std::is_base_of_v, T>, "T must be derived from hook"); +}; + +} // namespace kphp::stl::intrusive_list From bb47198dfae8dbb7c8863296c2d51f66b8625461 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 23 Jul 2026 19:36:24 +0300 Subject: [PATCH 02/35] apply code style --- runtime-common/core/std/intrusive-list.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 61f5c072b6..fdf096aaf1 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -19,18 +19,19 @@ class hook_base { // correct usage supposes that this->is_linked() == false void move_hook(hook_base&& other) noexcept { if (other.is_linked()) { - m_prev = std::exchange(other.m_prev, std::addressof(other)); - m_next = std::exchange(other.m_next, std::addressof(other)); + m_prev = std::exchange(other.m_prev, std::addressof(other)); + m_next = std::exchange(other.m_next, std::addressof(other)); - m_prev->m_next = this; - m_next->m_prev = this; + m_prev->m_next = this; + m_next->m_prev = this; } } public: hook_base() noexcept = default; - hook_base(const hook_base& /*unused*/) noexcept : hook_base() {} + hook_base(const hook_base& /*unused*/) noexcept + : hook_base() {} hook_base(hook_base&& other) noexcept { move_hook(std::move(other)); @@ -42,7 +43,7 @@ class hook_base { hook_base& operator=(hook_base&& other) noexcept { if (this == std::addressof(other)) { - return *this; + return *this; } unlink(); From 0edb4d58f5a810ee5903553b46e8a878e52fb0d9 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 23 Jul 2026 19:44:51 +0300 Subject: [PATCH 03/35] add assertion into hook_base --- runtime-common/core/std/intrusive-list.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index fdf096aaf1..4c406d1205 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -16,8 +17,8 @@ class hook_base { hook_base* m_prev{this}; hook_base* m_next{this}; - // correct usage supposes that this->is_linked() == false void move_hook(hook_base&& other) noexcept { + assert(!is_linked()); if (other.is_linked()) { m_prev = std::exchange(other.m_prev, std::addressof(other)); m_next = std::exchange(other.m_next, std::addressof(other)); From 7e676645d651bbc2f115544a91eefb3cca9124aa Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 23 Jul 2026 19:52:01 +0300 Subject: [PATCH 04/35] move definition of hook_base methods to cpp file --- runtime-common/core/std/intrusive-list.cpp | 54 ++++++++++++++++++++++ runtime-common/core/std/intrusive-list.h | 49 ++++---------------- 2 files changed, 62 insertions(+), 41 deletions(-) create mode 100644 runtime-common/core/std/intrusive-list.cpp diff --git a/runtime-common/core/std/intrusive-list.cpp b/runtime-common/core/std/intrusive-list.cpp new file mode 100644 index 0000000000..aeb871b362 --- /dev/null +++ b/runtime-common/core/std/intrusive-list.cpp @@ -0,0 +1,54 @@ +#include "intrusive-list.h" + +namespace kphp::stl::intrusive_list::details { + +void hook_base::move_hook(hook_base&& other) noexcept { + assert(!is_linked()); + if (other.is_linked()) { + m_prev = std::exchange(other.m_prev, std::addressof(other)); + m_next = std::exchange(other.m_next, std::addressof(other)); + + m_prev->m_next = this; + m_next->m_prev = this; + } +} + +hook_base::hook_base(const hook_base& /*unused*/) noexcept + : hook_base() {} + +hook_base::hook_base(hook_base&& other) noexcept { + move_hook(std::move(other)); +} + +hook_base& hook_base::operator=(const hook_base& /*unused*/) noexcept { + return *this; +} + +hook_base& hook_base::operator=(hook_base&& other) noexcept { + if (this == std::addressof(other)) { + return *this; + } + + unlink(); + move_hook(std::move(other)); + + return *this; +} + +hook_base::~hook_base() { + unlink(); +} + +bool hook_base::is_linked() const noexcept { + return m_prev != this; +} + +void hook_base::unlink() noexcept { + m_prev->m_next = m_next; + m_next->m_prev = m_prev; + + m_prev = this; + m_next = this; +} + +} // namespace kphp::stl::intrusive_list::details diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 4c406d1205..e82fb7479a 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -17,57 +17,24 @@ class hook_base { hook_base* m_prev{this}; hook_base* m_next{this}; - void move_hook(hook_base&& other) noexcept { - assert(!is_linked()); - if (other.is_linked()) { - m_prev = std::exchange(other.m_prev, std::addressof(other)); - m_next = std::exchange(other.m_next, std::addressof(other)); - - m_prev->m_next = this; - m_next->m_prev = this; - } - } + void move_hook(hook_base&& other) noexcept; public: hook_base() noexcept = default; - hook_base(const hook_base& /*unused*/) noexcept - : hook_base() {} - - hook_base(hook_base&& other) noexcept { - move_hook(std::move(other)); - } - - hook_base& operator=(const hook_base& /*unused*/) noexcept { - return *this; - } - - hook_base& operator=(hook_base&& other) noexcept { - if (this == std::addressof(other)) { - return *this; - } + hook_base(const hook_base& /*unused*/) noexcept; - unlink(); - move_hook(std::move(other)); + hook_base(hook_base&& other) noexcept; - return *this; - } + hook_base& operator=(const hook_base& /*unused*/) noexcept; - ~hook_base() { - unlink(); - } + hook_base& operator=(hook_base&& other) noexcept; - bool is_linked() const noexcept { - return m_prev != this; - } + ~hook_base(); - void unlink() noexcept { - m_prev->m_next = m_next; - m_next->m_prev = m_prev; + bool is_linked() const noexcept; - m_prev = this; - m_next = this; - } + void unlink() noexcept; }; } // namespace details From 6f8bcf6ec6df90f5954202c869b28e52f1c15ef6 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 23 Jul 2026 20:02:13 +0300 Subject: [PATCH 05/35] apply code style --- runtime-common/core/std/intrusive-list.cpp | 10 ++++---- runtime-common/core/std/intrusive-list.h | 28 ++++++++++++++++++---- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.cpp b/runtime-common/core/std/intrusive-list.cpp index aeb871b362..a151106a48 100644 --- a/runtime-common/core/std/intrusive-list.cpp +++ b/runtime-common/core/std/intrusive-list.cpp @@ -2,7 +2,7 @@ namespace kphp::stl::intrusive_list::details { -void hook_base::move_hook(hook_base&& other) noexcept { +auto hook_base::move_hook(hook_base&& other) noexcept -> void { assert(!is_linked()); if (other.is_linked()) { m_prev = std::exchange(other.m_prev, std::addressof(other)); @@ -20,11 +20,11 @@ hook_base::hook_base(hook_base&& other) noexcept { move_hook(std::move(other)); } -hook_base& hook_base::operator=(const hook_base& /*unused*/) noexcept { +auto hook_base::operator=(const hook_base& /*unused*/) noexcept -> hook_base& { return *this; } -hook_base& hook_base::operator=(hook_base&& other) noexcept { +auto hook_base::operator=(hook_base&& other) noexcept -> hook_base& { if (this == std::addressof(other)) { return *this; } @@ -39,11 +39,11 @@ hook_base::~hook_base() { unlink(); } -bool hook_base::is_linked() const noexcept { +auto hook_base::is_linked() const noexcept -> bool { return m_prev != this; } -void hook_base::unlink() noexcept { +auto hook_base::unlink() noexcept -> void { m_prev->m_next = m_next; m_next->m_prev = m_prev; diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index e82fb7479a..1fe3cc0d6c 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include #include #include @@ -17,7 +18,7 @@ class hook_base { hook_base* m_prev{this}; hook_base* m_next{this}; - void move_hook(hook_base&& other) noexcept; + auto move_hook(hook_base&& other) noexcept -> void; public: hook_base() noexcept = default; @@ -26,15 +27,15 @@ class hook_base { hook_base(hook_base&& other) noexcept; - hook_base& operator=(const hook_base& /*unused*/) noexcept; + auto operator=(const hook_base& /*unused*/) noexcept -> hook_base&; - hook_base& operator=(hook_base&& other) noexcept; + auto operator=(hook_base&& other) noexcept -> hook_base&; ~hook_base(); - bool is_linked() const noexcept; + auto is_linked() const noexcept -> bool; - void unlink() noexcept; + auto unlink() noexcept -> void; }; } // namespace details @@ -47,6 +48,23 @@ class hook : private details::hook_base {}; template class intrusive_list final { static_assert(std::is_base_of_v, T>, "T must be derived from hook"); + + details::hook_base m_sentinel; + +public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + using iterator = void; + using const_iterator = void; + using reverse_iterator = void; + using const_reverse_iterator = void; + + intrusive_list(); }; } // namespace kphp::stl::intrusive_list From 374b2372f6c7711fd544d533a676614154b20f0f Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Fri, 24 Jul 2026 16:22:55 +0300 Subject: [PATCH 06/35] implement helper structures and functions --- runtime-common/core/std/intrusive-list.cpp | 37 ++-- runtime-common/core/std/intrusive-list.h | 199 +++++++++++++++++---- 2 files changed, 184 insertions(+), 52 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.cpp b/runtime-common/core/std/intrusive-list.cpp index a151106a48..5bc5dbf7e1 100644 --- a/runtime-common/core/std/intrusive-list.cpp +++ b/runtime-common/core/std/intrusive-list.cpp @@ -1,9 +1,11 @@ #include "intrusive-list.h" -namespace kphp::stl::intrusive_list::details { +#include +#include -auto hook_base::move_hook(hook_base&& other) noexcept -> void { - assert(!is_linked()); +namespace kphp::stl::intrusive::details { + +constexpr auto list_node_base::insert_instead(list_node_base&& other) noexcept -> void { if (other.is_linked()) { m_prev = std::exchange(other.m_prev, std::addressof(other)); m_next = std::exchange(other.m_next, std::addressof(other)); @@ -13,37 +15,28 @@ auto hook_base::move_hook(hook_base&& other) noexcept -> void { } } -hook_base::hook_base(const hook_base& /*unused*/) noexcept - : hook_base() {} - -hook_base::hook_base(hook_base&& other) noexcept { - move_hook(std::move(other)); +constexpr list_node_base::list_node_base(list_node_base&& other) noexcept { + insert_instead(std::move(other)); } -auto hook_base::operator=(const hook_base& /*unused*/) noexcept -> hook_base& { - return *this; -} - -auto hook_base::operator=(hook_base&& other) noexcept -> hook_base& { - if (this == std::addressof(other)) { - return *this; +constexpr auto list_node_base::operator=(list_node_base&& other) noexcept -> list_node_base& { + if (this != std::addressof(other)) { + unlink(); + insert_instead(std::move(other)); } - unlink(); - move_hook(std::move(other)); - return *this; } -hook_base::~hook_base() { +list_node_base::~list_node_base() { unlink(); } -auto hook_base::is_linked() const noexcept -> bool { +constexpr auto list_node_base::is_linked() const noexcept -> bool { return m_prev != this; } -auto hook_base::unlink() noexcept -> void { +constexpr auto list_node_base::unlink() noexcept -> void { m_prev->m_next = m_next; m_next->m_prev = m_prev; @@ -51,4 +44,4 @@ auto hook_base::unlink() noexcept -> void { m_next = this; } -} // namespace kphp::stl::intrusive_list::details +} // namespace kphp::stl::intrusive::details diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 1fe3cc0d6c..98cc4f8b66 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -4,67 +4,206 @@ #pragma once -#include #include -#include +#include #include #include -namespace kphp::stl::intrusive_list { +namespace kphp::stl::intrusive { namespace details { -class hook_base { - hook_base* m_prev{this}; - hook_base* m_next{this}; +class list_node_base { + list_node_base* m_prev{this}; + list_node_base* m_next{this}; - auto move_hook(hook_base&& other) noexcept -> void; + constexpr auto insert_instead(list_node_base&& other) noexcept -> void; public: - hook_base() noexcept = default; + constexpr list_node_base() noexcept = default; - hook_base(const hook_base& /*unused*/) noexcept; + list_node_base(const list_node_base& /*unused*/) noexcept = delete; - hook_base(hook_base&& other) noexcept; + constexpr list_node_base(list_node_base&& other) noexcept; - auto operator=(const hook_base& /*unused*/) noexcept -> hook_base&; + auto operator=(const list_node_base& /*unused*/) noexcept -> list_node_base& = delete; - auto operator=(hook_base&& other) noexcept -> hook_base&; + constexpr auto operator=(list_node_base&& other) noexcept -> list_node_base&; - ~hook_base(); + ~list_node_base(); - auto is_linked() const noexcept -> bool; + constexpr auto is_linked() const noexcept -> bool; - auto unlink() noexcept -> void; + constexpr auto unlink() noexcept -> void; +}; + +template +class list_node final : public list_node_base { + T m_value; + +public: + constexpr explicit list_node(T value) noexcept + : m_value{std::move(value)} {} + + list_node(const list_node& other) = delete; + + list_node(list_node&& other) = delete; + + auto operator=(const list_node& other) -> list_node& = delete; + + auto operator=(list_node&& other) -> list_node& = delete; + + constexpr ~list_node() = default; + + constexpr auto get() noexcept -> T& { + return m_value; + } + + constexpr auto get() const noexcept -> const T& { + return m_value; + } +}; + +template +class list_node final : public list_node_base { + T& m_value; + +public: + constexpr explicit list_node(T& value) noexcept + : m_value{value} {} + + list_node(const list_node& other) = delete; + + list_node(list_node&& other) = delete; + + auto operator=(const list_node& other) -> list_node& = delete; + + auto operator=(list_node&& other) -> list_node& = delete; + + constexpr ~list_node() = default; + + constexpr auto get() noexcept -> T& { + return m_value; + } + + constexpr auto get() const noexcept -> const T& { + return m_value; + } }; } // namespace details -struct default_tag {}; +// Node for object that is stored in single intrusive list +template +using owning_list_node = details::list_node>>; + +// Node for object that is stored in multiple intrusive lists +template +using non_owning_list_node = details::list_node>&>; -template -class hook : private details::hook_base {}; +template +constexpr auto make_owning_list_node(T value) noexcept -> owning_list_node { + return owning_list_node{std::move(value)}; +} -template -class intrusive_list final { - static_assert(std::is_base_of_v, T>, "T must be derived from hook"); +template +constexpr auto make_non_owning_list_node(T& value) noexcept -> non_owning_list_node { + return non_owning_list_node{value}; +} - details::hook_base m_sentinel; +template +class list_iterator; + +template +class list final { + details::list_node_base m_sentinel; public: using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + using size_type = size_t; + using difference_type = ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; - using iterator = void; - using const_iterator = void; - using reverse_iterator = void; - using const_reverse_iterator = void; + using iterator = list_iterator; + using const_iterator = list_iterator; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + constexpr list() noexcept = default; + + template + constexpr list(It first, It last); + + list(const list& other) = delete; + + constexpr list(list&& other) noexcept = default; + + auto operator=(const list& other) -> list& = delete; + + constexpr auto operator=(list&& other) noexcept -> list& = default; + + ~list() = default; + + constexpr auto front() -> reference; + + constexpr auto front() const -> const_reference; + + constexpr auto back() -> reference; + + constexpr auto back() const -> const_reference; + + constexpr auto begin() -> iterator; + + constexpr auto begin() const -> const_iterator; + + constexpr auto cbegin() const -> const_iterator; + + constexpr auto end() -> iterator; + + constexpr auto end() const -> const_iterator; + + constexpr auto cend() const -> const_iterator; + + constexpr auto rbegin() -> reverse_iterator; + + constexpr auto rbegin() const -> const_reverse_iterator; + + constexpr auto crbegin() const -> const_reverse_iterator; + + constexpr auto rend() -> reverse_iterator; + + constexpr auto rend() const -> const_reverse_iterator; + + constexpr auto crend() const -> const_reverse_iterator; + + constexpr auto empty() const -> bool; + + constexpr auto size() const -> size_type; + + constexpr auto clear() -> void; + + constexpr auto insert(const_iterator pos, const_reference value) -> iterator; + + template + constexpr auto insert(const_iterator pos, It first, It last) -> iterator; + + constexpr auto erase(const_iterator pos) -> iterator; + + constexpr auto erase(const_iterator first, const_iterator last) -> iterator; + + constexpr auto push_back(const_reference value) -> void; + + constexpr auto push_front(const_reference value) -> void; + + constexpr auto pop_back() -> void; + + constexpr auto pop_front() -> void; + + constexpr auto swap(list& other) -> void; - intrusive_list(); + constexpr auto merge(list& other) -> void; }; -} // namespace kphp::stl::intrusive_list +} // namespace kphp::stl::intrusive From 109b1082eec60925798b42af815b1e5bc369caab Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Fri, 24 Jul 2026 18:19:17 +0300 Subject: [PATCH 07/35] add interface --- runtime-common/core/std/intrusive-list.h | 165 +++++++++++++---------- 1 file changed, 95 insertions(+), 70 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 98cc4f8b66..0602a54adc 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -19,6 +20,9 @@ class list_node_base { constexpr auto insert_instead(list_node_base&& other) noexcept -> void; + template + friend class list_iterator; + public: constexpr list_node_base() noexcept = default; @@ -37,11 +41,23 @@ class list_node_base { constexpr auto unlink() noexcept -> void; }; -template -class list_node final : public list_node_base { +template +struct tag_holder : public list_node_base {}; + +template +struct tag_holder_variadic : public tag_holder... {}; + +} // namespace details + +struct default_tag {}; + +template +class list_node final : private std::conditional_t, details::tag_holder_variadic> { T m_value; public: + using element_type = T; + constexpr explicit list_node(T value) noexcept : m_value{std::move(value)} {} @@ -64,77 +80,73 @@ class list_node final : public list_node_base { } }; -template -class list_node final : public list_node_base { - T& m_value; +template +constexpr auto make_list_node(T value) noexcept -> list_node { + return list_node{std::move(value)}; +} + +template +class list_iterator { + using node_type = std::conditional_t, details::list_node_base, const details::list_node_base>; + + node_type* m_curr; public: - constexpr explicit list_node(T& value) noexcept - : m_value{value} {} + using difference_type = std::ptrdiff_t; + using value_type = Node::element_type; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::bidirectional_iterator_tag; - list_node(const list_node& other) = delete; + constexpr explicit list_iterator(node_type& node) noexcept + : m_curr{std::addressof(node)} {} - list_node(list_node&& other) = delete; + constexpr list_iterator(const list_iterator& other) noexcept = default; - auto operator=(const list_node& other) -> list_node& = delete; + constexpr list_iterator(list_iterator&& other) noexcept = default; - auto operator=(list_node&& other) -> list_node& = delete; + constexpr auto operator=(const list_iterator& other) noexcept -> list_iterator& = default; - constexpr ~list_node() = default; + constexpr auto operator=(list_iterator&& other) noexcept -> list_iterator& = default; - constexpr auto get() noexcept -> T& { - return m_value; - } + ~list_iterator() = default; - constexpr auto get() const noexcept -> const T& { - return m_value; - } -}; + constexpr auto operator++() noexcept -> list_iterator&; -} // namespace details + constexpr auto operator++(int) noexcept -> list_iterator; -// Node for object that is stored in single intrusive list -template -using owning_list_node = details::list_node>>; + constexpr auto operator--() noexcept -> list_iterator&; -// Node for object that is stored in multiple intrusive lists -template -using non_owning_list_node = details::list_node>&>; + constexpr auto operator--(int) noexcept -> list_iterator; -template -constexpr auto make_owning_list_node(T value) noexcept -> owning_list_node { - return owning_list_node{std::move(value)}; -} + constexpr auto operator*() const noexcept -> reference; -template -constexpr auto make_non_owning_list_node(T& value) noexcept -> non_owning_list_node { - return non_owning_list_node{value}; -} + constexpr auto operator==(const list_iterator& other) const noexcept -> bool; -template -class list_iterator; + constexpr auto operator!=(const list_iterator& other) const noexcept -> bool; +}; -template +template class list final { details::list_node_base m_sentinel; public: - using value_type = T; + using value_type = Node::element_type; using size_type = size_t; using difference_type = ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; - using iterator = list_iterator; - using const_iterator = list_iterator; + using iterator = list_iterator; + using const_iterator = list_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; constexpr list() noexcept = default; template - constexpr list(It first, It last); + constexpr list(It first, It last) noexcept; list(const list& other) = delete; @@ -146,64 +158,77 @@ class list final { ~list() = default; - constexpr auto front() -> reference; + constexpr auto front() noexcept -> reference; - constexpr auto front() const -> const_reference; + constexpr auto front() const noexcept -> const_reference; - constexpr auto back() -> reference; + constexpr auto back() noexcept -> reference; - constexpr auto back() const -> const_reference; + constexpr auto back() const noexcept -> const_reference; - constexpr auto begin() -> iterator; + constexpr auto begin() noexcept -> iterator; - constexpr auto begin() const -> const_iterator; + constexpr auto begin() const noexcept -> const_iterator; - constexpr auto cbegin() const -> const_iterator; + constexpr auto cbegin() const noexcept -> const_iterator; - constexpr auto end() -> iterator; + constexpr auto end() noexcept -> iterator; - constexpr auto end() const -> const_iterator; + constexpr auto end() const noexcept -> const_iterator; - constexpr auto cend() const -> const_iterator; + constexpr auto cend() const noexcept -> const_iterator; - constexpr auto rbegin() -> reverse_iterator; + constexpr auto rbegin() noexcept -> reverse_iterator; - constexpr auto rbegin() const -> const_reverse_iterator; + constexpr auto rbegin() const noexcept -> const_reverse_iterator; - constexpr auto crbegin() const -> const_reverse_iterator; + constexpr auto crbegin() const noexcept -> const_reverse_iterator; - constexpr auto rend() -> reverse_iterator; + constexpr auto rend() noexcept -> reverse_iterator; - constexpr auto rend() const -> const_reverse_iterator; + constexpr auto rend() const noexcept -> const_reverse_iterator; - constexpr auto crend() const -> const_reverse_iterator; + constexpr auto crend() const noexcept -> const_reverse_iterator; - constexpr auto empty() const -> bool; + constexpr auto empty() const noexcept -> bool; - constexpr auto size() const -> size_type; + constexpr auto size() const noexcept -> size_type; - constexpr auto clear() -> void; + constexpr auto clear() noexcept -> void; - constexpr auto insert(const_iterator pos, const_reference value) -> iterator; + constexpr auto insert(const_iterator pos, const Node& node) noexcept -> iterator; template - constexpr auto insert(const_iterator pos, It first, It last) -> iterator; + constexpr auto insert(const_iterator pos, It first, It last) noexcept -> iterator; + + constexpr auto erase(const_iterator pos) noexcept -> iterator; + + constexpr auto erase(const_iterator first, const_iterator last) noexcept -> iterator; - constexpr auto erase(const_iterator pos) -> iterator; + constexpr auto push_back(const Node& node) noexcept -> void; - constexpr auto erase(const_iterator first, const_iterator last) -> iterator; + constexpr auto push_front(const Node& node) noexcept -> void; - constexpr auto push_back(const_reference value) -> void; + constexpr auto pop_back() noexcept -> void; - constexpr auto push_front(const_reference value) -> void; + constexpr auto pop_front() noexcept -> void; - constexpr auto pop_back() -> void; + constexpr auto swap(list& other) noexcept -> void; - constexpr auto pop_front() -> void; + constexpr auto splice(const_iterator pos, list& other) noexcept -> void; - constexpr auto swap(list& other) -> void; + constexpr auto splice(const_iterator pos, list&& other) noexcept -> void; - constexpr auto merge(list& other) -> void; + constexpr auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void; + + constexpr auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void; + + constexpr auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void; + + constexpr auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void; }; +template +constexpr auto swap(list& lhs, list& rhs) noexcept -> void; + } // namespace kphp::stl::intrusive From 67d1317669bfa243038bdc08a7abf10a7721c9a2 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Fri, 24 Jul 2026 19:23:47 +0300 Subject: [PATCH 08/35] implement list_iterator --- common/type_traits/apply_tuple.h | 22 ++++++++++ runtime-common/core/std/intrusive-list.h | 53 ++++++++++++++++++++---- 2 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 common/type_traits/apply_tuple.h diff --git a/common/type_traits/apply_tuple.h b/common/type_traits/apply_tuple.h new file mode 100644 index 0000000000..2570d6586c --- /dev/null +++ b/common/type_traits/apply_tuple.h @@ -0,0 +1,22 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +namespace vk { + +template typename Template, typename Tuple> +struct apply_tuple; + +template typename Template, typename... Args> +struct apply_tuple> { + using Type = Template; +}; + +template typename Template, typename Tuple> +using apply_tuple_t = typename apply_tuple::Type; + +} // namespace vk diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 0602a54adc..eb2eb771d5 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -7,11 +7,17 @@ #include #include #include +#include #include #include +#include "common/type_traits/apply_tuple.h" + namespace kphp::stl::intrusive { +template +class list_node; + namespace details { class list_node_base { @@ -47,6 +53,18 @@ struct tag_holder : public list_node_base {}; template struct tag_holder_variadic : public tag_holder... {}; +template +constexpr auto as_value(list_node_base& node) noexcept -> Node::element_type& { + using tag_holder_variadic_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).get(); +} + +template +constexpr auto as_value(const list_node_base& node) noexcept -> const Node::element_type& { + using tag_holder_variadic_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).get(); +} + } // namespace details struct default_tag {}; @@ -57,6 +75,7 @@ class list_node final : private std::conditional_t; constexpr explicit list_node(T value) noexcept : m_value{std::move(value)} {} @@ -111,19 +130,39 @@ class list_iterator { ~list_iterator() = default; - constexpr auto operator++() noexcept -> list_iterator&; + constexpr auto operator++() noexcept -> list_iterator& { + m_curr = m_curr->m_next; + return *this; + } - constexpr auto operator++(int) noexcept -> list_iterator; + constexpr auto operator++(int) noexcept -> list_iterator { + list_iterator res = *this; + ++*this; + return res; + } - constexpr auto operator--() noexcept -> list_iterator&; + constexpr auto operator--() noexcept -> list_iterator& { + m_curr = m_curr->m_prev; + return *this; + } - constexpr auto operator--(int) noexcept -> list_iterator; + constexpr auto operator--(int) noexcept -> list_iterator { + list_iterator res = *this; + --*this; + return res; + } - constexpr auto operator*() const noexcept -> reference; + constexpr auto operator*() const noexcept -> reference { + return details::as_value(*m_curr); + } - constexpr auto operator==(const list_iterator& other) const noexcept -> bool; + constexpr auto operator==(const list_iterator& other) const noexcept -> bool { + return m_curr == other.m_curr; + } - constexpr auto operator!=(const list_iterator& other) const noexcept -> bool; + constexpr auto operator!=(const list_iterator& other) const noexcept -> bool { + return !(*this == other); + } }; template From e47e5dfef4983208616ce822a140d1997e4acb51 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Sat, 25 Jul 2026 17:37:16 +0300 Subject: [PATCH 09/35] update naming --- runtime-common/core/std/intrusive-list.cpp | 14 +- runtime-common/core/std/intrusive-list.h | 226 ++++++++++++--------- 2 files changed, 141 insertions(+), 99 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.cpp b/runtime-common/core/std/intrusive-list.cpp index 5bc5dbf7e1..e01fb88133 100644 --- a/runtime-common/core/std/intrusive-list.cpp +++ b/runtime-common/core/std/intrusive-list.cpp @@ -5,7 +5,7 @@ namespace kphp::stl::intrusive::details { -constexpr auto list_node_base::insert_instead(list_node_base&& other) noexcept -> void { +auto list_node_base::take_place_of(list_node_base&& other) noexcept -> void { if (other.is_linked()) { m_prev = std::exchange(other.m_prev, std::addressof(other)); m_next = std::exchange(other.m_next, std::addressof(other)); @@ -15,14 +15,14 @@ constexpr auto list_node_base::insert_instead(list_node_base&& other) noexcept - } } -constexpr list_node_base::list_node_base(list_node_base&& other) noexcept { - insert_instead(std::move(other)); +list_node_base::list_node_base(list_node_base&& other) noexcept { + take_place_of(std::move(other)); } -constexpr auto list_node_base::operator=(list_node_base&& other) noexcept -> list_node_base& { +auto list_node_base::operator=(list_node_base&& other) noexcept -> list_node_base& { if (this != std::addressof(other)) { unlink(); - insert_instead(std::move(other)); + take_place_of(std::move(other)); } return *this; @@ -32,11 +32,11 @@ list_node_base::~list_node_base() { unlink(); } -constexpr auto list_node_base::is_linked() const noexcept -> bool { +auto list_node_base::is_linked() const noexcept -> bool { return m_prev != this; } -constexpr auto list_node_base::unlink() noexcept -> void { +auto list_node_base::unlink() noexcept -> void { m_prev->m_next = m_next; m_next->m_prev = m_prev; diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index eb2eb771d5..3528d9d640 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -15,8 +15,11 @@ namespace kphp::stl::intrusive { -template -class list_node; +template +class list; + +template +class list_iterator; namespace details { @@ -24,143 +27,144 @@ class list_node_base { list_node_base* m_prev{this}; list_node_base* m_next{this}; - constexpr auto insert_instead(list_node_base&& other) noexcept -> void; + auto take_place_of(list_node_base&& other) noexcept -> void; template - friend class list_iterator; + friend class kphp::stl::intrusive::list; + + template + friend class kphp::stl::intrusive::list_iterator; public: - constexpr list_node_base() noexcept = default; + list_node_base() noexcept = default; list_node_base(const list_node_base& /*unused*/) noexcept = delete; - constexpr list_node_base(list_node_base&& other) noexcept; + list_node_base(list_node_base&& other) noexcept; auto operator=(const list_node_base& /*unused*/) noexcept -> list_node_base& = delete; - constexpr auto operator=(list_node_base&& other) noexcept -> list_node_base&; + auto operator=(list_node_base&& other) noexcept -> list_node_base&; ~list_node_base(); - constexpr auto is_linked() const noexcept -> bool; + auto is_linked() const noexcept -> bool; - constexpr auto unlink() noexcept -> void; + auto unlink() noexcept -> void; }; template -struct tag_holder : public list_node_base {}; +struct tagged_hook : public list_node_base {}; template -struct tag_holder_variadic : public tag_holder... {}; - -template -constexpr auto as_value(list_node_base& node) noexcept -> Node::element_type& { - using tag_holder_variadic_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).get(); -} - -template -constexpr auto as_value(const list_node_base& node) noexcept -> const Node::element_type& { - using tag_holder_variadic_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).get(); -} +struct tagged_hooks : public tagged_hook... {}; } // namespace details struct default_tag {}; template -class list_node final : private std::conditional_t, details::tag_holder_variadic> { +class list_node final : private std::conditional_t, details::tagged_hooks> { T m_value; public: - using element_type = T; - using tags = std::tuple; + using value_type = T; + using tags = std::conditional_t, std::tuple>; - constexpr explicit list_node(T value) noexcept + explicit list_node(T value) noexcept : m_value{std::move(value)} {} list_node(const list_node& other) = delete; - list_node(list_node&& other) = delete; + list_node(list_node&& other) noexcept = default; auto operator=(const list_node& other) -> list_node& = delete; - auto operator=(list_node&& other) -> list_node& = delete; + auto operator=(list_node&& other) noexcept -> list_node& = default; - constexpr ~list_node() = default; + ~list_node() = default; - constexpr auto get() noexcept -> T& { + auto value() noexcept -> T& { return m_value; } - constexpr auto get() const noexcept -> const T& { + auto value() const noexcept -> const T& { return m_value; } }; template -constexpr auto make_list_node(T value) noexcept -> list_node { +auto make_list_node(T value) noexcept -> list_node { return list_node{std::move(value)}; } template -class list_iterator { - using node_type = std::conditional_t, details::list_node_base, const details::list_node_base>; +class list_iterator final { + using list_node_base_type = std::conditional_t, const details::list_node_base, details::list_node_base>; + + list_node_base_type* m_curr; + + static auto value_from_list_node_base(details::list_node_base& node) noexcept -> Node::element_type& { + using tag_holder_variadic_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).value(); + } - node_type* m_curr; + static auto value_from_list_node_base(const details::list_node_base& node) noexcept -> const Node::element_type& { + using tag_holder_variadic_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).value(); + } public: using difference_type = std::ptrdiff_t; - using value_type = Node::element_type; + using value_type = Node::value_type; using pointer = value_type*; using reference = value_type&; using iterator_category = std::bidirectional_iterator_tag; - constexpr explicit list_iterator(node_type& node) noexcept + explicit list_iterator(list_node_base_type& node) noexcept : m_curr{std::addressof(node)} {} - constexpr list_iterator(const list_iterator& other) noexcept = default; + list_iterator(const list_iterator& other) noexcept = default; - constexpr list_iterator(list_iterator&& other) noexcept = default; + list_iterator(list_iterator&& other) noexcept = default; - constexpr auto operator=(const list_iterator& other) noexcept -> list_iterator& = default; + auto operator=(const list_iterator& other) noexcept -> list_iterator& = default; - constexpr auto operator=(list_iterator&& other) noexcept -> list_iterator& = default; + auto operator=(list_iterator&& other) noexcept -> list_iterator& = default; ~list_iterator() = default; - constexpr auto operator++() noexcept -> list_iterator& { + auto operator++() noexcept -> list_iterator& { m_curr = m_curr->m_next; return *this; } - constexpr auto operator++(int) noexcept -> list_iterator { + auto operator++(int) noexcept -> list_iterator { list_iterator res = *this; ++*this; return res; } - constexpr auto operator--() noexcept -> list_iterator& { + auto operator--() noexcept -> list_iterator& { m_curr = m_curr->m_prev; return *this; } - constexpr auto operator--(int) noexcept -> list_iterator { + auto operator--(int) noexcept -> list_iterator { list_iterator res = *this; --*this; return res; } - constexpr auto operator*() const noexcept -> reference { - return details::as_value(*m_curr); + auto operator*() const noexcept -> reference { + return value_from_list_node_base(*m_curr); } - constexpr auto operator==(const list_iterator& other) const noexcept -> bool { + auto operator==(const list_iterator& other) const noexcept -> bool { return m_curr == other.m_curr; } - constexpr auto operator!=(const list_iterator& other) const noexcept -> bool { + auto operator!=(const list_iterator& other) const noexcept -> bool { return !(*this == other); } }; @@ -170,104 +174,142 @@ class list final { details::list_node_base m_sentinel; public: - using value_type = Node::element_type; + using value_type = Node::value_type; using size_type = size_t; using difference_type = ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; - using iterator = list_iterator; - using const_iterator = list_iterator; + using iterator = list_iterator; + using const_iterator = list_iterator; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - constexpr list() noexcept = default; + list() noexcept = default; template - constexpr list(It first, It last) noexcept; + list(It first, It last) noexcept; list(const list& other) = delete; - constexpr list(list&& other) noexcept = default; + list(list&& other) noexcept = default; auto operator=(const list& other) -> list& = delete; - constexpr auto operator=(list&& other) noexcept -> list& = default; + auto operator=(list&& other) noexcept -> list& = default; ~list() = default; - constexpr auto front() noexcept -> reference; + auto front() noexcept -> reference { + return *begin(); + } - constexpr auto front() const noexcept -> const_reference; + auto front() const noexcept -> const_reference { + return *begin(); + } - constexpr auto back() noexcept -> reference; + auto back() noexcept -> reference { + return *(--end()); + } - constexpr auto back() const noexcept -> const_reference; + auto back() const noexcept -> const_reference { + return *(--end()); + } - constexpr auto begin() noexcept -> iterator; + auto begin() noexcept -> iterator { + return iterator{*m_sentinel.m_next}; + } - constexpr auto begin() const noexcept -> const_iterator; + auto begin() const noexcept -> const_iterator { + return const_iterator{*m_sentinel.m_next}; + } - constexpr auto cbegin() const noexcept -> const_iterator; + auto cbegin() const noexcept -> const_iterator { + return begin(); + } - constexpr auto end() noexcept -> iterator; + auto end() noexcept -> iterator { + return iterator{m_sentinel}; + } - constexpr auto end() const noexcept -> const_iterator; + auto end() const noexcept -> const_iterator { + return const_iterator{m_sentinel}; + } - constexpr auto cend() const noexcept -> const_iterator; + auto cend() const noexcept -> const_iterator { + return end(); + } - constexpr auto rbegin() noexcept -> reverse_iterator; + auto rbegin() noexcept -> reverse_iterator { + return reverse_iterator{--end()}; + } - constexpr auto rbegin() const noexcept -> const_reverse_iterator; + auto rbegin() const noexcept -> const_reverse_iterator { + return const_reverse_iterator{--end()}; + } - constexpr auto crbegin() const noexcept -> const_reverse_iterator; + auto crbegin() const noexcept -> const_reverse_iterator { + return rbegin(); + } - constexpr auto rend() noexcept -> reverse_iterator; + auto rend() noexcept -> reverse_iterator { + return reverse_iterator{--begin()}; + } - constexpr auto rend() const noexcept -> const_reverse_iterator; + auto rend() const noexcept -> const_reverse_iterator { + return const_reverse_iterator{--begin()}; + } - constexpr auto crend() const noexcept -> const_reverse_iterator; + auto crend() const noexcept -> const_reverse_iterator { + return rend(); + } - constexpr auto empty() const noexcept -> bool; + auto empty() const noexcept -> bool { + return !m_sentinel.is_linked(); + } - constexpr auto size() const noexcept -> size_type; + auto size() const noexcept -> size_type { + return std::distance(begin(), end()); + } - constexpr auto clear() noexcept -> void; + auto clear() noexcept -> void { + m_sentinel.unlink(); + } - constexpr auto insert(const_iterator pos, const Node& node) noexcept -> iterator; + auto insert(const_iterator pos, const Node& node) noexcept -> iterator; template - constexpr auto insert(const_iterator pos, It first, It last) noexcept -> iterator; + auto insert(const_iterator pos, It first, It last) noexcept -> iterator; - constexpr auto erase(const_iterator pos) noexcept -> iterator; + auto erase(const_iterator pos) noexcept -> iterator; - constexpr auto erase(const_iterator first, const_iterator last) noexcept -> iterator; + auto erase(const_iterator first, const_iterator last) noexcept -> iterator; - constexpr auto push_back(const Node& node) noexcept -> void; + auto push_back(const Node& node) noexcept -> void; - constexpr auto push_front(const Node& node) noexcept -> void; + auto push_front(const Node& node) noexcept -> void; - constexpr auto pop_back() noexcept -> void; + auto pop_back() noexcept -> void; - constexpr auto pop_front() noexcept -> void; + auto pop_front() noexcept -> void; - constexpr auto swap(list& other) noexcept -> void; + auto swap(list& other) noexcept -> void; - constexpr auto splice(const_iterator pos, list& other) noexcept -> void; + auto splice(const_iterator pos, list& other) noexcept -> void; - constexpr auto splice(const_iterator pos, list&& other) noexcept -> void; + auto splice(const_iterator pos, list&& other) noexcept -> void; - constexpr auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void; + auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void; - constexpr auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void; + auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void; - constexpr auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void; + auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void; - constexpr auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void; + auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void; }; template -constexpr auto swap(list& lhs, list& rhs) noexcept -> void; +auto swap(list& lhs, list& rhs) noexcept -> void; } // namespace kphp::stl::intrusive From 0f692c026dfb93739475aca0dc6cec439e76fdc7 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Sat, 25 Jul 2026 17:42:35 +0300 Subject: [PATCH 10/35] update naming --- runtime-common/core/std/intrusive-list.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 3528d9d640..bac4aee995 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -104,14 +104,14 @@ class list_iterator final { list_node_base_type* m_curr; - static auto value_from_list_node_base(details::list_node_base& node) noexcept -> Node::element_type& { - using tag_holder_variadic_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).value(); + static auto value_from_list_node_base(details::list_node_base& node) noexcept -> Node::value_type& { + using tagged_hooks_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).value(); } - static auto value_from_list_node_base(const details::list_node_base& node) noexcept -> const Node::element_type& { - using tag_holder_variadic_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).value(); + static auto value_from_list_node_base(const details::list_node_base& node) noexcept -> const Node::value_type& { + using tagged_hooks_t = vk::apply_tuple_t; + return (static_cast(static_cast(static_cast&>(node)))).value(); } public: From d91d9e765c8aac2abad44029b6a742f4b84e7ee5 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Sat, 25 Jul 2026 18:00:44 +0300 Subject: [PATCH 11/35] add type traits to check correctness of template arguments --- runtime-common/core/std/intrusive-list.h | 38 ++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index bac4aee995..dac50fe50d 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -67,6 +67,12 @@ template class list_node final : private std::conditional_t, details::tagged_hooks> { T m_value; + template + friend class kphp::stl::intrusive::list; + + template + friend class kphp::stl::intrusive::list_iterator; + public: using value_type = T; using tags = std::conditional_t, std::tuple>; @@ -98,8 +104,33 @@ auto make_list_node(T value) noexcept -> list_node { return list_node{std::move(value)}; } +namespace details { + +template +struct is_list_node : std::false_type {}; + +template +struct is_list_node> : std::true_type {}; + +template +inline constexpr bool is_list_node_v = is_list_node>::value_type; + +template +struct is_tag_of : std::false_type {}; + +template +struct is_tag_of> : std::bool_constant<(std::is_same_v || ...)> {}; + +template +inline constexpr bool is_tag_of_v = is_tag_of::tags>::value; + +} // namespace details + template class list_iterator final { + static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); + static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); + using list_node_base_type = std::conditional_t, const details::list_node_base, details::list_node_base>; list_node_base_type* m_curr; @@ -117,8 +148,8 @@ class list_iterator final { public: using difference_type = std::ptrdiff_t; using value_type = Node::value_type; - using pointer = value_type*; - using reference = value_type&; + using pointer = std::conditional_t, const value_type*, value_type*>; + using reference = std::conditional_t, const value_type&, value_type&>; using iterator_category = std::bidirectional_iterator_tag; explicit list_iterator(list_node_base_type& node) noexcept @@ -171,6 +202,9 @@ class list_iterator final { template class list final { + static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); + static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); + details::list_node_base m_sentinel; public: From e91e6427f55029dd7e3bf7434151f30190abdf9f Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Sun, 26 Jul 2026 14:24:54 +0300 Subject: [PATCH 12/35] implement list --- runtime-common/core/std/intrusive-list.h | 201 ++++++++++++++++------- 1 file changed, 142 insertions(+), 59 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index dac50fe50d..b2b692b132 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -113,7 +113,7 @@ template struct is_list_node> : std::true_type {}; template -inline constexpr bool is_list_node_v = is_list_node>::value_type; +inline constexpr bool is_list_node_v = is_list_node>::value; template struct is_tag_of : std::false_type {}; @@ -127,36 +127,41 @@ inline constexpr bool is_tag_of_v = is_tag_of:: } // namespace details template -class list_iterator final { +class list_iterator { +public: + using difference_type = std::ptrdiff_t; + using value_type = typename Node::value_type; + using pointer = std::conditional_t, const value_type*, value_type*>; + using reference = std::conditional_t, const value_type&, value_type&>; + using iterator_category = std::bidirectional_iterator_tag; + +private: static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); - using list_node_base_type = std::conditional_t, const details::list_node_base, details::list_node_base>; + details::list_node_base* m_curr; - list_node_base_type* m_curr; - - static auto value_from_list_node_base(details::list_node_base& node) noexcept -> Node::value_type& { - using tagged_hooks_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).value(); - } + explicit list_iterator(const details::list_node_base* node) noexcept + : m_curr{const_cast(node)} {} - static auto value_from_list_node_base(const details::list_node_base& node) noexcept -> const Node::value_type& { + static auto value_from_list_node_base(details::list_node_base* node) noexcept -> reference { using tagged_hooks_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast&>(node)))).value(); + return (static_cast(static_cast(static_cast*>(node))))->value(); } -public: - using difference_type = std::ptrdiff_t; - using value_type = Node::value_type; - using pointer = std::conditional_t, const value_type*, value_type*>; - using reference = std::conditional_t, const value_type&, value_type&>; - using iterator_category = std::bidirectional_iterator_tag; + template + friend class kphp::stl::intrusive::list_iterator; - explicit list_iterator(list_node_base_type& node) noexcept - : m_curr{std::addressof(node)} {} + template + friend class kphp::stl::intrusive::list; +public: list_iterator(const list_iterator& other) noexcept = default; + template && !std::is_const_v>> + list_iterator(const list_iterator& other) noexcept // NOLINT (hicpp-explicit-conversions) + : m_curr{other.m_curr} {} + list_iterator(list_iterator&& other) noexcept = default; auto operator=(const list_iterator& other) noexcept -> list_iterator& = default; @@ -188,27 +193,24 @@ class list_iterator final { } auto operator*() const noexcept -> reference { - return value_from_list_node_base(*m_curr); + return value_from_list_node_base(m_curr); } - auto operator==(const list_iterator& other) const noexcept -> bool { + template, std::remove_const_t>>> + auto operator==(const list_iterator& other) const noexcept -> bool { return m_curr == other.m_curr; } - auto operator!=(const list_iterator& other) const noexcept -> bool { + template, std::remove_const_t>>> + auto operator!=(const list_iterator& other) const noexcept -> bool { return !(*this == other); } }; template -class list final { - static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); - static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); - - details::list_node_base m_sentinel; - +class list { public: - using value_type = Node::value_type; + using value_type = typename Node::value_type; using size_type = size_t; using difference_type = ptrdiff_t; using reference = value_type&; @@ -220,10 +222,39 @@ class list final { using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; - list() noexcept = default; +private: + static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); + static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); + + details::list_node_base m_sentinel; + + static auto list_node_base_from_list_node(Node& node) noexcept -> details::list_node_base* { + using tagged_hooks_t = vk::apply_tuple_t; + return static_cast(static_cast*>(static_cast(std::addressof(node)))); + } + + template + auto splice_impl(const_iterator pos, List&& other, const_iterator first, const_iterator last) noexcept -> void { + if (first == last || (this == std::addressof(other) && last == pos)) { + return; + } + + auto* pos_node = pos.m_curr; + auto* first_node = first.m_curr; + auto* last_node = last.m_curr->m_prev; + + first_node->m_prev->m_next = last_node->m_next; + last_node->m_next->m_prev = first_node->m_prev; + + first_node->m_prev = pos_node->m_prev; + last_node->m_next = pos_node; + + pos_node->m_prev->m_next = first_node; + pos_node->m_prev = last_node; + } - template - list(It first, It last) noexcept; +public: + list() noexcept = default; list(const list& other) = delete; @@ -244,19 +275,19 @@ class list final { } auto back() noexcept -> reference { - return *(--end()); + return *std::prev(end()); } auto back() const noexcept -> const_reference { - return *(--end()); + return *std::prev(end()); } auto begin() noexcept -> iterator { - return iterator{*m_sentinel.m_next}; + return iterator{m_sentinel.m_next}; } auto begin() const noexcept -> const_iterator { - return const_iterator{*m_sentinel.m_next}; + return const_iterator{m_sentinel.m_next}; } auto cbegin() const noexcept -> const_iterator { @@ -264,11 +295,11 @@ class list final { } auto end() noexcept -> iterator { - return iterator{m_sentinel}; + return iterator{std::addressof(m_sentinel)}; } auto end() const noexcept -> const_iterator { - return const_iterator{m_sentinel}; + return const_iterator{std::addressof(m_sentinel)}; } auto cend() const noexcept -> const_iterator { @@ -276,11 +307,11 @@ class list final { } auto rbegin() noexcept -> reverse_iterator { - return reverse_iterator{--end()}; + return reverse_iterator{std::prev(end())}; } auto rbegin() const noexcept -> const_reverse_iterator { - return const_reverse_iterator{--end()}; + return const_reverse_iterator{std::prev(end())}; } auto crbegin() const noexcept -> const_reverse_iterator { @@ -288,11 +319,11 @@ class list final { } auto rend() noexcept -> reverse_iterator { - return reverse_iterator{--begin()}; + return reverse_iterator{std::prev(begin())}; } auto rend() const noexcept -> const_reverse_iterator { - return const_reverse_iterator{--begin()}; + return const_reverse_iterator{std::prev(begin())}; } auto crend() const noexcept -> const_reverse_iterator { @@ -311,39 +342,91 @@ class list final { m_sentinel.unlink(); } - auto insert(const_iterator pos, const Node& node) noexcept -> iterator; + auto insert(const_iterator pos, Node& node) noexcept -> iterator { + auto* next_node = pos.m_curr; + auto* new_node = list_node_base_from_list_node(node); + if (new_node == next_node) { + return iterator{next_node}; + } - template - auto insert(const_iterator pos, It first, It last) noexcept -> iterator; + new_node->unlink(); - auto erase(const_iterator pos) noexcept -> iterator; + new_node->m_prev = next_node->m_prev; + new_node->m_next = next_node; - auto erase(const_iterator first, const_iterator last) noexcept -> iterator; + new_node->m_prev->m_next = new_node; + new_node->m_next->m_prev = new_node; - auto push_back(const Node& node) noexcept -> void; + return iterator{new_node}; + } - auto push_front(const Node& node) noexcept -> void; + auto erase(const_iterator pos) noexcept -> iterator { + auto* remove_node = pos.m_curr; + auto* next_node = remove_node->m_next; - auto pop_back() noexcept -> void; + remove_node->unlink(); - auto pop_front() noexcept -> void; + return iterator{next_node}; + } - auto swap(list& other) noexcept -> void; + auto erase(const_iterator first, const_iterator last) noexcept -> iterator { + while (first != last) { + first = erase(first); + } - auto splice(const_iterator pos, list& other) noexcept -> void; + return iterator{last.m_curr}; + } - auto splice(const_iterator pos, list&& other) noexcept -> void; + auto push_back(Node& node) noexcept -> void { + insert(end(), node); + } - auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void; + auto push_front(Node& node) noexcept -> void { + insert(begin(), node); + } - auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void; + auto pop_back() noexcept -> void { + erase(std::prev(end())); + } - auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void; + auto pop_front() noexcept -> void { + erase(begin()); + } + + auto swap(list& other) noexcept -> void { + details::list_node_base tmp{std::move(m_sentinel)}; + m_sentinel = std::move(other.m_sentinel); + other.m_sentinel = std::move(tmp); + } + + auto splice(const_iterator pos, list& other) noexcept -> void { + splice(pos, other, other.begin(), other.end()); + } + + auto splice(const_iterator pos, list&& other) noexcept -> void { + splice(pos, std::move(other), other.begin(), other.end()); + } - auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void; + auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void { + splice(pos, other, it, std::next(it)); + } + + auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void { + splice(pos, std::move(other), it, std::next(it)); + } + + auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void { + splice_impl(pos, other, first, last); + } + + auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void { + splice_impl(pos, std::move(other), first, last); + } }; template -auto swap(list& lhs, list& rhs) noexcept -> void; +auto swap(list& lhs, list& rhs) noexcept -> void { + lhs.swap(rhs); +} } // namespace kphp::stl::intrusive From fa4d22a99f27a059e3bcdbe813f7bddc4d6ebbb1 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Sun, 26 Jul 2026 14:40:42 +0300 Subject: [PATCH 13/35] implement list --- runtime-common/core/std/intrusive-list.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index b2b692b132..471a96fd79 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -139,7 +139,7 @@ class list_iterator { static_assert(details::is_list_node_v, "Node must be a specialization of list_node"); static_assert(details::is_tag_of_v, "Tag is not one of Node's tags"); - details::list_node_base* m_curr; + details::list_node_base* m_curr{nullptr}; explicit list_iterator(const details::list_node_base* node) noexcept : m_curr{const_cast(node)} {} @@ -156,6 +156,8 @@ class list_iterator { friend class kphp::stl::intrusive::list; public: + list_iterator() noexcept = default; + list_iterator(const list_iterator& other) noexcept = default; template && !std::is_const_v>> @@ -233,8 +235,7 @@ class list { return static_cast(static_cast*>(static_cast(std::addressof(node)))); } - template - auto splice_impl(const_iterator pos, List&& other, const_iterator first, const_iterator last) noexcept -> void { + auto splice_impl(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void { if (first == last || (this == std::addressof(other) && last == pos)) { return; } @@ -404,7 +405,7 @@ class list { } auto splice(const_iterator pos, list&& other) noexcept -> void { - splice(pos, std::move(other), other.begin(), other.end()); + splice(pos, other, other.begin(), other.end()); } auto splice(const_iterator pos, list& other, const_iterator it) noexcept -> void { @@ -412,7 +413,7 @@ class list { } auto splice(const_iterator pos, list&& other, const_iterator it) noexcept -> void { - splice(pos, std::move(other), it, std::next(it)); + splice(pos, other, it, std::next(it)); } auto splice(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void { @@ -420,7 +421,7 @@ class list { } auto splice(const_iterator pos, list&& other, const_iterator first, const_iterator last) noexcept -> void { - splice_impl(pos, std::move(other), first, last); + splice_impl(pos, other, first, last); } }; From 7b34fff16ee5c95287728fb33151cfd774142f7c Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Mon, 27 Jul 2026 18:09:52 +0300 Subject: [PATCH 14/35] fix bag with reverse iterators --- runtime-common/core/std/intrusive-list.h | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 471a96fd79..0872cc1095 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -146,7 +146,13 @@ class list_iterator { static auto value_from_list_node_base(details::list_node_base* node) noexcept -> reference { using tagged_hooks_t = vk::apply_tuple_t; - return (static_cast(static_cast(static_cast*>(node))))->value(); + /* + * save cast to local variable to avoid compilation error in g++-11: + * error: ‘this’ pointer is null [-Werror=nonnull] + * 150 | return (static_cast(static_cast(static_cast*>(node))))->value(); + */ + auto* list_node = static_cast(static_cast(static_cast*>(node))); + return list_node->value(); } template @@ -308,11 +314,11 @@ class list { } auto rbegin() noexcept -> reverse_iterator { - return reverse_iterator{std::prev(end())}; + return reverse_iterator{end()}; } auto rbegin() const noexcept -> const_reverse_iterator { - return const_reverse_iterator{std::prev(end())}; + return const_reverse_iterator{end()}; } auto crbegin() const noexcept -> const_reverse_iterator { @@ -320,11 +326,11 @@ class list { } auto rend() noexcept -> reverse_iterator { - return reverse_iterator{std::prev(begin())}; + return reverse_iterator{begin()}; } auto rend() const noexcept -> const_reverse_iterator { - return const_reverse_iterator{std::prev(begin())}; + return const_reverse_iterator{begin()}; } auto crend() const noexcept -> const_reverse_iterator { From d35f73e9bb0be5177403942138dfbc69471d4ab9 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Mon, 27 Jul 2026 18:10:30 +0300 Subject: [PATCH 15/35] add source file to cmake --- runtime-common/core/core.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime-common/core/core.cmake b/runtime-common/core/core.cmake index 3e42221b8f..bc26bd8c4c 100644 --- a/runtime-common/core/core.cmake +++ b/runtime-common/core/core.cmake @@ -7,4 +7,6 @@ prepend(CORE_MEMORY_RESOURCE core/memory-resource/ details/memory_chunk_tree.cpp details/memory_ordered_chunk_list.cpp monotonic_buffer_resource.cpp unsynchronized_pool_resource.cpp) -set(CORE_SRC ${CORE_UTILS} ${CORE_TYPES} ${CORE_MEMORY_RESOURCE}) +prepend(CORE_STD core/std/ intrusive-list.cpp) + +set(CORE_SRC ${CORE_UTILS} ${CORE_TYPES} ${CORE_MEMORY_RESOURCE} ${CORE_STD}) From 9b0ca96c6b9e1e63efe3ca253b67875eb12439d9 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Mon, 27 Jul 2026 18:11:05 +0300 Subject: [PATCH 16/35] add unit tests for intrusive list --- tests/cpp/runtime/intrusive-list-test.cpp | 787 ++++++++++++++++++++++ tests/cpp/runtime/runtime-tests.cmake | 1 + 2 files changed, 788 insertions(+) create mode 100644 tests/cpp/runtime/intrusive-list-test.cpp diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp new file mode 100644 index 0000000000..3bb679bfb3 --- /dev/null +++ b/tests/cpp/runtime/intrusive-list-test.cpp @@ -0,0 +1,787 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "runtime-common/core/std/intrusive-list.h" + +namespace { + +template +auto values(const List& l) -> std::vector { + std::vector v; + for (const auto& x : l) { + v.push_back(x); + } + + return v; +} + +template +auto reversed_values(List& l) -> std::vector { + std::vector v; + for (auto it = l.rbegin(); it != l.rend(); ++it) { + v.push_back(*it); + } + + return v; +} + +} // namespace + +TEST(intrusive_list_node_base, default_is_not_linked) { + kphp::stl::intrusive::details::list_node_base b; + + ASSERT_FALSE(b.is_linked()); +} + +TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { + kphp::stl::intrusive::details::list_node_base b; + b.unlink(); + b.unlink(); + + ASSERT_FALSE(b.is_linked()); +} + +TEST(intrusive_list_basic, empty_list) { + kphp::stl::intrusive::list> l; + + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); + ASSERT_EQ(l.begin(), l.end()); + ASSERT_EQ(l.cbegin(), l.cend()); + ASSERT_EQ(l.rbegin(), l.rend()); + ASSERT_EQ(l.crbegin(), l.crend()); +} + +TEST(intrusive_list_basic, push_back_single) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node n{42}; + + l.push_back(n); + + ASSERT_FALSE(l.empty()); + ASSERT_EQ(l.size(), 1); + ASSERT_EQ(l.front(), 42); + ASSERT_EQ(l.back(), 42); + ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); +} + +TEST(intrusive_list_basic, push_back_keeps_order) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(l.size(), 3); + ASSERT_EQ(values(l), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_basic, push_front_single) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node n{42}; + + l.push_front(n); + + ASSERT_FALSE(l.empty()); + ASSERT_EQ(l.size(), 1); + ASSERT_EQ(l.front(), 42); + ASSERT_EQ(l.back(), 42); + ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); +} + +TEST(intrusive_list_basic, push_front_reverses_order) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_front(n); + } + + ASSERT_EQ(l.size(), 3); + ASSERT_EQ(values(l), (std::vector{3, 2, 1})); +} + +TEST(intrusive_list_basic, mixed_push_front_and_back) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + l.push_back(b); // 2 + l.push_front(a); // 1 2 + l.push_back(c); // 1 2 3 + l.push_front(d); // 4 1 2 3 + + ASSERT_EQ(values(l), (std::vector{4, 1, 2, 3})); +} + +TEST(intrusive_list_basic, front_back_are_mutable_references) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + l.front() = 10; + l.back() = 20; + + ASSERT_EQ(values(l), (std::vector{10, 20})); + ASSERT_EQ(a.value(), 10); + ASSERT_EQ(b.value(), 20); +} + +TEST(intrusive_list_iteration, forward) { + kphp::stl::intrusive::list> l; + std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(values(l), (std::vector{1, 2, 3, 4})); +} + +TEST(intrusive_list_iteration, reverse) { + kphp::stl::intrusive::list> l; + std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(reversed_values(l), (std::vector{4, 3, 2, 1})); +} + +TEST(intrusive_list_iteration, bidirectional_increment_decrement) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_back(n); + } + auto it = l.begin(); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(*(++it), 2); + ASSERT_EQ(*(it++), 2); + ASSERT_EQ(*it, 3); + ASSERT_EQ(*(--it), 2); + ASSERT_EQ(*(it--), 2); + ASSERT_EQ(*it, 1); + + auto last = l.end(); + --last; + + ASSERT_EQ(*last, 3); +} + +TEST(intrusive_list_iteration, const_iteration) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{7}, kphp::stl::intrusive::list_node{8}, + kphp::stl::intrusive::list_node{9}}; + for (auto& n : ns) { + l.push_back(n); + } + const kphp::stl::intrusive::list>& cl = l; + + ASSERT_EQ(values(cl), (std::vector{7, 8, 9})); + + std::vector collected; + for (auto it = cl.cbegin(); it != cl.cend(); ++it) { + collected.push_back(*it); + } + + ASSERT_EQ(collected, (std::vector{7, 8, 9})); + + std::vector reversed_collected; + for (auto it = cl.crbegin(); it != cl.crend(); ++it) { + reversed_collected.push_back(*it); + } + + ASSERT_EQ(reversed_collected, (std::vector{9, 8, 7})); +} + +TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}; + l.push_back(a); + + kphp::stl::intrusive::list>::iterator it = l.begin(); + kphp::stl::intrusive::list>::const_iterator cit = it; // implicit non-const -> const conversion + + ASSERT_EQ(it, cit); + ASSERT_EQ(cit, it); + ASSERT_FALSE(it != cit); +} + +TEST(intrusive_list_iteration, works_with_std_algorithms) { + kphp::stl::intrusive::list> l; + std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, + kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(std::distance(l.begin(), l.end()), 5); + + auto found = std::find(l.begin(), l.end(), 3); + + ASSERT_NE(found, l.end()); + ASSERT_EQ(*found, 3); + + ASSERT_EQ(std::find(l.begin(), l.end(), 42), l.end()); + ASSERT_EQ(std::accumulate(l.begin(), l.end(), 0), 15); +} + +TEST(intrusive_list_insert, at_begin) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{2}, b{1}; + l.push_back(a); + auto it = l.insert(l.begin(), b); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, at_end) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.insert(l.end(), a); + l.insert(l.end(), b); + + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, c{3}, b{2}; + l.push_back(a); + l.push_back(c); + auto pos = std::next(l.begin()); // points at 3 + auto it = l.insert(pos, b); + + ASSERT_EQ(*it, 2); + ASSERT_EQ(values(l), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + // Inserting node 'a' right before itself must not corrupt the list. + auto it = l.insert(l.begin(), a); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { + kphp::stl::intrusive::list> src; + kphp::stl::intrusive::list> dst; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + src.push_back(a); + src.push_back(b); + src.push_back(c); + + // Moving 'b' into dst removes it from src (insert unlinks first). + dst.push_back(b); + ASSERT_EQ(values(src), (std::vector{1, 3})); + ASSERT_EQ(values(dst), (std::vector{2})); +} + +TEST(intrusive_list_insert, moves_node_within_same_list) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + // Re-insert front node before end -> moves it to the back. + l.insert(l.end(), a); + + ASSERT_EQ(values(l), (std::vector{2, 3, 1})); + ASSERT_EQ(l.size(), 3); +} + +TEST(intrusive_list_erase, single_returns_next) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + auto next = l.erase(std::next(l.begin())); // erase 2 + + ASSERT_EQ(*next, 3); + ASSERT_EQ(values(l), (std::vector{1, 3})); +} + +TEST(intrusive_list_erase, front) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + auto next = l.erase(l.begin()); + + ASSERT_EQ(*next, 2); + ASSERT_EQ(values(l), (std::vector{2})); +} + +TEST(intrusive_list_erase, end) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + auto next = l.erase(std::prev(l.end())); + + ASSERT_EQ(next, l.end()); + ASSERT_EQ(values(l), (std::vector{1})); +} + +TEST(intrusive_list_erase, range) { + kphp::stl::intrusive::list> l; + std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, + kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { + l.push_back(n); + } + // erase [2, 4) -> removes 2 and 3 + auto first = std::next(l.begin()); + auto last = std::next(l.begin(), 3); + auto it = l.erase(first, last); + + ASSERT_EQ(*it, 4); + ASSERT_EQ(values(l), (std::vector{1, 4, 5})); +} + +TEST(intrusive_list_erase, empty_range_is_noop) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + auto it = l.erase(l.begin(), l.begin()); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_erase, all_makes_empty) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_back(n); + } + auto it = l.erase(l.begin(), l.end()); + + ASSERT_EQ(it, l.end()); + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); +} + +TEST(intrusive_list_erase, node_is_reusable_after_erase) { + kphp::stl::intrusive::list> l1; + kphp::stl::intrusive::list> l2; + kphp::stl::intrusive::list_node a{1}; + l1.push_back(a); + l1.erase(l1.begin()); + + ASSERT_TRUE(l1.empty()); + + // The same node object can be linked into another list afterwards. + l2.push_back(a); + + ASSERT_EQ(values(l2), (std::vector{1})); +} + +TEST(intrusive_list_pop, pop_back) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + l.pop_back(); + + ASSERT_EQ(values(l), (std::vector{1, 2})); + + l.pop_back(); + + ASSERT_EQ(values(l), (std::vector{1})); +} + +TEST(intrusive_list_pop, pop_front) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + l.pop_front(); + + ASSERT_EQ(values(l), (std::vector{2, 3})); + l.pop_front(); + + ASSERT_EQ(values(l), (std::vector{3})); +} + +TEST(intrusive_list_pop, pop_until_empty) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + l.pop_front(); + l.pop_back(); + + ASSERT_TRUE(l.empty()); +} + +TEST(intrusive_list_clear, makes_list_empty) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_back(n); + } + l.clear(); + + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); + ASSERT_EQ(l.begin(), l.end()); +} + +TEST(intrusive_list_clear, list_is_reusable_after_clear) { + kphp::stl::intrusive::list> l; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + l.push_back(n); + } + l.clear(); + kphp::stl::intrusive::list_node x{10}, y{20}; + l.push_back(x); + l.push_back(y); + + ASSERT_EQ(values(l), (std::vector{10, 20})); +} + +TEST(intrusive_list_swap, two_non_empty) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}; + kphp::stl::intrusive::list_node b1{3}, b2{4}, b3{5}; + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + b.push_back(b3); + + a.swap(b); + + ASSERT_EQ(values(a), (std::vector{3, 4, 5})); + ASSERT_EQ(values(b), (std::vector{1, 2})); +} + +TEST(intrusive_list_swap, empty_with_non_empty) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node b1{1}, b2{2}; + b.push_back(b1); + b.push_back(b2); + + a.swap(b); + + ASSERT_EQ(values(a), (std::vector{1, 2})); + ASSERT_TRUE(b.empty()); + + a.swap(b); + + ASSERT_TRUE(a.empty()); + ASSERT_EQ(values(b), (std::vector{1, 2})); +} + +TEST(intrusive_list_swap, both_empty) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + a.swap(b); + + ASSERT_TRUE(a.empty()); + ASSERT_TRUE(b.empty()); +} + +TEST(intrusive_list_swap, free_function) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}; + kphp::stl::intrusive::list_node b1{2}, b2{3}; + a.push_back(a1); + b.push_back(b1); + b.push_back(b2); + + swap(a, b); + + ASSERT_EQ(values(a), (std::vector{2, 3})); + ASSERT_EQ(values(b), (std::vector{1})); +} + +TEST(intrusive_list_splice, whole_list_at_end) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; + kphp::stl::intrusive::list_node b1{4}, b2{5}; + a.push_back(a1); + a.push_back(a2); + a.push_back(a3); + b.push_back(b1); + b.push_back(b2); + + b.splice(b.end(), a); + + ASSERT_EQ(values(b), (std::vector{4, 5, 1, 2, 3})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, whole_list_at_begin) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}; + kphp::stl::intrusive::list_node b1{3}, b2{4}; + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + + b.splice(b.begin(), a); + + ASSERT_EQ(values(b), (std::vector{1, 2, 3, 4})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, whole_list_in_middle) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}; + kphp::stl::intrusive::list_node b1{3}, b2{4}; + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + + b.splice(std::next(b.begin()), a); // between 3 and 4 + + ASSERT_EQ(values(b), (std::vector{3, 1, 2, 4})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, single_element) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; + a.push_back(a1); + a.push_back(a2); + a.push_back(a3); + + b.splice(b.end(), a, std::next(a.begin())); // move `2` + + ASSERT_EQ(values(b), (std::vector{2})); + ASSERT_EQ(values(a), (std::vector{1, 3})); +} + +TEST(intrusive_list_splice, sub_range) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, + kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { + a.push_back(n); + } + // move [2, 4) -> nodes 2 and 3 + auto first = std::next(a.begin()); + auto last = std::next(a.begin(), 3); + b.splice(b.end(), a, first, last); + + ASSERT_EQ(values(b), (std::vector{2, 3})); + ASSERT_EQ(values(a), (std::vector{1, 4, 5})); +} + +TEST(intrusive_list_splice, empty_other_is_noop) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node b1{1}, b2{2}; + b.push_back(b1); + b.push_back(b2); + + b.splice(b.begin(), a); + + ASSERT_EQ(values(b), (std::vector{1, 2})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, within_same_list_moves_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + // move node `2` to the front + l.splice(l.begin(), l, std::next(l.begin())); + + ASSERT_EQ(values(l), (std::vector{2, 1, 3})); + ASSERT_EQ(l.size(), 3); +} + +TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + // splicing the first node to the position right after it (== its own place) is a noop + l.splice(std::next(l.begin()), l, l.begin()); + + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_splice, rvalue_overloads) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list> b; + kphp::stl::intrusive::list_node a1{1}, a2{2}; + a.push_back(a1); + a.push_back(a2); + + b.splice(b.end(), std::move(a)); + + ASSERT_EQ(values(b), (std::vector{1, 2})); + ASSERT_TRUE(a.empty()); + + a.splice(a.begin(), std::move(b), b.begin()); + + ASSERT_EQ(values(a), (std::vector{1})); + ASSERT_EQ(values(b), (std::vector{2})); + + a.splice(a.begin(), std::move(b), b.begin(), std::next(b.begin())); + + ASSERT_EQ(values(a), (std::vector{2, 1})); + ASSERT_TRUE(b.empty()); +} + +TEST(intrusive_list_move, move_construct_transfers_nodes) { + kphp::stl::intrusive::list> src; + std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, + kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { + src.push_back(n); + } + kphp::stl::intrusive::list> dst{std::move(src)}; + + ASSERT_EQ(values(dst), (std::vector{1, 2, 3})); + ASSERT_TRUE(src.empty()); +} + +TEST(intrusive_list_move, move_assign_into_empty) { + kphp::stl::intrusive::list> src; + kphp::stl::intrusive::list_node a{1}, b{2}; + src.push_back(a); + src.push_back(b); + + kphp::stl::intrusive::list> dst; + dst = std::move(src); + + ASSERT_EQ(values(dst), (std::vector{1, 2})); + ASSERT_TRUE(src.empty()); +} + +TEST(intrusive_list_move, move_assign) { + kphp::stl::intrusive::list> src; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + src.push_back(a); + src.push_back(b); + + kphp::stl::intrusive::list> dst; + dst.push_back(c); + dst.push_back(d); + dst = std::move(src); + + ASSERT_EQ(values(dst), (std::vector{1, 2})); + ASSERT_TRUE(src.empty()); +} + +namespace { + +struct tag_a {}; +struct tag_b {}; + +} // namespace + +TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + + lb.push_back(n3); + lb.push_back(n2); + lb.push_back(n1); + + ASSERT_EQ(values(la), (std::vector{1, 2, 3})); + ASSERT_EQ(values(lb), (std::vector{3, 2, 1})); +} + +TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + lb.push_back(n1); + lb.push_back(n2); + lb.push_back(n3); + + la.erase(la.begin()); // remove n1 from `la` only + + ASSERT_EQ(values(la), (std::vector{2, 3})); + ASSERT_EQ(values(lb), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n{5}; + la.push_back(n); + lb.push_back(n); + + la.front() = 99; + + ASSERT_EQ(lb.front(), 99); +} + +TEST(intrusive_list_value, stores_move_only_value_type) { + kphp::stl::intrusive::list>> l; + kphp::stl::intrusive::list_node> a{std::make_unique(1)}; + kphp::stl::intrusive::list_node> b{std::make_unique(2)}; + l.push_back(a); + l.push_back(b); + + auto it = l.begin(); + + ASSERT_EQ(**it, 1); + + ++it; + + ASSERT_EQ(**it, 2); +} + +TEST(intrusive_list_value, make_list_node_helper) { + auto n = kphp::stl::intrusive::make_list_node(123); + static_assert(std::is_same_v); + kphp::stl::intrusive::list> l; + l.push_back(n); + + ASSERT_EQ(l.front(), 123); +} diff --git a/tests/cpp/runtime/runtime-tests.cmake b/tests/cpp/runtime/runtime-tests.cmake index 731da98fe6..51d697605a 100644 --- a/tests/cpp/runtime/runtime-tests.cmake +++ b/tests/cpp/runtime/runtime-tests.cmake @@ -20,6 +20,7 @@ prepend(RUNTIME_TESTS_SOURCES ${BASE_DIR}/tests/cpp/runtime/ memory_resource/extra-memory-pool-test.cpp memory_resource/unsynchronized_pool_resource-test.cpp string-list-test.cpp + intrusive-list-test.cpp string-test.cpp zstd-test.cpp) From d0e1eb85320ffa5d95901c6b02e493109874e500 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Mon, 27 Jul 2026 18:41:18 +0300 Subject: [PATCH 17/35] add default constructor to list_node --- runtime-common/core/std/intrusive-list.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index 0872cc1095..e8a6626bbc 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -65,7 +65,7 @@ struct default_tag {}; template class list_node final : private std::conditional_t, details::tagged_hooks> { - T m_value; + T m_value{}; template friend class kphp::stl::intrusive::list; @@ -77,6 +77,8 @@ class list_node final : private std::conditional_t, std::tuple>; + list_node() noexcept = default; + explicit list_node(T value) noexcept : m_value{std::move(value)} {} @@ -146,7 +148,7 @@ class list_iterator { static auto value_from_list_node_base(details::list_node_base* node) noexcept -> reference { using tagged_hooks_t = vk::apply_tuple_t; - /* + /* * save cast to local variable to avoid compilation error in g++-11: * error: ‘this’ pointer is null [-Werror=nonnull] * 150 | return (static_cast(static_cast(static_cast*>(node))))->value(); From 7f97f17b846e597761bf72bc3ba94edc0c98caa6 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 12:13:51 +0300 Subject: [PATCH 18/35] add iterator_to method into list --- runtime-common/core/std/intrusive-list.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index e8a6626bbc..c04d5aca79 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -243,6 +243,11 @@ class list { return static_cast(static_cast*>(static_cast(std::addressof(node)))); } + static auto list_node_base_from_list_node(const Node& node) noexcept -> const details::list_node_base* { + using tagged_hooks_t = vk::apply_tuple_t; + return static_cast(static_cast*>(static_cast(std::addressof(node)))); + } + auto splice_impl(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void { if (first == last || (this == std::addressof(other) && last == pos)) { return; @@ -386,6 +391,14 @@ class list { return iterator{last.m_curr}; } + auto iterator_to(Node& node) noexcept -> iterator { + return iterator{list_node_base_from_list_node(node)}; + } + + auto iterator_to(const Node& node) const noexcept -> const_iterator { + return const_iterator{list_node_base_from_list_node(node)}; + } + auto push_back(Node& node) noexcept -> void { insert(end(), node); } From e28e19c34538c927f2dd2606f5f09d294763c09d Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 14:48:44 +0300 Subject: [PATCH 19/35] add tests for iterator_to method --- runtime-common/core/std/intrusive-list.h | 2 +- tests/cpp/runtime/intrusive-list-test.cpp | 93 +++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index c04d5aca79..a2588fcd3b 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -249,7 +249,7 @@ class list { } auto splice_impl(const_iterator pos, list& other, const_iterator first, const_iterator last) noexcept -> void { - if (first == last || (this == std::addressof(other) && last == pos)) { + if (first == last || (this == std::addressof(other) && (first == pos || last == pos))) { return; } diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp index 3bb679bfb3..5a62d8ff86 100644 --- a/tests/cpp/runtime/intrusive-list-test.cpp +++ b/tests/cpp/runtime/intrusive-list-test.cpp @@ -525,6 +525,19 @@ TEST(intrusive_list_swap, free_function) { ASSERT_EQ(values(b), (std::vector{1})); } +TEST(intrusive_list_swap, swap_with_self) { + kphp::stl::intrusive::list> a; + kphp::stl::intrusive::list_node a1{1}, a2{2}; + + a.swap(a); + + ASSERT_EQ(values(a), (std::vector{1, 2})); + + swap(a, a); + + ASSERT_EQ(values(a), (std::vector{1, 2})); +} + TEST(intrusive_list_splice, whole_list_at_end) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; @@ -641,6 +654,11 @@ TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { l.splice(std::next(l.begin()), l, l.begin()); ASSERT_EQ(values(l), (std::vector{1, 2})); + + // splicing the first node to the position right before it (== its own place) is a noop + l.splice(l.begin(), l, l.begin()); + + ASSERT_EQ(values(l), (std::vector{1, 2})); } TEST(intrusive_list_splice, rvalue_overloads) { @@ -785,3 +803,78 @@ TEST(intrusive_list_value, make_list_node_helper) { ASSERT_EQ(l.front(), 123); } + +TEST(intrusive_list_iterator_to, single_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{42}; + l.push_back(a); + + auto it = l.iterator_to(a); + + ASSERT_EQ(it, l.begin()); + ASSERT_EQ(*it, 42); +} + +TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + + auto it = l.iterator_to(b); + + ASSERT_EQ(*it, 2); + ASSERT_EQ(it, std::next(l.begin())); + ASSERT_EQ(*std::prev(it), 1); + ASSERT_EQ(*std::next(it), 3); +} + +TEST(intrusive_list_iterator_to, works_for_front_and_back) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); + l.push_back(b); + l.push_back(c); + + ASSERT_EQ(l.iterator_to(a), l.begin()); + ASSERT_EQ(l.iterator_to(c), std::prev(l.end())); +} + +TEST(intrusive_list_iterator_to, const_overload) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + const auto& cl = l; + + auto cit = cl.iterator_to(b); + + ASSERT_EQ(*cit, 2); + ASSERT_EQ(cit, std::next(cl.cbegin())); +} + +TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_via_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}; + l.push_back(a); + + auto it = l.iterator_to(a); + *it = 100; + + ASSERT_EQ(a.value(), 100); +} + +TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); + l.push_back(b); + + auto relocated = std::make_unique>(std::move(a)); + auto it = l.iterator_to(*relocated); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(it, l.begin()); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} From 9316805ef6d661c57ed47c26393594a9e42fe509 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 15:33:51 +0300 Subject: [PATCH 20/35] add self-swap guard --- runtime-common/core/std/intrusive-list.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index a2588fcd3b..ca81669dcc 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -416,6 +416,10 @@ class list { } auto swap(list& other) noexcept -> void { + if (this == std::addressof(other)) { + return; + } + details::list_node_base tmp{std::move(m_sentinel)}; m_sentinel = std::move(other.m_sentinel); other.m_sentinel = std::move(tmp); From 30816bbe7d1c90fc396cfed265522bd4d427fa42 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 15:34:09 +0300 Subject: [PATCH 21/35] add more tests --- tests/cpp/runtime/intrusive-list-test.cpp | 236 ++++++++++++++++++---- 1 file changed, 199 insertions(+), 37 deletions(-) diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp index 5a62d8ff86..ff4263295c 100644 --- a/tests/cpp/runtime/intrusive-list-test.cpp +++ b/tests/cpp/runtime/intrusive-list-test.cpp @@ -41,6 +41,7 @@ TEST(intrusive_list_node_base, default_is_not_linked) { TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { kphp::stl::intrusive::details::list_node_base b; + b.unlink(); b.unlink(); @@ -75,6 +76,7 @@ TEST(intrusive_list_basic, push_back_keeps_order) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_back(n); } @@ -100,6 +102,7 @@ TEST(intrusive_list_basic, push_front_reverses_order) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_front(n); } @@ -111,6 +114,7 @@ TEST(intrusive_list_basic, push_front_reverses_order) { TEST(intrusive_list_basic, mixed_push_front_and_back) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + l.push_back(b); // 2 l.push_front(a); // 1 2 l.push_back(c); // 1 2 3 @@ -122,6 +126,7 @@ TEST(intrusive_list_basic, mixed_push_front_and_back) { TEST(intrusive_list_basic, front_back_are_mutable_references) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); l.front() = 10; @@ -136,6 +141,7 @@ TEST(intrusive_list_iteration, forward) { kphp::stl::intrusive::list> l; std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + for (auto& n : ns) { l.push_back(n); } @@ -147,6 +153,7 @@ TEST(intrusive_list_iteration, reverse) { kphp::stl::intrusive::list> l; std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + for (auto& n : ns) { l.push_back(n); } @@ -158,6 +165,7 @@ TEST(intrusive_list_iteration, bidirectional_increment_decrement) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_back(n); } @@ -181,6 +189,7 @@ TEST(intrusive_list_iteration, const_iteration) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{7}, kphp::stl::intrusive::list_node{8}, kphp::stl::intrusive::list_node{9}}; + for (auto& n : ns) { l.push_back(n); } @@ -206,6 +215,7 @@ TEST(intrusive_list_iteration, const_iteration) { TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}; + l.push_back(a); kphp::stl::intrusive::list>::iterator it = l.begin(); @@ -221,6 +231,7 @@ TEST(intrusive_list_iteration, works_with_std_algorithms) { std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { l.push_back(n); } @@ -239,6 +250,7 @@ TEST(intrusive_list_iteration, works_with_std_algorithms) { TEST(intrusive_list_insert, at_begin) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{2}, b{1}; + l.push_back(a); auto it = l.insert(l.begin(), b); @@ -249,6 +261,7 @@ TEST(intrusive_list_insert, at_begin) { TEST(intrusive_list_insert, at_end) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.insert(l.end(), a); l.insert(l.end(), b); @@ -258,6 +271,7 @@ TEST(intrusive_list_insert, at_end) { TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, c{3}, b{2}; + l.push_back(a); l.push_back(c); auto pos = std::next(l.begin()); // points at 3 @@ -270,6 +284,7 @@ TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); // Inserting node 'a' right before itself must not corrupt the list. @@ -283,6 +298,7 @@ TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { kphp::stl::intrusive::list> src; kphp::stl::intrusive::list> dst; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + src.push_back(a); src.push_back(b); src.push_back(c); @@ -296,6 +312,7 @@ TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { TEST(intrusive_list_insert, moves_node_within_same_list) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -309,6 +326,7 @@ TEST(intrusive_list_insert, moves_node_within_same_list) { TEST(intrusive_list_erase, single_returns_next) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -321,6 +339,7 @@ TEST(intrusive_list_erase, single_returns_next) { TEST(intrusive_list_erase, front) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); auto next = l.erase(l.begin()); @@ -332,6 +351,7 @@ TEST(intrusive_list_erase, front) { TEST(intrusive_list_erase, end) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); auto next = l.erase(std::prev(l.end())); @@ -345,6 +365,7 @@ TEST(intrusive_list_erase, range) { std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { l.push_back(n); } @@ -360,6 +381,7 @@ TEST(intrusive_list_erase, range) { TEST(intrusive_list_erase, empty_range_is_noop) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); auto it = l.erase(l.begin(), l.begin()); @@ -372,6 +394,7 @@ TEST(intrusive_list_erase, all_makes_empty) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_back(n); } @@ -386,6 +409,7 @@ TEST(intrusive_list_erase, node_is_reusable_after_erase) { kphp::stl::intrusive::list> l1; kphp::stl::intrusive::list> l2; kphp::stl::intrusive::list_node a{1}; + l1.push_back(a); l1.erase(l1.begin()); @@ -400,6 +424,7 @@ TEST(intrusive_list_erase, node_is_reusable_after_erase) { TEST(intrusive_list_pop, pop_back) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -415,6 +440,7 @@ TEST(intrusive_list_pop, pop_back) { TEST(intrusive_list_pop, pop_front) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -429,6 +455,7 @@ TEST(intrusive_list_pop, pop_front) { TEST(intrusive_list_pop, pop_until_empty) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); l.pop_front(); @@ -441,6 +468,7 @@ TEST(intrusive_list_clear, makes_list_empty) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_back(n); } @@ -455,6 +483,7 @@ TEST(intrusive_list_clear, list_is_reusable_after_clear) { kphp::stl::intrusive::list> l; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { l.push_back(n); } @@ -471,6 +500,7 @@ TEST(intrusive_list_swap, two_non_empty) { kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}; kphp::stl::intrusive::list_node b1{3}, b2{4}, b3{5}; + a.push_back(a1); a.push_back(a2); b.push_back(b1); @@ -487,6 +517,7 @@ TEST(intrusive_list_swap, empty_with_non_empty) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node b1{1}, b2{2}; + b.push_back(b1); b.push_back(b2); @@ -504,6 +535,7 @@ TEST(intrusive_list_swap, empty_with_non_empty) { TEST(intrusive_list_swap, both_empty) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; + a.swap(b); ASSERT_TRUE(a.empty()); @@ -515,6 +547,7 @@ TEST(intrusive_list_swap, free_function) { kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}; kphp::stl::intrusive::list_node b1{2}, b2{3}; + a.push_back(a1); b.push_back(b1); b.push_back(b2); @@ -529,6 +562,9 @@ TEST(intrusive_list_swap, swap_with_self) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list_node a1{1}, a2{2}; + a.push_back(a1); + a.push_back(a2); + a.swap(a); ASSERT_EQ(values(a), (std::vector{1, 2})); @@ -543,6 +579,7 @@ TEST(intrusive_list_splice, whole_list_at_end) { kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; kphp::stl::intrusive::list_node b1{4}, b2{5}; + a.push_back(a1); a.push_back(a2); a.push_back(a3); @@ -560,6 +597,7 @@ TEST(intrusive_list_splice, whole_list_at_begin) { kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}; kphp::stl::intrusive::list_node b1{3}, b2{4}; + a.push_back(a1); a.push_back(a2); b.push_back(b1); @@ -576,6 +614,7 @@ TEST(intrusive_list_splice, whole_list_in_middle) { kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}; kphp::stl::intrusive::list_node b1{3}, b2{4}; + a.push_back(a1); a.push_back(a2); b.push_back(b1); @@ -591,6 +630,7 @@ TEST(intrusive_list_splice, single_element) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; + a.push_back(a1); a.push_back(a2); a.push_back(a3); @@ -607,6 +647,7 @@ TEST(intrusive_list_splice, sub_range) { std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, kphp::stl::intrusive::list_node{5}}; + for (auto& n : ns) { a.push_back(n); } @@ -623,6 +664,7 @@ TEST(intrusive_list_splice, empty_other_is_noop) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node b1{1}, b2{2}; + b.push_back(b1); b.push_back(b2); @@ -635,6 +677,7 @@ TEST(intrusive_list_splice, empty_other_is_noop) { TEST(intrusive_list_splice, within_same_list_moves_node) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -648,6 +691,7 @@ TEST(intrusive_list_splice, within_same_list_moves_node) { TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); // splicing the first node to the position right after it (== its own place) is a noop @@ -665,6 +709,7 @@ TEST(intrusive_list_splice, rvalue_overloads) { kphp::stl::intrusive::list> a; kphp::stl::intrusive::list> b; kphp::stl::intrusive::list_node a1{1}, a2{2}; + a.push_back(a1); a.push_back(a2); @@ -688,6 +733,7 @@ TEST(intrusive_list_move, move_construct_transfers_nodes) { kphp::stl::intrusive::list> src; std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, kphp::stl::intrusive::list_node{3}}; + for (auto& n : ns) { src.push_back(n); } @@ -700,10 +746,12 @@ TEST(intrusive_list_move, move_construct_transfers_nodes) { TEST(intrusive_list_move, move_assign_into_empty) { kphp::stl::intrusive::list> src; kphp::stl::intrusive::list_node a{1}, b{2}; + src.push_back(a); src.push_back(b); kphp::stl::intrusive::list> dst; + dst = std::move(src); ASSERT_EQ(values(dst), (std::vector{1, 2})); @@ -713,10 +761,12 @@ TEST(intrusive_list_move, move_assign_into_empty) { TEST(intrusive_list_move, move_assign) { kphp::stl::intrusive::list> src; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + src.push_back(a); src.push_back(b); kphp::stl::intrusive::list> dst; + dst.push_back(c); dst.push_back(d); dst = std::move(src); @@ -725,64 +775,81 @@ TEST(intrusive_list_move, move_assign) { ASSERT_TRUE(src.empty()); } -namespace { +TEST(intrusive_list_move, move_linked_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; -struct tag_a {}; -struct tag_b {}; + l.push_back(a); + l.push_back(b); -} // namespace + kphp::stl::intrusive::list_node c{std::move(a)}; -TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + ASSERT_EQ(values(l), (std::vector{1, 2})); - la.push_back(n1); - la.push_back(n2); - la.push_back(n3); + ASSERT_EQ(std::addressof(c.value()), std::addressof(*l.begin())); +} - lb.push_back(n3); - lb.push_back(n2); - lb.push_back(n1); +TEST(intrusive_list_move, move_assign_unlinked_node) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; - ASSERT_EQ(values(la), (std::vector{1, 2, 3})); - ASSERT_EQ(values(lb), (std::vector{3, 2, 1})); + l.push_back(a); + l.push_back(b); + + kphp::stl::intrusive::list_node c{0}; + c = std::move(a); + + ASSERT_EQ(values(l), (std::vector{1, 2})); + ASSERT_EQ(std::addressof(l.front()), std::addressof(c.value())); } -TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; +TEST(intrusive_list_move, move_assign_linked_node) { + kphp::stl::intrusive::list> l1; + kphp::stl::intrusive::list> l2; + kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; - la.push_back(n1); - la.push_back(n2); - la.push_back(n3); - lb.push_back(n1); - lb.push_back(n2); - lb.push_back(n3); + l1.push_back(a); + l1.push_back(b); + l2.push_back(c); - la.erase(la.begin()); // remove n1 from `la` only + // 'c' is already linked in l2; assigning into it must first unlink it from l2, + // then take over 'a's place in l1. + c = std::move(a); - ASSERT_EQ(values(la), (std::vector{2, 3})); - ASSERT_EQ(values(lb), (std::vector{1, 2, 3})); + ASSERT_TRUE(l2.empty()); + ASSERT_EQ(values(l1), (std::vector{1, 2})); + ASSERT_EQ(std::addressof(l1.front()), std::addressof(c.value())); } -TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n{5}; - la.push_back(n); - lb.push_back(n); +TEST(intrusive_list_move, self_move_assign_node_is_noop) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}; - la.front() = 99; + l.push_back(a); - ASSERT_EQ(lb.front(), 99); + a = std::move(a); + + ASSERT_EQ(values(l), (std::vector{1})); + ASSERT_EQ(std::addressof(l.front()), std::addressof(a.value())); +} + +TEST(intrusive_list_move, self_move_assign_is_noop) { + kphp::stl::intrusive::list> l; + kphp::stl::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + + l = std::move(l); + + ASSERT_EQ(values(l), (std::vector{1, 2})); } TEST(intrusive_list_value, stores_move_only_value_type) { kphp::stl::intrusive::list>> l; kphp::stl::intrusive::list_node> a{std::make_unique(1)}; kphp::stl::intrusive::list_node> b{std::make_unique(2)}; + l.push_back(a); l.push_back(b); @@ -799,6 +866,7 @@ TEST(intrusive_list_value, make_list_node_helper) { auto n = kphp::stl::intrusive::make_list_node(123); static_assert(std::is_same_v); kphp::stl::intrusive::list> l; + l.push_back(n); ASSERT_EQ(l.front(), 123); @@ -807,6 +875,7 @@ TEST(intrusive_list_value, make_list_node_helper) { TEST(intrusive_list_iterator_to, single_node) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{42}; + l.push_back(a); auto it = l.iterator_to(a); @@ -818,6 +887,7 @@ TEST(intrusive_list_iterator_to, single_node) { TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -833,6 +903,7 @@ TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { TEST(intrusive_list_iterator_to, works_for_front_and_back) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + l.push_back(a); l.push_back(b); l.push_back(c); @@ -844,6 +915,7 @@ TEST(intrusive_list_iterator_to, works_for_front_and_back) { TEST(intrusive_list_iterator_to, const_overload) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); const auto& cl = l; @@ -857,6 +929,7 @@ TEST(intrusive_list_iterator_to, const_overload) { TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_via_node) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}; + l.push_back(a); auto it = l.iterator_to(a); @@ -868,6 +941,7 @@ TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_v TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { kphp::stl::intrusive::list> l; kphp::stl::intrusive::list_node a{1}, b{2}; + l.push_back(a); l.push_back(b); @@ -878,3 +952,91 @@ TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { ASSERT_EQ(it, l.begin()); ASSERT_EQ(values(l), (std::vector{1, 2})); } + +namespace { + +struct tag_a {}; +struct tag_b {}; + +} // namespace + +TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + + lb.push_back(n3); + lb.push_back(n2); + lb.push_back(n1); + + ASSERT_EQ(values(la), (std::vector{1, 2, 3})); + ASSERT_EQ(values(lb), (std::vector{3, 2, 1})); +} + +TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + lb.push_back(n1); + lb.push_back(n2); + lb.push_back(n3); + + la.erase(la.begin()); // remove n1 from `la` only + + ASSERT_EQ(values(la), (std::vector{2, 3})); + ASSERT_EQ(values(lb), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n{5}; + + la.push_back(n); + lb.push_back(n); + + la.front() = 99; + + ASSERT_EQ(lb.front(), 99); +} + +TEST(intrusive_list_tags, move_linked_node_with_two_tags) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n{5}; + + la.push_back(n); + lb.push_back(n); + + kphp::stl::intrusive::list_node new_n{std::move(n)}; + + ASSERT_EQ(values(la), (std::vector{5})); + ASSERT_EQ(values(lb), (std::vector{5})); + ASSERT_EQ(la.iterator_to(new_n), la.begin()); + ASSERT_EQ(lb.iterator_to(new_n), lb.begin()); +} + +TEST(intrusive_list_tags, move_assign_linked_node_with_two_tags) { + kphp::stl::intrusive::list, tag_a> la; + kphp::stl::intrusive::list, tag_b> lb; + kphp::stl::intrusive::list_node n{5}; + kphp::stl::intrusive::list_node dst{7}; + + la.push_back(n); + lb.push_back(n); + + dst = std::move(n); + + ASSERT_EQ(values(la), (std::vector{5})); + ASSERT_EQ(values(lb), (std::vector{5})); + ASSERT_EQ(la.iterator_to(dst), la.begin()); + ASSERT_EQ(lb.iterator_to(dst), lb.begin()); +} From da30160d60739198ac20c29bc7b31fecad886c0e Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 17:01:51 +0300 Subject: [PATCH 22/35] apply code style --- runtime-common/core/std/intrusive-list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-common/core/std/intrusive-list.h b/runtime-common/core/std/intrusive-list.h index ca81669dcc..0bf5def5d3 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/runtime-common/core/std/intrusive-list.h @@ -419,7 +419,7 @@ class list { if (this == std::addressof(other)) { return; } - + details::list_node_base tmp{std::move(m_sentinel)}; m_sentinel = std::move(other.m_sentinel); other.m_sentinel = std::move(tmp); From 1466106fe67b123516a84e6bf9e454cacbac031f Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Tue, 28 Jul 2026 17:19:28 +0300 Subject: [PATCH 23/35] add fix to avoid compilation error [-Wself-move] --- tests/cpp/runtime/intrusive-list-test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp index ff4263295c..da16f9eb87 100644 --- a/tests/cpp/runtime/intrusive-list-test.cpp +++ b/tests/cpp/runtime/intrusive-list-test.cpp @@ -827,7 +827,8 @@ TEST(intrusive_list_move, self_move_assign_node_is_noop) { l.push_back(a); - a = std::move(a); + auto* self = std::addressof(a); // to avoid compilation error [-Wself-move] + a = std::move(*self); ASSERT_EQ(values(l), (std::vector{1})); ASSERT_EQ(std::addressof(l.front()), std::addressof(a.value())); @@ -840,7 +841,8 @@ TEST(intrusive_list_move, self_move_assign_is_noop) { l.push_back(a); l.push_back(b); - l = std::move(l); + auto* self = std::addressof(l); // to avoid compilation error [-Wself-move] + l = std::move(*self); ASSERT_EQ(values(l), (std::vector{1, 2})); } From bffcdf60b831645ca5b2cb49eb13eb3948160fa7 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 12:44:28 +0300 Subject: [PATCH 24/35] move intrusive list to common folder --- .../containers}/intrusive-list.cpp | 4 +- .../containers}/intrusive-list.h | 16 +- runtime-common/core/std/containers.h | 8 + tests/cpp/runtime/intrusive-list-test.cpp | 361 +++++++++--------- 4 files changed, 193 insertions(+), 196 deletions(-) rename {runtime-common/core/std => common/containers}/intrusive-list.cpp (91%) rename {runtime-common/core/std => common/containers}/intrusive-list.h (97%) diff --git a/runtime-common/core/std/intrusive-list.cpp b/common/containers/intrusive-list.cpp similarity index 91% rename from runtime-common/core/std/intrusive-list.cpp rename to common/containers/intrusive-list.cpp index e01fb88133..a791d79d9a 100644 --- a/runtime-common/core/std/intrusive-list.cpp +++ b/common/containers/intrusive-list.cpp @@ -3,7 +3,7 @@ #include #include -namespace kphp::stl::intrusive::details { +namespace vk::intrusive::details { auto list_node_base::take_place_of(list_node_base&& other) noexcept -> void { if (other.is_linked()) { @@ -44,4 +44,4 @@ auto list_node_base::unlink() noexcept -> void { m_next = this; } -} // namespace kphp::stl::intrusive::details +} // namespace vk::intrusive::details diff --git a/runtime-common/core/std/intrusive-list.h b/common/containers/intrusive-list.h similarity index 97% rename from runtime-common/core/std/intrusive-list.h rename to common/containers/intrusive-list.h index 0bf5def5d3..95788eaf19 100644 --- a/runtime-common/core/std/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -13,7 +13,7 @@ #include "common/type_traits/apply_tuple.h" -namespace kphp::stl::intrusive { +namespace vk::intrusive { template class list; @@ -30,10 +30,10 @@ class list_node_base { auto take_place_of(list_node_base&& other) noexcept -> void; template - friend class kphp::stl::intrusive::list; + friend class vk::intrusive::list; template - friend class kphp::stl::intrusive::list_iterator; + friend class vk::intrusive::list_iterator; public: list_node_base() noexcept = default; @@ -68,10 +68,10 @@ class list_node final : private std::conditional_t - friend class kphp::stl::intrusive::list; + friend class vk::intrusive::list; template - friend class kphp::stl::intrusive::list_iterator; + friend class vk::intrusive::list_iterator; public: using value_type = T; @@ -158,10 +158,10 @@ class list_iterator { } template - friend class kphp::stl::intrusive::list_iterator; + friend class vk::intrusive::list_iterator; template - friend class kphp::stl::intrusive::list; + friend class vk::intrusive::list; public: list_iterator() noexcept = default; @@ -455,4 +455,4 @@ auto swap(list& lhs, list& rhs) noexcept -> void { lhs.swap(rhs); } -} // namespace kphp::stl::intrusive +} // namespace vk::intrusive diff --git a/runtime-common/core/std/containers.h b/runtime-common/core/std/containers.h index d0b2563973..4538e5ec0f 100644 --- a/runtime-common/core/std/containers.h +++ b/runtime-common/core/std/containers.h @@ -4,6 +4,7 @@ #pragma once +#include "common/containers/intrusive-list.h" #include #include #include @@ -50,6 +51,13 @@ using stack = std::stack>; template class Allocator> using string = std::basic_string, Allocator>; +namespace intrusive { + +template +using list = vk::intrusive::list; + +} // namespace intrusive + } // namespace stl } // namespace kphp diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp index da16f9eb87..6496a55539 100644 --- a/tests/cpp/runtime/intrusive-list-test.cpp +++ b/tests/cpp/runtime/intrusive-list-test.cpp @@ -7,7 +7,7 @@ #include #include -#include "runtime-common/core/std/intrusive-list.h" +#include "common/containers/intrusive-list.h" namespace { @@ -34,13 +34,13 @@ auto reversed_values(List& l) -> std::vector { } // namespace TEST(intrusive_list_node_base, default_is_not_linked) { - kphp::stl::intrusive::details::list_node_base b; + vk::intrusive::details::list_node_base b; ASSERT_FALSE(b.is_linked()); } TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { - kphp::stl::intrusive::details::list_node_base b; + vk::intrusive::details::list_node_base b; b.unlink(); b.unlink(); @@ -49,7 +49,7 @@ TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { } TEST(intrusive_list_basic, empty_list) { - kphp::stl::intrusive::list> l; + vk::intrusive::list> l; ASSERT_TRUE(l.empty()); ASSERT_EQ(l.size(), 0); @@ -60,8 +60,8 @@ TEST(intrusive_list_basic, empty_list) { } TEST(intrusive_list_basic, push_back_single) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node n{42}; + vk::intrusive::list> l; + vk::intrusive::list_node n{42}; l.push_back(n); @@ -73,9 +73,8 @@ TEST(intrusive_list_basic, push_back_single) { } TEST(intrusive_list_basic, push_back_keeps_order) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_back(n); @@ -86,8 +85,8 @@ TEST(intrusive_list_basic, push_back_keeps_order) { } TEST(intrusive_list_basic, push_front_single) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node n{42}; + vk::intrusive::list> l; + vk::intrusive::list_node n{42}; l.push_front(n); @@ -99,9 +98,8 @@ TEST(intrusive_list_basic, push_front_single) { } TEST(intrusive_list_basic, push_front_reverses_order) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_front(n); @@ -112,8 +110,8 @@ TEST(intrusive_list_basic, push_front_reverses_order) { } TEST(intrusive_list_basic, mixed_push_front_and_back) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; l.push_back(b); // 2 l.push_front(a); // 1 2 @@ -124,8 +122,8 @@ TEST(intrusive_list_basic, mixed_push_front_and_back) { } TEST(intrusive_list_basic, front_back_are_mutable_references) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -138,9 +136,9 @@ TEST(intrusive_list_basic, front_back_are_mutable_references) { } TEST(intrusive_list_iteration, forward) { - kphp::stl::intrusive::list> l; - std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + vk::intrusive::list> l; + std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}}; for (auto& n : ns) { l.push_back(n); @@ -150,9 +148,9 @@ TEST(intrusive_list_iteration, forward) { } TEST(intrusive_list_iteration, reverse) { - kphp::stl::intrusive::list> l; - std::array, 4> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}}; + vk::intrusive::list> l; + std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}}; for (auto& n : ns) { l.push_back(n); @@ -162,9 +160,8 @@ TEST(intrusive_list_iteration, reverse) { } TEST(intrusive_list_iteration, bidirectional_increment_decrement) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_back(n); @@ -186,14 +183,13 @@ TEST(intrusive_list_iteration, bidirectional_increment_decrement) { } TEST(intrusive_list_iteration, const_iteration) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{7}, kphp::stl::intrusive::list_node{8}, - kphp::stl::intrusive::list_node{9}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{7}, vk::intrusive::list_node{8}, vk::intrusive::list_node{9}}; for (auto& n : ns) { l.push_back(n); } - const kphp::stl::intrusive::list>& cl = l; + const vk::intrusive::list>& cl = l; ASSERT_EQ(values(cl), (std::vector{7, 8, 9})); @@ -213,13 +209,13 @@ TEST(intrusive_list_iteration, const_iteration) { } TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; l.push_back(a); - kphp::stl::intrusive::list>::iterator it = l.begin(); - kphp::stl::intrusive::list>::const_iterator cit = it; // implicit non-const -> const conversion + vk::intrusive::list>::iterator it = l.begin(); + vk::intrusive::list>::const_iterator cit = it; // implicit non-const -> const conversion ASSERT_EQ(it, cit); ASSERT_EQ(cit, it); @@ -227,10 +223,9 @@ TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { } TEST(intrusive_list_iteration, works_with_std_algorithms) { - kphp::stl::intrusive::list> l; - std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, - kphp::stl::intrusive::list_node{5}}; + vk::intrusive::list> l; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; for (auto& n : ns) { l.push_back(n); @@ -248,8 +243,8 @@ TEST(intrusive_list_iteration, works_with_std_algorithms) { } TEST(intrusive_list_insert, at_begin) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{2}, b{1}; + vk::intrusive::list> l; + vk::intrusive::list_node a{2}, b{1}; l.push_back(a); auto it = l.insert(l.begin(), b); @@ -259,8 +254,8 @@ TEST(intrusive_list_insert, at_begin) { } TEST(intrusive_list_insert, at_end) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.insert(l.end(), a); l.insert(l.end(), b); @@ -269,8 +264,8 @@ TEST(intrusive_list_insert, at_end) { } TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, c{3}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, c{3}, b{2}; l.push_back(a); l.push_back(c); @@ -282,8 +277,8 @@ TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { } TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -295,9 +290,9 @@ TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { } TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { - kphp::stl::intrusive::list> src; - kphp::stl::intrusive::list> dst; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> src; + vk::intrusive::list> dst; + vk::intrusive::list_node a{1}, b{2}, c{3}; src.push_back(a); src.push_back(b); @@ -310,8 +305,8 @@ TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { } TEST(intrusive_list_insert, moves_node_within_same_list) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -324,8 +319,8 @@ TEST(intrusive_list_insert, moves_node_within_same_list) { } TEST(intrusive_list_erase, single_returns_next) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -337,8 +332,8 @@ TEST(intrusive_list_erase, single_returns_next) { } TEST(intrusive_list_erase, front) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -349,8 +344,8 @@ TEST(intrusive_list_erase, front) { } TEST(intrusive_list_erase, end) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -361,10 +356,9 @@ TEST(intrusive_list_erase, end) { } TEST(intrusive_list_erase, range) { - kphp::stl::intrusive::list> l; - std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, - kphp::stl::intrusive::list_node{5}}; + vk::intrusive::list> l; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; for (auto& n : ns) { l.push_back(n); @@ -379,8 +373,8 @@ TEST(intrusive_list_erase, range) { } TEST(intrusive_list_erase, empty_range_is_noop) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -391,9 +385,8 @@ TEST(intrusive_list_erase, empty_range_is_noop) { } TEST(intrusive_list_erase, all_makes_empty) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_back(n); @@ -406,9 +399,9 @@ TEST(intrusive_list_erase, all_makes_empty) { } TEST(intrusive_list_erase, node_is_reusable_after_erase) { - kphp::stl::intrusive::list> l1; - kphp::stl::intrusive::list> l2; - kphp::stl::intrusive::list_node a{1}; + vk::intrusive::list> l1; + vk::intrusive::list> l2; + vk::intrusive::list_node a{1}; l1.push_back(a); l1.erase(l1.begin()); @@ -422,8 +415,8 @@ TEST(intrusive_list_erase, node_is_reusable_after_erase) { } TEST(intrusive_list_pop, pop_back) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -438,8 +431,8 @@ TEST(intrusive_list_pop, pop_back) { } TEST(intrusive_list_pop, pop_front) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -453,8 +446,8 @@ TEST(intrusive_list_pop, pop_front) { } TEST(intrusive_list_pop, pop_until_empty) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -465,9 +458,8 @@ TEST(intrusive_list_pop, pop_until_empty) { } TEST(intrusive_list_clear, makes_list_empty) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_back(n); @@ -480,15 +472,14 @@ TEST(intrusive_list_clear, makes_list_empty) { } TEST(intrusive_list_clear, list_is_reusable_after_clear) { - kphp::stl::intrusive::list> l; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { l.push_back(n); } l.clear(); - kphp::stl::intrusive::list_node x{10}, y{20}; + vk::intrusive::list_node x{10}, y{20}; l.push_back(x); l.push_back(y); @@ -496,10 +487,10 @@ TEST(intrusive_list_clear, list_is_reusable_after_clear) { } TEST(intrusive_list_swap, two_non_empty) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}; - kphp::stl::intrusive::list_node b1{3}, b2{4}, b3{5}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}, b3{5}; a.push_back(a1); a.push_back(a2); @@ -514,9 +505,9 @@ TEST(intrusive_list_swap, two_non_empty) { } TEST(intrusive_list_swap, empty_with_non_empty) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node b1{1}, b2{2}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node b1{1}, b2{2}; b.push_back(b1); b.push_back(b2); @@ -533,8 +524,8 @@ TEST(intrusive_list_swap, empty_with_non_empty) { } TEST(intrusive_list_swap, both_empty) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; + vk::intrusive::list> a; + vk::intrusive::list> b; a.swap(b); @@ -543,10 +534,10 @@ TEST(intrusive_list_swap, both_empty) { } TEST(intrusive_list_swap, free_function) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}; - kphp::stl::intrusive::list_node b1{2}, b2{3}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}; + vk::intrusive::list_node b1{2}, b2{3}; a.push_back(a1); b.push_back(b1); @@ -559,8 +550,8 @@ TEST(intrusive_list_swap, free_function) { } TEST(intrusive_list_swap, swap_with_self) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list> a; + vk::intrusive::list_node a1{1}, a2{2}; a.push_back(a1); a.push_back(a2); @@ -575,10 +566,10 @@ TEST(intrusive_list_swap, swap_with_self) { } TEST(intrusive_list_splice, whole_list_at_end) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; - kphp::stl::intrusive::list_node b1{4}, b2{5}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}, a3{3}; + vk::intrusive::list_node b1{4}, b2{5}; a.push_back(a1); a.push_back(a2); @@ -593,10 +584,10 @@ TEST(intrusive_list_splice, whole_list_at_end) { } TEST(intrusive_list_splice, whole_list_at_begin) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}; - kphp::stl::intrusive::list_node b1{3}, b2{4}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}; a.push_back(a1); a.push_back(a2); @@ -610,10 +601,10 @@ TEST(intrusive_list_splice, whole_list_at_begin) { } TEST(intrusive_list_splice, whole_list_in_middle) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}; - kphp::stl::intrusive::list_node b1{3}, b2{4}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}; a.push_back(a1); a.push_back(a2); @@ -627,9 +618,9 @@ TEST(intrusive_list_splice, whole_list_in_middle) { } TEST(intrusive_list_splice, single_element) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}, a3{3}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}, a3{3}; a.push_back(a1); a.push_back(a2); @@ -642,11 +633,10 @@ TEST(intrusive_list_splice, single_element) { } TEST(intrusive_list_splice, sub_range) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - std::array, 5> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}, kphp::stl::intrusive::list_node{4}, - kphp::stl::intrusive::list_node{5}}; + vk::intrusive::list> a; + vk::intrusive::list> b; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; for (auto& n : ns) { a.push_back(n); @@ -661,9 +651,9 @@ TEST(intrusive_list_splice, sub_range) { } TEST(intrusive_list_splice, empty_other_is_noop) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node b1{1}, b2{2}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node b1{1}, b2{2}; b.push_back(b1); b.push_back(b2); @@ -675,8 +665,8 @@ TEST(intrusive_list_splice, empty_other_is_noop) { } TEST(intrusive_list_splice, within_same_list_moves_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -689,8 +679,8 @@ TEST(intrusive_list_splice, within_same_list_moves_node) { } TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -706,9 +696,9 @@ TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { } TEST(intrusive_list_splice, rvalue_overloads) { - kphp::stl::intrusive::list> a; - kphp::stl::intrusive::list> b; - kphp::stl::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; a.push_back(a1); a.push_back(a2); @@ -730,27 +720,26 @@ TEST(intrusive_list_splice, rvalue_overloads) { } TEST(intrusive_list_move, move_construct_transfers_nodes) { - kphp::stl::intrusive::list> src; - std::array, 3> ns{kphp::stl::intrusive::list_node{1}, kphp::stl::intrusive::list_node{2}, - kphp::stl::intrusive::list_node{3}}; + vk::intrusive::list> src; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; for (auto& n : ns) { src.push_back(n); } - kphp::stl::intrusive::list> dst{std::move(src)}; + vk::intrusive::list> dst{std::move(src)}; ASSERT_EQ(values(dst), (std::vector{1, 2, 3})); ASSERT_TRUE(src.empty()); } TEST(intrusive_list_move, move_assign_into_empty) { - kphp::stl::intrusive::list> src; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> src; + vk::intrusive::list_node a{1}, b{2}; src.push_back(a); src.push_back(b); - kphp::stl::intrusive::list> dst; + vk::intrusive::list> dst; dst = std::move(src); @@ -759,13 +748,13 @@ TEST(intrusive_list_move, move_assign_into_empty) { } TEST(intrusive_list_move, move_assign) { - kphp::stl::intrusive::list> src; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + vk::intrusive::list> src; + vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; src.push_back(a); src.push_back(b); - kphp::stl::intrusive::list> dst; + vk::intrusive::list> dst; dst.push_back(c); dst.push_back(d); @@ -776,13 +765,13 @@ TEST(intrusive_list_move, move_assign) { } TEST(intrusive_list_move, move_linked_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); - kphp::stl::intrusive::list_node c{std::move(a)}; + vk::intrusive::list_node c{std::move(a)}; ASSERT_EQ(values(l), (std::vector{1, 2})); @@ -790,13 +779,13 @@ TEST(intrusive_list_move, move_linked_node) { } TEST(intrusive_list_move, move_assign_unlinked_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); - kphp::stl::intrusive::list_node c{0}; + vk::intrusive::list_node c{0}; c = std::move(a); ASSERT_EQ(values(l), (std::vector{1, 2})); @@ -804,9 +793,9 @@ TEST(intrusive_list_move, move_assign_unlinked_node) { } TEST(intrusive_list_move, move_assign_linked_node) { - kphp::stl::intrusive::list> l1; - kphp::stl::intrusive::list> l2; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l1; + vk::intrusive::list> l2; + vk::intrusive::list_node a{1}, b{2}, c{3}; l1.push_back(a); l1.push_back(b); @@ -822,8 +811,8 @@ TEST(intrusive_list_move, move_assign_linked_node) { } TEST(intrusive_list_move, self_move_assign_node_is_noop) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; l.push_back(a); @@ -835,8 +824,8 @@ TEST(intrusive_list_move, self_move_assign_node_is_noop) { } TEST(intrusive_list_move, self_move_assign_is_noop) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -848,9 +837,9 @@ TEST(intrusive_list_move, self_move_assign_is_noop) { } TEST(intrusive_list_value, stores_move_only_value_type) { - kphp::stl::intrusive::list>> l; - kphp::stl::intrusive::list_node> a{std::make_unique(1)}; - kphp::stl::intrusive::list_node> b{std::make_unique(2)}; + vk::intrusive::list>> l; + vk::intrusive::list_node> a{std::make_unique(1)}; + vk::intrusive::list_node> b{std::make_unique(2)}; l.push_back(a); l.push_back(b); @@ -865,9 +854,9 @@ TEST(intrusive_list_value, stores_move_only_value_type) { } TEST(intrusive_list_value, make_list_node_helper) { - auto n = kphp::stl::intrusive::make_list_node(123); + auto n = vk::intrusive::make_list_node(123); static_assert(std::is_same_v); - kphp::stl::intrusive::list> l; + vk::intrusive::list> l; l.push_back(n); @@ -875,8 +864,8 @@ TEST(intrusive_list_value, make_list_node_helper) { } TEST(intrusive_list_iterator_to, single_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{42}; + vk::intrusive::list> l; + vk::intrusive::list_node a{42}; l.push_back(a); @@ -887,8 +876,8 @@ TEST(intrusive_list_iterator_to, single_node) { } TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -903,8 +892,8 @@ TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { } TEST(intrusive_list_iterator_to, works_for_front_and_back) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}, c{3}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; l.push_back(a); l.push_back(b); @@ -915,8 +904,8 @@ TEST(intrusive_list_iterator_to, works_for_front_and_back) { } TEST(intrusive_list_iterator_to, const_overload) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); @@ -929,8 +918,8 @@ TEST(intrusive_list_iterator_to, const_overload) { } TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_via_node) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; l.push_back(a); @@ -941,13 +930,13 @@ TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_v } TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { - kphp::stl::intrusive::list> l; - kphp::stl::intrusive::list_node a{1}, b{2}; + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; l.push_back(a); l.push_back(b); - auto relocated = std::make_unique>(std::move(a)); + auto relocated = std::make_unique>(std::move(a)); auto it = l.iterator_to(*relocated); ASSERT_EQ(*it, 1); @@ -963,9 +952,9 @@ struct tag_b {}; } // namespace TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n1{1}, n2{2}, n3{3}; la.push_back(n1); la.push_back(n2); @@ -980,9 +969,9 @@ TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { } TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n1{1}, n2{2}, n3{3}; + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n1{1}, n2{2}, n3{3}; la.push_back(n1); la.push_back(n2); @@ -998,9 +987,9 @@ TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { } TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n{5}; + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; la.push_back(n); lb.push_back(n); @@ -1011,14 +1000,14 @@ TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { } TEST(intrusive_list_tags, move_linked_node_with_two_tags) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n{5}; + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; la.push_back(n); lb.push_back(n); - kphp::stl::intrusive::list_node new_n{std::move(n)}; + vk::intrusive::list_node new_n{std::move(n)}; ASSERT_EQ(values(la), (std::vector{5})); ASSERT_EQ(values(lb), (std::vector{5})); @@ -1027,10 +1016,10 @@ TEST(intrusive_list_tags, move_linked_node_with_two_tags) { } TEST(intrusive_list_tags, move_assign_linked_node_with_two_tags) { - kphp::stl::intrusive::list, tag_a> la; - kphp::stl::intrusive::list, tag_b> lb; - kphp::stl::intrusive::list_node n{5}; - kphp::stl::intrusive::list_node dst{7}; + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; + vk::intrusive::list_node dst{7}; la.push_back(n); lb.push_back(n); From 5c63a073575787ee02c1dadbe3ee8786cc21b5d6 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 14:42:39 +0300 Subject: [PATCH 25/35] move tests for intrusive list to common folder --- common/common-tests.cmake | 3 ++- common/common.cmake | 4 +++- runtime-common/core/core.cmake | 4 +--- tests/cpp/runtime/runtime-tests.cmake | 1 - 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/common-tests.cmake b/common/common-tests.cmake index df450ea94d..5fd0b42b23 100644 --- a/common/common-tests.cmake +++ b/common/common-tests.cmake @@ -17,7 +17,8 @@ prepend(COMMON_TESTS_SOURCES ${COMMON_DIR}/ type_traits/list_of_types_test.cpp wrappers/span-test.cpp wrappers/string_view-test.cpp - ucontext/ucontext-portable-test.cpp) + ucontext/ucontext-portable-test.cpp + containers/intrusive-list-test.cpp) allow_deprecated_declarations(${COMMON_TESTS_SOURCES}/algorithms/projections-test.cpp) diff --git a/common/common.cmake b/common/common.cmake index 5ea09ebc43..203878c15b 100644 --- a/common/common.cmake +++ b/common/common.cmake @@ -19,7 +19,9 @@ prepend(COMMON_MAIN_SOURCES ${COMMON_DIR}/ fast-backtrace.cpp string-processing.cpp - kphp-tasks-lease/lease-worker-mode.cpp) + kphp-tasks-lease/lease-worker-mode.cpp + + containers/intrusive-list.cpp) prepend(COMMON_KFS_SOURCES ${COMMON_DIR}/kfs/ kfs.cpp diff --git a/runtime-common/core/core.cmake b/runtime-common/core/core.cmake index bc26bd8c4c..3e42221b8f 100644 --- a/runtime-common/core/core.cmake +++ b/runtime-common/core/core.cmake @@ -7,6 +7,4 @@ prepend(CORE_MEMORY_RESOURCE core/memory-resource/ details/memory_chunk_tree.cpp details/memory_ordered_chunk_list.cpp monotonic_buffer_resource.cpp unsynchronized_pool_resource.cpp) -prepend(CORE_STD core/std/ intrusive-list.cpp) - -set(CORE_SRC ${CORE_UTILS} ${CORE_TYPES} ${CORE_MEMORY_RESOURCE} ${CORE_STD}) +set(CORE_SRC ${CORE_UTILS} ${CORE_TYPES} ${CORE_MEMORY_RESOURCE}) diff --git a/tests/cpp/runtime/runtime-tests.cmake b/tests/cpp/runtime/runtime-tests.cmake index 51d697605a..731da98fe6 100644 --- a/tests/cpp/runtime/runtime-tests.cmake +++ b/tests/cpp/runtime/runtime-tests.cmake @@ -20,7 +20,6 @@ prepend(RUNTIME_TESTS_SOURCES ${BASE_DIR}/tests/cpp/runtime/ memory_resource/extra-memory-pool-test.cpp memory_resource/unsynchronized_pool_resource-test.cpp string-list-test.cpp - intrusive-list-test.cpp string-test.cpp zstd-test.cpp) From 0688cfc718a23439a753b1b2c718ea50661f6cce Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 14:49:06 +0300 Subject: [PATCH 26/35] move tests for intrusive list to common folder --- common/containers/intrusive-list-test.cpp | 1033 +++++++++++++++++++++ 1 file changed, 1033 insertions(+) create mode 100644 common/containers/intrusive-list-test.cpp diff --git a/common/containers/intrusive-list-test.cpp b/common/containers/intrusive-list-test.cpp new file mode 100644 index 0000000000..6496a55539 --- /dev/null +++ b/common/containers/intrusive-list-test.cpp @@ -0,0 +1,1033 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "common/containers/intrusive-list.h" + +namespace { + +template +auto values(const List& l) -> std::vector { + std::vector v; + for (const auto& x : l) { + v.push_back(x); + } + + return v; +} + +template +auto reversed_values(List& l) -> std::vector { + std::vector v; + for (auto it = l.rbegin(); it != l.rend(); ++it) { + v.push_back(*it); + } + + return v; +} + +} // namespace + +TEST(intrusive_list_node_base, default_is_not_linked) { + vk::intrusive::details::list_node_base b; + + ASSERT_FALSE(b.is_linked()); +} + +TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { + vk::intrusive::details::list_node_base b; + + b.unlink(); + b.unlink(); + + ASSERT_FALSE(b.is_linked()); +} + +TEST(intrusive_list_basic, empty_list) { + vk::intrusive::list> l; + + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); + ASSERT_EQ(l.begin(), l.end()); + ASSERT_EQ(l.cbegin(), l.cend()); + ASSERT_EQ(l.rbegin(), l.rend()); + ASSERT_EQ(l.crbegin(), l.crend()); +} + +TEST(intrusive_list_basic, push_back_single) { + vk::intrusive::list> l; + vk::intrusive::list_node n{42}; + + l.push_back(n); + + ASSERT_FALSE(l.empty()); + ASSERT_EQ(l.size(), 1); + ASSERT_EQ(l.front(), 42); + ASSERT_EQ(l.back(), 42); + ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); +} + +TEST(intrusive_list_basic, push_back_keeps_order) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(l.size(), 3); + ASSERT_EQ(values(l), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_basic, push_front_single) { + vk::intrusive::list> l; + vk::intrusive::list_node n{42}; + + l.push_front(n); + + ASSERT_FALSE(l.empty()); + ASSERT_EQ(l.size(), 1); + ASSERT_EQ(l.front(), 42); + ASSERT_EQ(l.back(), 42); + ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); +} + +TEST(intrusive_list_basic, push_front_reverses_order) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_front(n); + } + + ASSERT_EQ(l.size(), 3); + ASSERT_EQ(values(l), (std::vector{3, 2, 1})); +} + +TEST(intrusive_list_basic, mixed_push_front_and_back) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + + l.push_back(b); // 2 + l.push_front(a); // 1 2 + l.push_back(c); // 1 2 3 + l.push_front(d); // 4 1 2 3 + + ASSERT_EQ(values(l), (std::vector{4, 1, 2, 3})); +} + +TEST(intrusive_list_basic, front_back_are_mutable_references) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + l.front() = 10; + l.back() = 20; + + ASSERT_EQ(values(l), (std::vector{10, 20})); + ASSERT_EQ(a.value(), 10); + ASSERT_EQ(b.value(), 20); +} + +TEST(intrusive_list_iteration, forward) { + vk::intrusive::list> l; + std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}}; + + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(values(l), (std::vector{1, 2, 3, 4})); +} + +TEST(intrusive_list_iteration, reverse) { + vk::intrusive::list> l; + std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}}; + + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(reversed_values(l), (std::vector{4, 3, 2, 1})); +} + +TEST(intrusive_list_iteration, bidirectional_increment_decrement) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_back(n); + } + auto it = l.begin(); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(*(++it), 2); + ASSERT_EQ(*(it++), 2); + ASSERT_EQ(*it, 3); + ASSERT_EQ(*(--it), 2); + ASSERT_EQ(*(it--), 2); + ASSERT_EQ(*it, 1); + + auto last = l.end(); + --last; + + ASSERT_EQ(*last, 3); +} + +TEST(intrusive_list_iteration, const_iteration) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{7}, vk::intrusive::list_node{8}, vk::intrusive::list_node{9}}; + + for (auto& n : ns) { + l.push_back(n); + } + const vk::intrusive::list>& cl = l; + + ASSERT_EQ(values(cl), (std::vector{7, 8, 9})); + + std::vector collected; + for (auto it = cl.cbegin(); it != cl.cend(); ++it) { + collected.push_back(*it); + } + + ASSERT_EQ(collected, (std::vector{7, 8, 9})); + + std::vector reversed_collected; + for (auto it = cl.crbegin(); it != cl.crend(); ++it) { + reversed_collected.push_back(*it); + } + + ASSERT_EQ(reversed_collected, (std::vector{9, 8, 7})); +} + +TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; + + l.push_back(a); + + vk::intrusive::list>::iterator it = l.begin(); + vk::intrusive::list>::const_iterator cit = it; // implicit non-const -> const conversion + + ASSERT_EQ(it, cit); + ASSERT_EQ(cit, it); + ASSERT_FALSE(it != cit); +} + +TEST(intrusive_list_iteration, works_with_std_algorithms) { + vk::intrusive::list> l; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; + + for (auto& n : ns) { + l.push_back(n); + } + + ASSERT_EQ(std::distance(l.begin(), l.end()), 5); + + auto found = std::find(l.begin(), l.end(), 3); + + ASSERT_NE(found, l.end()); + ASSERT_EQ(*found, 3); + + ASSERT_EQ(std::find(l.begin(), l.end(), 42), l.end()); + ASSERT_EQ(std::accumulate(l.begin(), l.end(), 0), 15); +} + +TEST(intrusive_list_insert, at_begin) { + vk::intrusive::list> l; + vk::intrusive::list_node a{2}, b{1}; + + l.push_back(a); + auto it = l.insert(l.begin(), b); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, at_end) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.insert(l.end(), a); + l.insert(l.end(), b); + + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, c{3}, b{2}; + + l.push_back(a); + l.push_back(c); + auto pos = std::next(l.begin()); // points at 3 + auto it = l.insert(pos, b); + + ASSERT_EQ(*it, 2); + ASSERT_EQ(values(l), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + // Inserting node 'a' right before itself must not corrupt the list. + auto it = l.insert(l.begin(), a); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { + vk::intrusive::list> src; + vk::intrusive::list> dst; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + src.push_back(a); + src.push_back(b); + src.push_back(c); + + // Moving 'b' into dst removes it from src (insert unlinks first). + dst.push_back(b); + ASSERT_EQ(values(src), (std::vector{1, 3})); + ASSERT_EQ(values(dst), (std::vector{2})); +} + +TEST(intrusive_list_insert, moves_node_within_same_list) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + // Re-insert front node before end -> moves it to the back. + l.insert(l.end(), a); + + ASSERT_EQ(values(l), (std::vector{2, 3, 1})); + ASSERT_EQ(l.size(), 3); +} + +TEST(intrusive_list_erase, single_returns_next) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + auto next = l.erase(std::next(l.begin())); // erase 2 + + ASSERT_EQ(*next, 3); + ASSERT_EQ(values(l), (std::vector{1, 3})); +} + +TEST(intrusive_list_erase, front) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + auto next = l.erase(l.begin()); + + ASSERT_EQ(*next, 2); + ASSERT_EQ(values(l), (std::vector{2})); +} + +TEST(intrusive_list_erase, end) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + auto next = l.erase(std::prev(l.end())); + + ASSERT_EQ(next, l.end()); + ASSERT_EQ(values(l), (std::vector{1})); +} + +TEST(intrusive_list_erase, range) { + vk::intrusive::list> l; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; + + for (auto& n : ns) { + l.push_back(n); + } + // erase [2, 4) -> removes 2 and 3 + auto first = std::next(l.begin()); + auto last = std::next(l.begin(), 3); + auto it = l.erase(first, last); + + ASSERT_EQ(*it, 4); + ASSERT_EQ(values(l), (std::vector{1, 4, 5})); +} + +TEST(intrusive_list_erase, empty_range_is_noop) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + auto it = l.erase(l.begin(), l.begin()); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_erase, all_makes_empty) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_back(n); + } + auto it = l.erase(l.begin(), l.end()); + + ASSERT_EQ(it, l.end()); + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); +} + +TEST(intrusive_list_erase, node_is_reusable_after_erase) { + vk::intrusive::list> l1; + vk::intrusive::list> l2; + vk::intrusive::list_node a{1}; + + l1.push_back(a); + l1.erase(l1.begin()); + + ASSERT_TRUE(l1.empty()); + + // The same node object can be linked into another list afterwards. + l2.push_back(a); + + ASSERT_EQ(values(l2), (std::vector{1})); +} + +TEST(intrusive_list_pop, pop_back) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + l.pop_back(); + + ASSERT_EQ(values(l), (std::vector{1, 2})); + + l.pop_back(); + + ASSERT_EQ(values(l), (std::vector{1})); +} + +TEST(intrusive_list_pop, pop_front) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + l.pop_front(); + + ASSERT_EQ(values(l), (std::vector{2, 3})); + l.pop_front(); + + ASSERT_EQ(values(l), (std::vector{3})); +} + +TEST(intrusive_list_pop, pop_until_empty) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + l.pop_front(); + l.pop_back(); + + ASSERT_TRUE(l.empty()); +} + +TEST(intrusive_list_clear, makes_list_empty) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_back(n); + } + l.clear(); + + ASSERT_TRUE(l.empty()); + ASSERT_EQ(l.size(), 0); + ASSERT_EQ(l.begin(), l.end()); +} + +TEST(intrusive_list_clear, list_is_reusable_after_clear) { + vk::intrusive::list> l; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + l.push_back(n); + } + l.clear(); + vk::intrusive::list_node x{10}, y{20}; + l.push_back(x); + l.push_back(y); + + ASSERT_EQ(values(l), (std::vector{10, 20})); +} + +TEST(intrusive_list_swap, two_non_empty) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}, b3{5}; + + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + b.push_back(b3); + + a.swap(b); + + ASSERT_EQ(values(a), (std::vector{3, 4, 5})); + ASSERT_EQ(values(b), (std::vector{1, 2})); +} + +TEST(intrusive_list_swap, empty_with_non_empty) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node b1{1}, b2{2}; + + b.push_back(b1); + b.push_back(b2); + + a.swap(b); + + ASSERT_EQ(values(a), (std::vector{1, 2})); + ASSERT_TRUE(b.empty()); + + a.swap(b); + + ASSERT_TRUE(a.empty()); + ASSERT_EQ(values(b), (std::vector{1, 2})); +} + +TEST(intrusive_list_swap, both_empty) { + vk::intrusive::list> a; + vk::intrusive::list> b; + + a.swap(b); + + ASSERT_TRUE(a.empty()); + ASSERT_TRUE(b.empty()); +} + +TEST(intrusive_list_swap, free_function) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}; + vk::intrusive::list_node b1{2}, b2{3}; + + a.push_back(a1); + b.push_back(b1); + b.push_back(b2); + + swap(a, b); + + ASSERT_EQ(values(a), (std::vector{2, 3})); + ASSERT_EQ(values(b), (std::vector{1})); +} + +TEST(intrusive_list_swap, swap_with_self) { + vk::intrusive::list> a; + vk::intrusive::list_node a1{1}, a2{2}; + + a.push_back(a1); + a.push_back(a2); + + a.swap(a); + + ASSERT_EQ(values(a), (std::vector{1, 2})); + + swap(a, a); + + ASSERT_EQ(values(a), (std::vector{1, 2})); +} + +TEST(intrusive_list_splice, whole_list_at_end) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}, a3{3}; + vk::intrusive::list_node b1{4}, b2{5}; + + a.push_back(a1); + a.push_back(a2); + a.push_back(a3); + b.push_back(b1); + b.push_back(b2); + + b.splice(b.end(), a); + + ASSERT_EQ(values(b), (std::vector{4, 5, 1, 2, 3})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, whole_list_at_begin) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}; + + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + + b.splice(b.begin(), a); + + ASSERT_EQ(values(b), (std::vector{1, 2, 3, 4})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, whole_list_in_middle) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + vk::intrusive::list_node b1{3}, b2{4}; + + a.push_back(a1); + a.push_back(a2); + b.push_back(b1); + b.push_back(b2); + + b.splice(std::next(b.begin()), a); // between 3 and 4 + + ASSERT_EQ(values(b), (std::vector{3, 1, 2, 4})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, single_element) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}, a3{3}; + + a.push_back(a1); + a.push_back(a2); + a.push_back(a3); + + b.splice(b.end(), a, std::next(a.begin())); // move `2` + + ASSERT_EQ(values(b), (std::vector{2})); + ASSERT_EQ(values(a), (std::vector{1, 3})); +} + +TEST(intrusive_list_splice, sub_range) { + vk::intrusive::list> a; + vk::intrusive::list> b; + std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, + vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; + + for (auto& n : ns) { + a.push_back(n); + } + // move [2, 4) -> nodes 2 and 3 + auto first = std::next(a.begin()); + auto last = std::next(a.begin(), 3); + b.splice(b.end(), a, first, last); + + ASSERT_EQ(values(b), (std::vector{2, 3})); + ASSERT_EQ(values(a), (std::vector{1, 4, 5})); +} + +TEST(intrusive_list_splice, empty_other_is_noop) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node b1{1}, b2{2}; + + b.push_back(b1); + b.push_back(b2); + + b.splice(b.begin(), a); + + ASSERT_EQ(values(b), (std::vector{1, 2})); + ASSERT_TRUE(a.empty()); +} + +TEST(intrusive_list_splice, within_same_list_moves_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + // move node `2` to the front + l.splice(l.begin(), l, std::next(l.begin())); + + ASSERT_EQ(values(l), (std::vector{2, 1, 3})); + ASSERT_EQ(l.size(), 3); +} + +TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + // splicing the first node to the position right after it (== its own place) is a noop + l.splice(std::next(l.begin()), l, l.begin()); + + ASSERT_EQ(values(l), (std::vector{1, 2})); + + // splicing the first node to the position right before it (== its own place) is a noop + l.splice(l.begin(), l, l.begin()); + + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_splice, rvalue_overloads) { + vk::intrusive::list> a; + vk::intrusive::list> b; + vk::intrusive::list_node a1{1}, a2{2}; + + a.push_back(a1); + a.push_back(a2); + + b.splice(b.end(), std::move(a)); + + ASSERT_EQ(values(b), (std::vector{1, 2})); + ASSERT_TRUE(a.empty()); + + a.splice(a.begin(), std::move(b), b.begin()); + + ASSERT_EQ(values(a), (std::vector{1})); + ASSERT_EQ(values(b), (std::vector{2})); + + a.splice(a.begin(), std::move(b), b.begin(), std::next(b.begin())); + + ASSERT_EQ(values(a), (std::vector{2, 1})); + ASSERT_TRUE(b.empty()); +} + +TEST(intrusive_list_move, move_construct_transfers_nodes) { + vk::intrusive::list> src; + std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; + + for (auto& n : ns) { + src.push_back(n); + } + vk::intrusive::list> dst{std::move(src)}; + + ASSERT_EQ(values(dst), (std::vector{1, 2, 3})); + ASSERT_TRUE(src.empty()); +} + +TEST(intrusive_list_move, move_assign_into_empty) { + vk::intrusive::list> src; + vk::intrusive::list_node a{1}, b{2}; + + src.push_back(a); + src.push_back(b); + + vk::intrusive::list> dst; + + dst = std::move(src); + + ASSERT_EQ(values(dst), (std::vector{1, 2})); + ASSERT_TRUE(src.empty()); +} + +TEST(intrusive_list_move, move_assign) { + vk::intrusive::list> src; + vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; + + src.push_back(a); + src.push_back(b); + + vk::intrusive::list> dst; + + dst.push_back(c); + dst.push_back(d); + dst = std::move(src); + + ASSERT_EQ(values(dst), (std::vector{1, 2})); + ASSERT_TRUE(src.empty()); +} + +TEST(intrusive_list_move, move_linked_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + + vk::intrusive::list_node c{std::move(a)}; + + ASSERT_EQ(values(l), (std::vector{1, 2})); + + ASSERT_EQ(std::addressof(c.value()), std::addressof(*l.begin())); +} + +TEST(intrusive_list_move, move_assign_unlinked_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + + vk::intrusive::list_node c{0}; + c = std::move(a); + + ASSERT_EQ(values(l), (std::vector{1, 2})); + ASSERT_EQ(std::addressof(l.front()), std::addressof(c.value())); +} + +TEST(intrusive_list_move, move_assign_linked_node) { + vk::intrusive::list> l1; + vk::intrusive::list> l2; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l1.push_back(a); + l1.push_back(b); + l2.push_back(c); + + // 'c' is already linked in l2; assigning into it must first unlink it from l2, + // then take over 'a's place in l1. + c = std::move(a); + + ASSERT_TRUE(l2.empty()); + ASSERT_EQ(values(l1), (std::vector{1, 2})); + ASSERT_EQ(std::addressof(l1.front()), std::addressof(c.value())); +} + +TEST(intrusive_list_move, self_move_assign_node_is_noop) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; + + l.push_back(a); + + auto* self = std::addressof(a); // to avoid compilation error [-Wself-move] + a = std::move(*self); + + ASSERT_EQ(values(l), (std::vector{1})); + ASSERT_EQ(std::addressof(l.front()), std::addressof(a.value())); +} + +TEST(intrusive_list_move, self_move_assign_is_noop) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + + auto* self = std::addressof(l); // to avoid compilation error [-Wself-move] + l = std::move(*self); + + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +TEST(intrusive_list_value, stores_move_only_value_type) { + vk::intrusive::list>> l; + vk::intrusive::list_node> a{std::make_unique(1)}; + vk::intrusive::list_node> b{std::make_unique(2)}; + + l.push_back(a); + l.push_back(b); + + auto it = l.begin(); + + ASSERT_EQ(**it, 1); + + ++it; + + ASSERT_EQ(**it, 2); +} + +TEST(intrusive_list_value, make_list_node_helper) { + auto n = vk::intrusive::make_list_node(123); + static_assert(std::is_same_v); + vk::intrusive::list> l; + + l.push_back(n); + + ASSERT_EQ(l.front(), 123); +} + +TEST(intrusive_list_iterator_to, single_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{42}; + + l.push_back(a); + + auto it = l.iterator_to(a); + + ASSERT_EQ(it, l.begin()); + ASSERT_EQ(*it, 42); +} + +TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + + auto it = l.iterator_to(b); + + ASSERT_EQ(*it, 2); + ASSERT_EQ(it, std::next(l.begin())); + ASSERT_EQ(*std::prev(it), 1); + ASSERT_EQ(*std::next(it), 3); +} + +TEST(intrusive_list_iterator_to, works_for_front_and_back) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}, c{3}; + + l.push_back(a); + l.push_back(b); + l.push_back(c); + + ASSERT_EQ(l.iterator_to(a), l.begin()); + ASSERT_EQ(l.iterator_to(c), std::prev(l.end())); +} + +TEST(intrusive_list_iterator_to, const_overload) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + const auto& cl = l; + + auto cit = cl.iterator_to(b); + + ASSERT_EQ(*cit, 2); + ASSERT_EQ(cit, std::next(cl.cbegin())); +} + +TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_via_node) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}; + + l.push_back(a); + + auto it = l.iterator_to(a); + *it = 100; + + ASSERT_EQ(a.value(), 100); +} + +TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { + vk::intrusive::list> l; + vk::intrusive::list_node a{1}, b{2}; + + l.push_back(a); + l.push_back(b); + + auto relocated = std::make_unique>(std::move(a)); + auto it = l.iterator_to(*relocated); + + ASSERT_EQ(*it, 1); + ASSERT_EQ(it, l.begin()); + ASSERT_EQ(values(l), (std::vector{1, 2})); +} + +namespace { + +struct tag_a {}; +struct tag_b {}; + +} // namespace + +TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + + lb.push_back(n3); + lb.push_back(n2); + lb.push_back(n1); + + ASSERT_EQ(values(la), (std::vector{1, 2, 3})); + ASSERT_EQ(values(lb), (std::vector{3, 2, 1})); +} + +TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n1{1}, n2{2}, n3{3}; + + la.push_back(n1); + la.push_back(n2); + la.push_back(n3); + lb.push_back(n1); + lb.push_back(n2); + lb.push_back(n3); + + la.erase(la.begin()); // remove n1 from `la` only + + ASSERT_EQ(values(la), (std::vector{2, 3})); + ASSERT_EQ(values(lb), (std::vector{1, 2, 3})); +} + +TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; + + la.push_back(n); + lb.push_back(n); + + la.front() = 99; + + ASSERT_EQ(lb.front(), 99); +} + +TEST(intrusive_list_tags, move_linked_node_with_two_tags) { + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; + + la.push_back(n); + lb.push_back(n); + + vk::intrusive::list_node new_n{std::move(n)}; + + ASSERT_EQ(values(la), (std::vector{5})); + ASSERT_EQ(values(lb), (std::vector{5})); + ASSERT_EQ(la.iterator_to(new_n), la.begin()); + ASSERT_EQ(lb.iterator_to(new_n), lb.begin()); +} + +TEST(intrusive_list_tags, move_assign_linked_node_with_two_tags) { + vk::intrusive::list, tag_a> la; + vk::intrusive::list, tag_b> lb; + vk::intrusive::list_node n{5}; + vk::intrusive::list_node dst{7}; + + la.push_back(n); + lb.push_back(n); + + dst = std::move(n); + + ASSERT_EQ(values(la), (std::vector{5})); + ASSERT_EQ(values(lb), (std::vector{5})); + ASSERT_EQ(la.iterator_to(dst), la.begin()); + ASSERT_EQ(lb.iterator_to(dst), lb.begin()); +} From 73a2210607ca733bf92b9b59606cefee5557ca6b Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 19:33:05 +0300 Subject: [PATCH 27/35] remove duplicate file --- tests/cpp/runtime/intrusive-list-test.cpp | 1033 --------------------- 1 file changed, 1033 deletions(-) delete mode 100644 tests/cpp/runtime/intrusive-list-test.cpp diff --git a/tests/cpp/runtime/intrusive-list-test.cpp b/tests/cpp/runtime/intrusive-list-test.cpp deleted file mode 100644 index 6496a55539..0000000000 --- a/tests/cpp/runtime/intrusive-list-test.cpp +++ /dev/null @@ -1,1033 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include - -#include "common/containers/intrusive-list.h" - -namespace { - -template -auto values(const List& l) -> std::vector { - std::vector v; - for (const auto& x : l) { - v.push_back(x); - } - - return v; -} - -template -auto reversed_values(List& l) -> std::vector { - std::vector v; - for (auto it = l.rbegin(); it != l.rend(); ++it) { - v.push_back(*it); - } - - return v; -} - -} // namespace - -TEST(intrusive_list_node_base, default_is_not_linked) { - vk::intrusive::details::list_node_base b; - - ASSERT_FALSE(b.is_linked()); -} - -TEST(intrusive_list_node_base, unlink_on_unlinked_is_safe) { - vk::intrusive::details::list_node_base b; - - b.unlink(); - b.unlink(); - - ASSERT_FALSE(b.is_linked()); -} - -TEST(intrusive_list_basic, empty_list) { - vk::intrusive::list> l; - - ASSERT_TRUE(l.empty()); - ASSERT_EQ(l.size(), 0); - ASSERT_EQ(l.begin(), l.end()); - ASSERT_EQ(l.cbegin(), l.cend()); - ASSERT_EQ(l.rbegin(), l.rend()); - ASSERT_EQ(l.crbegin(), l.crend()); -} - -TEST(intrusive_list_basic, push_back_single) { - vk::intrusive::list> l; - vk::intrusive::list_node n{42}; - - l.push_back(n); - - ASSERT_FALSE(l.empty()); - ASSERT_EQ(l.size(), 1); - ASSERT_EQ(l.front(), 42); - ASSERT_EQ(l.back(), 42); - ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); -} - -TEST(intrusive_list_basic, push_back_keeps_order) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_back(n); - } - - ASSERT_EQ(l.size(), 3); - ASSERT_EQ(values(l), (std::vector{1, 2, 3})); -} - -TEST(intrusive_list_basic, push_front_single) { - vk::intrusive::list> l; - vk::intrusive::list_node n{42}; - - l.push_front(n); - - ASSERT_FALSE(l.empty()); - ASSERT_EQ(l.size(), 1); - ASSERT_EQ(l.front(), 42); - ASSERT_EQ(l.back(), 42); - ASSERT_EQ(std::addressof(l.front()), std::addressof(l.back())); -} - -TEST(intrusive_list_basic, push_front_reverses_order) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_front(n); - } - - ASSERT_EQ(l.size(), 3); - ASSERT_EQ(values(l), (std::vector{3, 2, 1})); -} - -TEST(intrusive_list_basic, mixed_push_front_and_back) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; - - l.push_back(b); // 2 - l.push_front(a); // 1 2 - l.push_back(c); // 1 2 3 - l.push_front(d); // 4 1 2 3 - - ASSERT_EQ(values(l), (std::vector{4, 1, 2, 3})); -} - -TEST(intrusive_list_basic, front_back_are_mutable_references) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - l.front() = 10; - l.back() = 20; - - ASSERT_EQ(values(l), (std::vector{10, 20})); - ASSERT_EQ(a.value(), 10); - ASSERT_EQ(b.value(), 20); -} - -TEST(intrusive_list_iteration, forward) { - vk::intrusive::list> l; - std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, - vk::intrusive::list_node{4}}; - - for (auto& n : ns) { - l.push_back(n); - } - - ASSERT_EQ(values(l), (std::vector{1, 2, 3, 4})); -} - -TEST(intrusive_list_iteration, reverse) { - vk::intrusive::list> l; - std::array, 4> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, - vk::intrusive::list_node{4}}; - - for (auto& n : ns) { - l.push_back(n); - } - - ASSERT_EQ(reversed_values(l), (std::vector{4, 3, 2, 1})); -} - -TEST(intrusive_list_iteration, bidirectional_increment_decrement) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_back(n); - } - auto it = l.begin(); - - ASSERT_EQ(*it, 1); - ASSERT_EQ(*(++it), 2); - ASSERT_EQ(*(it++), 2); - ASSERT_EQ(*it, 3); - ASSERT_EQ(*(--it), 2); - ASSERT_EQ(*(it--), 2); - ASSERT_EQ(*it, 1); - - auto last = l.end(); - --last; - - ASSERT_EQ(*last, 3); -} - -TEST(intrusive_list_iteration, const_iteration) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{7}, vk::intrusive::list_node{8}, vk::intrusive::list_node{9}}; - - for (auto& n : ns) { - l.push_back(n); - } - const vk::intrusive::list>& cl = l; - - ASSERT_EQ(values(cl), (std::vector{7, 8, 9})); - - std::vector collected; - for (auto it = cl.cbegin(); it != cl.cend(); ++it) { - collected.push_back(*it); - } - - ASSERT_EQ(collected, (std::vector{7, 8, 9})); - - std::vector reversed_collected; - for (auto it = cl.crbegin(); it != cl.crend(); ++it) { - reversed_collected.push_back(*it); - } - - ASSERT_EQ(reversed_collected, (std::vector{9, 8, 7})); -} - -TEST(intrusive_list_iteration, iterator_converts_to_const_and_compares) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}; - - l.push_back(a); - - vk::intrusive::list>::iterator it = l.begin(); - vk::intrusive::list>::const_iterator cit = it; // implicit non-const -> const conversion - - ASSERT_EQ(it, cit); - ASSERT_EQ(cit, it); - ASSERT_FALSE(it != cit); -} - -TEST(intrusive_list_iteration, works_with_std_algorithms) { - vk::intrusive::list> l; - std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, - vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; - - for (auto& n : ns) { - l.push_back(n); - } - - ASSERT_EQ(std::distance(l.begin(), l.end()), 5); - - auto found = std::find(l.begin(), l.end(), 3); - - ASSERT_NE(found, l.end()); - ASSERT_EQ(*found, 3); - - ASSERT_EQ(std::find(l.begin(), l.end(), 42), l.end()); - ASSERT_EQ(std::accumulate(l.begin(), l.end(), 0), 15); -} - -TEST(intrusive_list_insert, at_begin) { - vk::intrusive::list> l; - vk::intrusive::list_node a{2}, b{1}; - - l.push_back(a); - auto it = l.insert(l.begin(), b); - - ASSERT_EQ(*it, 1); - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_insert, at_end) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.insert(l.end(), a); - l.insert(l.end(), b); - - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_insert, in_middle_returns_iterator_to_new_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, c{3}, b{2}; - - l.push_back(a); - l.push_back(c); - auto pos = std::next(l.begin()); // points at 3 - auto it = l.insert(pos, b); - - ASSERT_EQ(*it, 2); - ASSERT_EQ(values(l), (std::vector{1, 2, 3})); -} - -TEST(intrusive_list_insert, same_node_at_its_own_position_is_noop) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - // Inserting node 'a' right before itself must not corrupt the list. - auto it = l.insert(l.begin(), a); - - ASSERT_EQ(*it, 1); - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_insert, relinks_node_moving_it_from_another_list) { - vk::intrusive::list> src; - vk::intrusive::list> dst; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - src.push_back(a); - src.push_back(b); - src.push_back(c); - - // Moving 'b' into dst removes it from src (insert unlinks first). - dst.push_back(b); - ASSERT_EQ(values(src), (std::vector{1, 3})); - ASSERT_EQ(values(dst), (std::vector{2})); -} - -TEST(intrusive_list_insert, moves_node_within_same_list) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - // Re-insert front node before end -> moves it to the back. - l.insert(l.end(), a); - - ASSERT_EQ(values(l), (std::vector{2, 3, 1})); - ASSERT_EQ(l.size(), 3); -} - -TEST(intrusive_list_erase, single_returns_next) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - auto next = l.erase(std::next(l.begin())); // erase 2 - - ASSERT_EQ(*next, 3); - ASSERT_EQ(values(l), (std::vector{1, 3})); -} - -TEST(intrusive_list_erase, front) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - auto next = l.erase(l.begin()); - - ASSERT_EQ(*next, 2); - ASSERT_EQ(values(l), (std::vector{2})); -} - -TEST(intrusive_list_erase, end) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - auto next = l.erase(std::prev(l.end())); - - ASSERT_EQ(next, l.end()); - ASSERT_EQ(values(l), (std::vector{1})); -} - -TEST(intrusive_list_erase, range) { - vk::intrusive::list> l; - std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, - vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; - - for (auto& n : ns) { - l.push_back(n); - } - // erase [2, 4) -> removes 2 and 3 - auto first = std::next(l.begin()); - auto last = std::next(l.begin(), 3); - auto it = l.erase(first, last); - - ASSERT_EQ(*it, 4); - ASSERT_EQ(values(l), (std::vector{1, 4, 5})); -} - -TEST(intrusive_list_erase, empty_range_is_noop) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - auto it = l.erase(l.begin(), l.begin()); - - ASSERT_EQ(*it, 1); - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_erase, all_makes_empty) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_back(n); - } - auto it = l.erase(l.begin(), l.end()); - - ASSERT_EQ(it, l.end()); - ASSERT_TRUE(l.empty()); - ASSERT_EQ(l.size(), 0); -} - -TEST(intrusive_list_erase, node_is_reusable_after_erase) { - vk::intrusive::list> l1; - vk::intrusive::list> l2; - vk::intrusive::list_node a{1}; - - l1.push_back(a); - l1.erase(l1.begin()); - - ASSERT_TRUE(l1.empty()); - - // The same node object can be linked into another list afterwards. - l2.push_back(a); - - ASSERT_EQ(values(l2), (std::vector{1})); -} - -TEST(intrusive_list_pop, pop_back) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - l.pop_back(); - - ASSERT_EQ(values(l), (std::vector{1, 2})); - - l.pop_back(); - - ASSERT_EQ(values(l), (std::vector{1})); -} - -TEST(intrusive_list_pop, pop_front) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - l.pop_front(); - - ASSERT_EQ(values(l), (std::vector{2, 3})); - l.pop_front(); - - ASSERT_EQ(values(l), (std::vector{3})); -} - -TEST(intrusive_list_pop, pop_until_empty) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - l.pop_front(); - l.pop_back(); - - ASSERT_TRUE(l.empty()); -} - -TEST(intrusive_list_clear, makes_list_empty) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_back(n); - } - l.clear(); - - ASSERT_TRUE(l.empty()); - ASSERT_EQ(l.size(), 0); - ASSERT_EQ(l.begin(), l.end()); -} - -TEST(intrusive_list_clear, list_is_reusable_after_clear) { - vk::intrusive::list> l; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - l.push_back(n); - } - l.clear(); - vk::intrusive::list_node x{10}, y{20}; - l.push_back(x); - l.push_back(y); - - ASSERT_EQ(values(l), (std::vector{10, 20})); -} - -TEST(intrusive_list_swap, two_non_empty) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}; - vk::intrusive::list_node b1{3}, b2{4}, b3{5}; - - a.push_back(a1); - a.push_back(a2); - b.push_back(b1); - b.push_back(b2); - b.push_back(b3); - - a.swap(b); - - ASSERT_EQ(values(a), (std::vector{3, 4, 5})); - ASSERT_EQ(values(b), (std::vector{1, 2})); -} - -TEST(intrusive_list_swap, empty_with_non_empty) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node b1{1}, b2{2}; - - b.push_back(b1); - b.push_back(b2); - - a.swap(b); - - ASSERT_EQ(values(a), (std::vector{1, 2})); - ASSERT_TRUE(b.empty()); - - a.swap(b); - - ASSERT_TRUE(a.empty()); - ASSERT_EQ(values(b), (std::vector{1, 2})); -} - -TEST(intrusive_list_swap, both_empty) { - vk::intrusive::list> a; - vk::intrusive::list> b; - - a.swap(b); - - ASSERT_TRUE(a.empty()); - ASSERT_TRUE(b.empty()); -} - -TEST(intrusive_list_swap, free_function) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}; - vk::intrusive::list_node b1{2}, b2{3}; - - a.push_back(a1); - b.push_back(b1); - b.push_back(b2); - - swap(a, b); - - ASSERT_EQ(values(a), (std::vector{2, 3})); - ASSERT_EQ(values(b), (std::vector{1})); -} - -TEST(intrusive_list_swap, swap_with_self) { - vk::intrusive::list> a; - vk::intrusive::list_node a1{1}, a2{2}; - - a.push_back(a1); - a.push_back(a2); - - a.swap(a); - - ASSERT_EQ(values(a), (std::vector{1, 2})); - - swap(a, a); - - ASSERT_EQ(values(a), (std::vector{1, 2})); -} - -TEST(intrusive_list_splice, whole_list_at_end) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}, a3{3}; - vk::intrusive::list_node b1{4}, b2{5}; - - a.push_back(a1); - a.push_back(a2); - a.push_back(a3); - b.push_back(b1); - b.push_back(b2); - - b.splice(b.end(), a); - - ASSERT_EQ(values(b), (std::vector{4, 5, 1, 2, 3})); - ASSERT_TRUE(a.empty()); -} - -TEST(intrusive_list_splice, whole_list_at_begin) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}; - vk::intrusive::list_node b1{3}, b2{4}; - - a.push_back(a1); - a.push_back(a2); - b.push_back(b1); - b.push_back(b2); - - b.splice(b.begin(), a); - - ASSERT_EQ(values(b), (std::vector{1, 2, 3, 4})); - ASSERT_TRUE(a.empty()); -} - -TEST(intrusive_list_splice, whole_list_in_middle) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}; - vk::intrusive::list_node b1{3}, b2{4}; - - a.push_back(a1); - a.push_back(a2); - b.push_back(b1); - b.push_back(b2); - - b.splice(std::next(b.begin()), a); // between 3 and 4 - - ASSERT_EQ(values(b), (std::vector{3, 1, 2, 4})); - ASSERT_TRUE(a.empty()); -} - -TEST(intrusive_list_splice, single_element) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}, a3{3}; - - a.push_back(a1); - a.push_back(a2); - a.push_back(a3); - - b.splice(b.end(), a, std::next(a.begin())); // move `2` - - ASSERT_EQ(values(b), (std::vector{2})); - ASSERT_EQ(values(a), (std::vector{1, 3})); -} - -TEST(intrusive_list_splice, sub_range) { - vk::intrusive::list> a; - vk::intrusive::list> b; - std::array, 5> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}, - vk::intrusive::list_node{4}, vk::intrusive::list_node{5}}; - - for (auto& n : ns) { - a.push_back(n); - } - // move [2, 4) -> nodes 2 and 3 - auto first = std::next(a.begin()); - auto last = std::next(a.begin(), 3); - b.splice(b.end(), a, first, last); - - ASSERT_EQ(values(b), (std::vector{2, 3})); - ASSERT_EQ(values(a), (std::vector{1, 4, 5})); -} - -TEST(intrusive_list_splice, empty_other_is_noop) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node b1{1}, b2{2}; - - b.push_back(b1); - b.push_back(b2); - - b.splice(b.begin(), a); - - ASSERT_EQ(values(b), (std::vector{1, 2})); - ASSERT_TRUE(a.empty()); -} - -TEST(intrusive_list_splice, within_same_list_moves_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - // move node `2` to the front - l.splice(l.begin(), l, std::next(l.begin())); - - ASSERT_EQ(values(l), (std::vector{2, 1, 3})); - ASSERT_EQ(l.size(), 3); -} - -TEST(intrusive_list_splice, self_splice_to_same_position_is_noop) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - // splicing the first node to the position right after it (== its own place) is a noop - l.splice(std::next(l.begin()), l, l.begin()); - - ASSERT_EQ(values(l), (std::vector{1, 2})); - - // splicing the first node to the position right before it (== its own place) is a noop - l.splice(l.begin(), l, l.begin()); - - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_splice, rvalue_overloads) { - vk::intrusive::list> a; - vk::intrusive::list> b; - vk::intrusive::list_node a1{1}, a2{2}; - - a.push_back(a1); - a.push_back(a2); - - b.splice(b.end(), std::move(a)); - - ASSERT_EQ(values(b), (std::vector{1, 2})); - ASSERT_TRUE(a.empty()); - - a.splice(a.begin(), std::move(b), b.begin()); - - ASSERT_EQ(values(a), (std::vector{1})); - ASSERT_EQ(values(b), (std::vector{2})); - - a.splice(a.begin(), std::move(b), b.begin(), std::next(b.begin())); - - ASSERT_EQ(values(a), (std::vector{2, 1})); - ASSERT_TRUE(b.empty()); -} - -TEST(intrusive_list_move, move_construct_transfers_nodes) { - vk::intrusive::list> src; - std::array, 3> ns{vk::intrusive::list_node{1}, vk::intrusive::list_node{2}, vk::intrusive::list_node{3}}; - - for (auto& n : ns) { - src.push_back(n); - } - vk::intrusive::list> dst{std::move(src)}; - - ASSERT_EQ(values(dst), (std::vector{1, 2, 3})); - ASSERT_TRUE(src.empty()); -} - -TEST(intrusive_list_move, move_assign_into_empty) { - vk::intrusive::list> src; - vk::intrusive::list_node a{1}, b{2}; - - src.push_back(a); - src.push_back(b); - - vk::intrusive::list> dst; - - dst = std::move(src); - - ASSERT_EQ(values(dst), (std::vector{1, 2})); - ASSERT_TRUE(src.empty()); -} - -TEST(intrusive_list_move, move_assign) { - vk::intrusive::list> src; - vk::intrusive::list_node a{1}, b{2}, c{3}, d{4}; - - src.push_back(a); - src.push_back(b); - - vk::intrusive::list> dst; - - dst.push_back(c); - dst.push_back(d); - dst = std::move(src); - - ASSERT_EQ(values(dst), (std::vector{1, 2})); - ASSERT_TRUE(src.empty()); -} - -TEST(intrusive_list_move, move_linked_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - - vk::intrusive::list_node c{std::move(a)}; - - ASSERT_EQ(values(l), (std::vector{1, 2})); - - ASSERT_EQ(std::addressof(c.value()), std::addressof(*l.begin())); -} - -TEST(intrusive_list_move, move_assign_unlinked_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - - vk::intrusive::list_node c{0}; - c = std::move(a); - - ASSERT_EQ(values(l), (std::vector{1, 2})); - ASSERT_EQ(std::addressof(l.front()), std::addressof(c.value())); -} - -TEST(intrusive_list_move, move_assign_linked_node) { - vk::intrusive::list> l1; - vk::intrusive::list> l2; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l1.push_back(a); - l1.push_back(b); - l2.push_back(c); - - // 'c' is already linked in l2; assigning into it must first unlink it from l2, - // then take over 'a's place in l1. - c = std::move(a); - - ASSERT_TRUE(l2.empty()); - ASSERT_EQ(values(l1), (std::vector{1, 2})); - ASSERT_EQ(std::addressof(l1.front()), std::addressof(c.value())); -} - -TEST(intrusive_list_move, self_move_assign_node_is_noop) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}; - - l.push_back(a); - - auto* self = std::addressof(a); // to avoid compilation error [-Wself-move] - a = std::move(*self); - - ASSERT_EQ(values(l), (std::vector{1})); - ASSERT_EQ(std::addressof(l.front()), std::addressof(a.value())); -} - -TEST(intrusive_list_move, self_move_assign_is_noop) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - - auto* self = std::addressof(l); // to avoid compilation error [-Wself-move] - l = std::move(*self); - - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -TEST(intrusive_list_value, stores_move_only_value_type) { - vk::intrusive::list>> l; - vk::intrusive::list_node> a{std::make_unique(1)}; - vk::intrusive::list_node> b{std::make_unique(2)}; - - l.push_back(a); - l.push_back(b); - - auto it = l.begin(); - - ASSERT_EQ(**it, 1); - - ++it; - - ASSERT_EQ(**it, 2); -} - -TEST(intrusive_list_value, make_list_node_helper) { - auto n = vk::intrusive::make_list_node(123); - static_assert(std::is_same_v); - vk::intrusive::list> l; - - l.push_back(n); - - ASSERT_EQ(l.front(), 123); -} - -TEST(intrusive_list_iterator_to, single_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{42}; - - l.push_back(a); - - auto it = l.iterator_to(a); - - ASSERT_EQ(it, l.begin()); - ASSERT_EQ(*it, 42); -} - -TEST(intrusive_list_iterator_to, points_at_correct_position_in_middle) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - - auto it = l.iterator_to(b); - - ASSERT_EQ(*it, 2); - ASSERT_EQ(it, std::next(l.begin())); - ASSERT_EQ(*std::prev(it), 1); - ASSERT_EQ(*std::next(it), 3); -} - -TEST(intrusive_list_iterator_to, works_for_front_and_back) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}, c{3}; - - l.push_back(a); - l.push_back(b); - l.push_back(c); - - ASSERT_EQ(l.iterator_to(a), l.begin()); - ASSERT_EQ(l.iterator_to(c), std::prev(l.end())); -} - -TEST(intrusive_list_iterator_to, const_overload) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - const auto& cl = l; - - auto cit = cl.iterator_to(b); - - ASSERT_EQ(*cit, 2); - ASSERT_EQ(cit, std::next(cl.cbegin())); -} - -TEST(intrusive_list_iterator_to, mutating_through_returned_iterator_is_visible_via_node) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}; - - l.push_back(a); - - auto it = l.iterator_to(a); - *it = 100; - - ASSERT_EQ(a.value(), 100); -} - -TEST(intrusive_list_iterator_to, remains_valid_after_owning_node_is_relocated) { - vk::intrusive::list> l; - vk::intrusive::list_node a{1}, b{2}; - - l.push_back(a); - l.push_back(b); - - auto relocated = std::make_unique>(std::move(a)); - auto it = l.iterator_to(*relocated); - - ASSERT_EQ(*it, 1); - ASSERT_EQ(it, l.begin()); - ASSERT_EQ(values(l), (std::vector{1, 2})); -} - -namespace { - -struct tag_a {}; -struct tag_b {}; - -} // namespace - -TEST(intrusive_list_tags, node_lives_in_two_lists_at_once) { - vk::intrusive::list, tag_a> la; - vk::intrusive::list, tag_b> lb; - vk::intrusive::list_node n1{1}, n2{2}, n3{3}; - - la.push_back(n1); - la.push_back(n2); - la.push_back(n3); - - lb.push_back(n3); - lb.push_back(n2); - lb.push_back(n1); - - ASSERT_EQ(values(la), (std::vector{1, 2, 3})); - ASSERT_EQ(values(lb), (std::vector{3, 2, 1})); -} - -TEST(intrusive_list_tags, erase_from_one_list_keeps_the_other) { - vk::intrusive::list, tag_a> la; - vk::intrusive::list, tag_b> lb; - vk::intrusive::list_node n1{1}, n2{2}, n3{3}; - - la.push_back(n1); - la.push_back(n2); - la.push_back(n3); - lb.push_back(n1); - lb.push_back(n2); - lb.push_back(n3); - - la.erase(la.begin()); // remove n1 from `la` only - - ASSERT_EQ(values(la), (std::vector{2, 3})); - ASSERT_EQ(values(lb), (std::vector{1, 2, 3})); -} - -TEST(intrusive_list_tags, mutation_through_one_tag_visible_via_other) { - vk::intrusive::list, tag_a> la; - vk::intrusive::list, tag_b> lb; - vk::intrusive::list_node n{5}; - - la.push_back(n); - lb.push_back(n); - - la.front() = 99; - - ASSERT_EQ(lb.front(), 99); -} - -TEST(intrusive_list_tags, move_linked_node_with_two_tags) { - vk::intrusive::list, tag_a> la; - vk::intrusive::list, tag_b> lb; - vk::intrusive::list_node n{5}; - - la.push_back(n); - lb.push_back(n); - - vk::intrusive::list_node new_n{std::move(n)}; - - ASSERT_EQ(values(la), (std::vector{5})); - ASSERT_EQ(values(lb), (std::vector{5})); - ASSERT_EQ(la.iterator_to(new_n), la.begin()); - ASSERT_EQ(lb.iterator_to(new_n), lb.begin()); -} - -TEST(intrusive_list_tags, move_assign_linked_node_with_two_tags) { - vk::intrusive::list, tag_a> la; - vk::intrusive::list, tag_b> lb; - vk::intrusive::list_node n{5}; - vk::intrusive::list_node dst{7}; - - la.push_back(n); - lb.push_back(n); - - dst = std::move(n); - - ASSERT_EQ(values(la), (std::vector{5})); - ASSERT_EQ(values(lb), (std::vector{5})); - ASSERT_EQ(la.iterator_to(dst), la.begin()); - ASSERT_EQ(lb.iterator_to(dst), lb.begin()); -} From a90f1b0fc0c9349c95c82bf31f764c484b6596f7 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 19:34:02 +0300 Subject: [PATCH 28/35] small fixes --- common/containers/intrusive-list.h | 2 ++ common/type_traits/apply_tuple.h | 4 ++-- runtime-common/core/std/containers.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/common/containers/intrusive-list.h b/common/containers/intrusive-list.h index 95788eaf19..6abd722620 100644 --- a/common/containers/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -348,6 +348,7 @@ class list { return !m_sentinel.is_linked(); } + // complexity O(n) auto size() const noexcept -> size_type { return std::distance(begin(), end()); } @@ -356,6 +357,7 @@ class list { m_sentinel.unlink(); } + // if the node is linked in other list with the same tag, it will be unlinked from other list before insertion auto insert(const_iterator pos, Node& node) noexcept -> iterator { auto* next_node = pos.m_curr; auto* new_node = list_node_base_from_list_node(node); diff --git a/common/type_traits/apply_tuple.h b/common/type_traits/apply_tuple.h index 2570d6586c..96d97206a6 100644 --- a/common/type_traits/apply_tuple.h +++ b/common/type_traits/apply_tuple.h @@ -13,10 +13,10 @@ struct apply_tuple; template typename Template, typename... Args> struct apply_tuple> { - using Type = Template; + using type = Template; }; template typename Template, typename Tuple> -using apply_tuple_t = typename apply_tuple::Type; +using apply_tuple_t = typename apply_tuple::type; } // namespace vk diff --git a/runtime-common/core/std/containers.h b/runtime-common/core/std/containers.h index 4538e5ec0f..abe4981014 100644 --- a/runtime-common/core/std/containers.h +++ b/runtime-common/core/std/containers.h @@ -56,6 +56,8 @@ namespace intrusive { template using list = vk::intrusive::list; +using vk::intrusive::make_list_node; + } // namespace intrusive } // namespace stl From 09c54a860f5d31e03229ce9eb5c950b7829d6aaf Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 19:35:46 +0300 Subject: [PATCH 29/35] update comment for insert --- common/containers/intrusive-list.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/containers/intrusive-list.h b/common/containers/intrusive-list.h index 6abd722620..3dc4c088b2 100644 --- a/common/containers/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -357,7 +357,7 @@ class list { m_sentinel.unlink(); } - // if the node is linked in other list with the same tag, it will be unlinked from other list before insertion + // if the node is linked in list with the same tag, it will be unlinked from list before insertion auto insert(const_iterator pos, Node& node) noexcept -> iterator { auto* next_node = pos.m_curr; auto* new_node = list_node_base_from_list_node(node); From 2484cb37d79ff678c7e0add4404ce5c5c18fbb62 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 19:49:22 +0300 Subject: [PATCH 30/35] make intrusive list header only --- common/common.cmake | 4 +-- common/containers/intrusive-list.cpp | 47 ---------------------------- common/containers/intrusive-list.h | 39 +++++++++++++++++++---- 3 files changed, 34 insertions(+), 56 deletions(-) delete mode 100644 common/containers/intrusive-list.cpp diff --git a/common/common.cmake b/common/common.cmake index 203878c15b..5ea09ebc43 100644 --- a/common/common.cmake +++ b/common/common.cmake @@ -19,9 +19,7 @@ prepend(COMMON_MAIN_SOURCES ${COMMON_DIR}/ fast-backtrace.cpp string-processing.cpp - kphp-tasks-lease/lease-worker-mode.cpp - - containers/intrusive-list.cpp) + kphp-tasks-lease/lease-worker-mode.cpp) prepend(COMMON_KFS_SOURCES ${COMMON_DIR}/kfs/ kfs.cpp diff --git a/common/containers/intrusive-list.cpp b/common/containers/intrusive-list.cpp deleted file mode 100644 index a791d79d9a..0000000000 --- a/common/containers/intrusive-list.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "intrusive-list.h" - -#include -#include - -namespace vk::intrusive::details { - -auto list_node_base::take_place_of(list_node_base&& other) noexcept -> void { - if (other.is_linked()) { - m_prev = std::exchange(other.m_prev, std::addressof(other)); - m_next = std::exchange(other.m_next, std::addressof(other)); - - m_prev->m_next = this; - m_next->m_prev = this; - } -} - -list_node_base::list_node_base(list_node_base&& other) noexcept { - take_place_of(std::move(other)); -} - -auto list_node_base::operator=(list_node_base&& other) noexcept -> list_node_base& { - if (this != std::addressof(other)) { - unlink(); - take_place_of(std::move(other)); - } - - return *this; -} - -list_node_base::~list_node_base() { - unlink(); -} - -auto list_node_base::is_linked() const noexcept -> bool { - return m_prev != this; -} - -auto list_node_base::unlink() noexcept -> void { - m_prev->m_next = m_next; - m_next->m_prev = m_prev; - - m_prev = this; - m_next = this; -} - -} // namespace vk::intrusive::details diff --git a/common/containers/intrusive-list.h b/common/containers/intrusive-list.h index 3dc4c088b2..5fe44a2064 100644 --- a/common/containers/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -27,7 +27,15 @@ class list_node_base { list_node_base* m_prev{this}; list_node_base* m_next{this}; - auto take_place_of(list_node_base&& other) noexcept -> void; + auto take_place_of(list_node_base&& other) noexcept -> void { + if (other.is_linked()) { + m_prev = std::exchange(other.m_prev, std::addressof(other)); + m_next = std::exchange(other.m_next, std::addressof(other)); + + m_prev->m_next = this; + m_next->m_prev = this; + } + } template friend class vk::intrusive::list; @@ -40,17 +48,36 @@ class list_node_base { list_node_base(const list_node_base& /*unused*/) noexcept = delete; - list_node_base(list_node_base&& other) noexcept; + list_node_base(list_node_base&& other) noexcept { + take_place_of(std::move(other)); + } auto operator=(const list_node_base& /*unused*/) noexcept -> list_node_base& = delete; - auto operator=(list_node_base&& other) noexcept -> list_node_base&; + auto operator=(list_node_base&& other) noexcept -> list_node_base& { + if (this != std::addressof(other)) { + unlink(); + take_place_of(std::move(other)); + } + + return *this; + } + + ~list_node_base() { + unlink(); + } - ~list_node_base(); + auto is_linked() const noexcept -> bool { + return m_prev != this; + } - auto is_linked() const noexcept -> bool; + auto unlink() noexcept -> void { + m_prev->m_next = m_next; + m_next->m_prev = m_prev; - auto unlink() noexcept -> void; + m_prev = this; + m_next = this; + } }; template From ac7145ef73b7e72a907cac9b875e15e0d7b9942f Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Wed, 29 Jul 2026 19:56:00 +0300 Subject: [PATCH 31/35] add using for list_node --- runtime-common/core/std/containers.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runtime-common/core/std/containers.h b/runtime-common/core/std/containers.h index abe4981014..8ef0af7b96 100644 --- a/runtime-common/core/std/containers.h +++ b/runtime-common/core/std/containers.h @@ -53,6 +53,9 @@ using string = std::basic_string, Allocator>; namespace intrusive { +template +using list_node = vk::intrusive::list_node; + template using list = vk::intrusive::list; From 88816ccb044ac71ebb71a8ada2ceb5da4ed2320c Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 30 Jul 2026 17:26:11 +0300 Subject: [PATCH 32/35] add operator-> to list_iterator --- common/containers/intrusive-list.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/containers/intrusive-list.h b/common/containers/intrusive-list.h index 5fe44a2064..608e5c79d4 100644 --- a/common/containers/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -233,6 +233,10 @@ class list_iterator { return value_from_list_node_base(m_curr); } + auto operator->() const noexcept -> pointer { + return std::addressof(value_from_list_node_base(m_curr)); + } + template, std::remove_const_t>>> auto operator==(const list_iterator& other) const noexcept -> bool { return m_curr == other.m_curr; From 23ab0b7baf1d0aa32274b5832cc7c4422594aa9c Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 30 Jul 2026 19:19:19 +0300 Subject: [PATCH 33/35] fix using for list --- runtime-common/core/std/containers.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime-common/core/std/containers.h b/runtime-common/core/std/containers.h index 8ef0af7b96..2327126ce6 100644 --- a/runtime-common/core/std/containers.h +++ b/runtime-common/core/std/containers.h @@ -56,8 +56,8 @@ namespace intrusive { template using list_node = vk::intrusive::list_node; -template -using list = vk::intrusive::list; +template +using list = vk::intrusive::list; using vk::intrusive::make_list_node; From fc4fdcd10f3f0825de40e5902a345707d7eed257 Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 30 Jul 2026 19:20:48 +0300 Subject: [PATCH 34/35] fix using for list --- runtime-common/core/std/containers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-common/core/std/containers.h b/runtime-common/core/std/containers.h index 2327126ce6..f8d0ec549c 100644 --- a/runtime-common/core/std/containers.h +++ b/runtime-common/core/std/containers.h @@ -56,7 +56,7 @@ namespace intrusive { template using list_node = vk::intrusive::list_node; -template +template using list = vk::intrusive::list; using vk::intrusive::make_list_node; From ff04ab7e2d8495e291a01ab230256c8537f77a9e Mon Sep 17 00:00:00 2001 From: Kirill Kotliar Date: Thu, 30 Jul 2026 23:10:56 +0300 Subject: [PATCH 35/35] apply code style --- common/containers/intrusive-list.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/common/containers/intrusive-list.h b/common/containers/intrusive-list.h index 608e5c79d4..eef7e420b7 100644 --- a/common/containers/intrusive-list.h +++ b/common/containers/intrusive-list.h @@ -180,7 +180,7 @@ class list_iterator { * error: ‘this’ pointer is null [-Werror=nonnull] * 150 | return (static_cast(static_cast(static_cast*>(node))))->value(); */ - auto* list_node = static_cast(static_cast(static_cast*>(node))); + auto* list_node{static_cast(static_cast(static_cast*>(node)))}; return list_node->value(); } @@ -213,7 +213,7 @@ class list_iterator { } auto operator++(int) noexcept -> list_iterator { - list_iterator res = *this; + list_iterator res{*this}; ++*this; return res; } @@ -224,7 +224,7 @@ class list_iterator { } auto operator--(int) noexcept -> list_iterator { - list_iterator res = *this; + list_iterator res{*this}; --*this; return res; } @@ -284,9 +284,9 @@ class list { return; } - auto* pos_node = pos.m_curr; - auto* first_node = first.m_curr; - auto* last_node = last.m_curr->m_prev; + auto* pos_node{pos.m_curr}; + auto* first_node{first.m_curr}; + auto* last_node{last.m_curr->m_prev}; first_node->m_prev->m_next = last_node->m_next; last_node->m_next->m_prev = first_node->m_prev; @@ -390,8 +390,8 @@ class list { // if the node is linked in list with the same tag, it will be unlinked from list before insertion auto insert(const_iterator pos, Node& node) noexcept -> iterator { - auto* next_node = pos.m_curr; - auto* new_node = list_node_base_from_list_node(node); + auto* next_node{pos.m_curr}; + auto* new_node{list_node_base_from_list_node(node)}; if (new_node == next_node) { return iterator{next_node}; } @@ -408,8 +408,8 @@ class list { } auto erase(const_iterator pos) noexcept -> iterator { - auto* remove_node = pos.m_curr; - auto* next_node = remove_node->m_next; + auto* remove_node{pos.m_curr}; + auto* next_node{remove_node->m_next}; remove_node->unlink();