From bdeffa75fdf96fd4494850a0b1bf35cb46e7bd53 Mon Sep 17 00:00:00 2001 From: Suyash Bhattarai Date: Tue, 13 Oct 2026 19:54:23 +0545 Subject: [PATCH] Fix equality for identifier sets of different sizes Compare the complete underlying sets so a matching prefix is not considered equal and comparison cannot overrun a shorter right-hand set. Add exhaustive signed and unsigned identifier-set regression tests, run in release and checked-iterator builds in CI. --- .github/workflows/check-files.yml | 14 +++++++ include/cpp_common/identifiers.hpp | 2 +- tools/testers/identifiers_test.cpp | 62 ++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 tools/testers/identifiers_test.cpp diff --git a/.github/workflows/check-files.yml b/.github/workflows/check-files.yml index 89ad6242c09..040e9801881 100644 --- a/.github/workflows/check-files.yml +++ b/.github/workflows/check-files.yml @@ -75,3 +75,17 @@ jobs: run: | ls code_linter bash ./tools/scripts/code_checker.sh + + cpp-header-tests: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Identifier equality in release and checked-iterator builds + run: | + c++ -std=c++17 -O2 -DNDEBUG -Wall -Wextra -Werror -Iinclude \ + tools/testers/identifiers_test.cpp -o /tmp/identifiers_test + /tmp/identifiers_test + c++ -std=c++17 -O0 -D_GLIBCXX_DEBUG -Wall -Wextra -Werror -Iinclude \ + tools/testers/identifiers_test.cpp -o /tmp/identifiers_test_debug + /tmp/identifiers_test_debug diff --git a/include/cpp_common/identifiers.hpp b/include/cpp_common/identifiers.hpp index 0194110e61f..f07ce537763 100644 --- a/include/cpp_common/identifiers.hpp +++ b/include/cpp_common/identifiers.hpp @@ -109,7 +109,7 @@ class Identifiers { * @param [in] rhs set of identifiers to be compared */ bool operator==(const Identifiers &rhs) const { - return std::equal(m_ids.begin(), m_ids.end(), rhs.m_ids.begin()); + return m_ids == rhs.m_ids; } //! @name set UNION diff --git a/tools/testers/identifiers_test.cpp b/tools/testers/identifiers_test.cpp new file mode 100644 index 00000000000..0caa96cc09c --- /dev/null +++ b/tools/testers/identifiers_test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2026 Suyash Bhattarai + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "cpp_common/identifiers.hpp" + +namespace { + +template +pgrouting::Identifiers identifiers_from_mask( + const std::array& values, std::size_t mask) { + pgrouting::Identifiers result; + for (std::size_t bit = 0; bit < N; ++bit) { + if (mask & (std::size_t{1} << bit)) result += values[bit]; + } + return result; +} + +template +bool check_equality(const std::array& values) { + const auto subsets = std::size_t{1} << N; + for (std::size_t left_mask = 0; left_mask < subsets; ++left_mask) { + const auto left = identifiers_from_mask(values, left_mask); + for (std::size_t right_mask = 0; right_mask < subsets; ++right_mask) { + const auto right = identifiers_from_mask(values, right_mask); + // Each distinct bit mask represents a distinct set of identifiers. + if ((left == right) != (left_mask == right_mask)) { + std::cerr << "Incorrect equality for " << left << " and " << right << '\n'; + return false; + } + } + } + return true; +} + +} // namespace + +int main() { + const std::array signed_values{ + -7, 0, 3, 42, (std::numeric_limits::max)()}; + const std::array unsigned_values{ + 0, 3, 42, 1000, (std::numeric_limits::max)()}; + if (!check_equality(signed_values) || !check_equality(unsigned_values)) return 1; + std::cout << "All 2048 identifier-set comparisons passed.\n"; + return 0; +}