Skip to content

Commit 133919e

Browse files
authored
Merge pull request #60 from whimxiqal/bug/overlap-overflow
Guard against overflow for dynamic interval overlap
2 parents dab26b2 + 002865e commit 133919e

5 files changed

Lines changed: 136 additions & 34 deletions

File tree

.github/workflows/build_and_test.yml

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
run: sudo apt-get update
4343

4444
- name: Do install
45-
run: sudo apt-get install -y ${{ matrix.compiler.pkg }} cmake ninja-build libgtest-dev libgmock-dev
45+
run: sudo apt-get install -y ${{ matrix.compiler.pkg }} cmake ninja-build
4646

4747
- name: Checkout
4848
uses: actions/checkout@v2
@@ -64,24 +64,15 @@ jobs:
6464
uses: actions/checkout@v2
6565

6666
- name: Setup MSVC
67-
uses: microsoft/setup-msbuild@v1.0.2
68-
69-
- name: Install vcpkg and gtest
70-
run: |
71-
git clone https://github.com/microsoft/vcpkg.git
72-
.\vcpkg\bootstrap-vcpkg.bat
73-
.\vcpkg\vcpkg.exe install gtest
74-
shell: pwsh
75-
env:
76-
VCPKG_DEFAULT_TRIPLET: x64-windows
67+
uses: ilammy/msvc-dev-cmd@v1
7768

7869
- name: CMake Configure
79-
run: cmake -B ${{github.workspace}}/build -G"Visual Studio 17 2022" -A x64 -DCMAKE_TOOLCHAIN_FILE=${{github.workspace}}/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_CXX_STANDARD=20 -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
70+
run: cmake -B ${{github.workspace}}/build -G"Ninja" -DCMAKE_CXX_STANDARD=20 -DINT_TREE_USE_OPTIONAL_POLYFILL=on -DINT_TREE_BUILD_EXAMPLES=on -DINT_TREE_ENABLE_TESTS=on -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
8071

8172
- name: Build
8273
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
8374

8475
- name: Test
8576
working-directory: ${{github.workspace}}/build
86-
run: .\tests\Release\tree-tests.exe
87-
shell: cmd
77+
run: .\tests\tree-tests.exe
78+
shell: cmd

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,16 @@ int main()
5656
```
5757

5858
## Compile & Run Testing
59-
Having googletest (find here on github) installed / built is a requirement to run the tests.
6059
Create a build folder, navigate there, run cmake and build the tree-tests target.
61-
You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system.
60+
61+
```bash
62+
mkdir build
63+
cd build
64+
cmake .. -DINT_TREE_ENABLE_TESTS=on
65+
cmake --build .
66+
./tests/tree-tests
67+
```
68+
6269
If you want to generate the pretty drawings, install cairo, pull the submodule and pass INT_TREE_DRAW_EXAMPLES=on to the cmake command line to generate a drawings/make_drawings executeable.
6370

6471
Some features of this library require the presence of an optional type.

include/interval-tree/interval_types.hpp

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <algorithm>
77
#include <utility>
88
#include <type_traits>
9+
#include <limits>
910

1011
namespace lib_interval_tree
1112
{
@@ -220,7 +221,8 @@ namespace lib_interval_tree
220221
#endif
221222
overlaps(numerical_type l1, numerical_type h1, numerical_type l2, numerical_type h2)
222223
{
223-
return (l1 <= (h2 + 1)) && (l2 <= (h1 + 1));
224+
return (h2 == std::numeric_limits<numerical_type>::max() || l1 <= (h2 + 1)) &&
225+
(h1 == std::numeric_limits<numerical_type>::max() || l2 <= (h1 + 1));
224226
}
225227

226228
template <typename numerical_type>
@@ -412,23 +414,25 @@ namespace lib_interval_tree
412414

413415
auto closedOverlap =
414416
closed::overlaps(closedEquiv1.low(), closedEquiv1.high(), closedEquiv2.low(), closedEquiv2.high());
415-
if (!closedOverlap)
417+
if (closedOverlap)
416418
{
417-
if (closedEquiv1.high() + 1 == closedEquiv2.low() &&
418-
(ival1.right_border() == interval_border::closed_adjacent ||
419-
ival2.left_border() == interval_border::closed_adjacent))
420-
{
421-
return true;
422-
}
423-
if (closedEquiv2.high() + 1 == closedEquiv1.low() &&
424-
(ival2.right_border() == interval_border::closed_adjacent ||
425-
ival1.left_border() == interval_border::closed_adjacent))
426-
{
427-
return true;
428-
}
429-
return false;
419+
return true;
430420
}
431-
return true;
421+
if (closedEquiv1.high() != std::numeric_limits<typename interval_type::value_type>::max() &&
422+
closedEquiv1.high() + 1 == closedEquiv2.low() &&
423+
(ival1.right_border() == interval_border::closed_adjacent ||
424+
ival2.left_border() == interval_border::closed_adjacent))
425+
{
426+
return true;
427+
}
428+
if (closedEquiv2.high() != std::numeric_limits<typename interval_type::value_type>::max() &&
429+
closedEquiv2.high() + 1 == closedEquiv1.low() &&
430+
(ival2.right_border() == interval_border::closed_adjacent ||
431+
ival1.left_border() == interval_border::closed_adjacent))
432+
{
433+
return true;
434+
}
435+
return false;
432436
}
433437

434438
template <typename interval_type>

tests/CMakeLists.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,14 @@ file(GLOB sources "*.cpp")
1919
# Add Executable
2020
add_executable(tree-tests ${sources})
2121

22-
find_package(GTest REQUIRED)
22+
include(FetchContent)
23+
FetchContent_Declare(
24+
googletest
25+
GIT_REPOSITORY https://github.com/google/googletest.git
26+
GIT_TAG v1.15.2
27+
)
28+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
29+
FetchContent_MakeAvailable(googletest)
2330

2431
target_link_libraries(tree-tests PRIVATE interval-tree GTest::gtest GTest::gmock GTest::gmock_main)
2532

@@ -47,4 +54,9 @@ if (DEFINED ENV{MSYSTEM})
4754
COMMAND bash -c "ldd $<TARGET_FILE:tree-tests>" | "grep" "clang" | awk "NF == 4 { system(\"${CMAKE_COMMAND} -E copy \" \$3 \" $<TARGET_FILE_DIR:tree-tests>\") }"
4855
VERBATIM
4956
)
50-
endif()
57+
endif()
58+
59+
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
60+
target_compile_options(tree-tests PRIVATE -fsanitize=signed-integer-overflow -fno-sanitize-recover=all)
61+
target_link_options(tree-tests PRIVATE -fsanitize=signed-integer-overflow -fno-sanitize-recover=all)
62+
endif()

tests/interval_tests.hpp

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,94 @@ TEST_F(OverlapTests, DynamicBorderTests)
640640
// one side open or closed follows from other tests
641641
}
642642

643+
TEST_F(OverlapTests, DynamicBorderTestsLimits)
644+
{
645+
const auto MIN = std::numeric_limits<int>::min();
646+
const auto MAX = std::numeric_limits<int>::max();
647+
648+
constexpr auto c = interval_border::closed;
649+
constexpr auto o = interval_border::open;
650+
constexpr auto ca = interval_border::closed_adjacent;
651+
652+
// one min
653+
EXPECT_TRUE(i<dynamic>(MIN, 5, c, c).overlaps({3, 16, c, c}));
654+
EXPECT_TRUE(i<dynamic>(MIN, 5, o, c).overlaps({3, 16, c, c}));
655+
EXPECT_TRUE(i<dynamic>(MIN, 5, ca, c).overlaps({3, 16, c, c}));
656+
657+
// one max
658+
EXPECT_TRUE(i<dynamic>(0, 5, c, c).overlaps({3, MAX, c, c}));
659+
EXPECT_TRUE(i<dynamic>(0, 5, c, c).overlaps({3, MAX, c, o}));
660+
EXPECT_TRUE(i<dynamic>(0, 5, c, c).overlaps({3, MAX, c, ca}));
661+
662+
// both min
663+
EXPECT_TRUE(i<dynamic>(MIN, 5, c, c).overlaps({MIN, 16, c, c}));
664+
EXPECT_TRUE(i<dynamic>(MIN, 5, o, c).overlaps({MIN, 16, c, c}));
665+
EXPECT_TRUE(i<dynamic>(MIN, 5, ca, c).overlaps({MIN, 16, c, c}));
666+
EXPECT_TRUE(i<dynamic>(MIN, 5, c, c).overlaps({MIN, 16, o, c}));
667+
EXPECT_TRUE(i<dynamic>(MIN, 5, o, c).overlaps({MIN, 16, o, c}));
668+
EXPECT_TRUE(i<dynamic>(MIN, 5, ca, c).overlaps({MIN, 16, o, c}));
669+
EXPECT_TRUE(i<dynamic>(MIN, 5, c, c).overlaps({MIN, 16, ca, c}));
670+
EXPECT_TRUE(i<dynamic>(MIN, 5, o, c).overlaps({MIN, 16, ca, c}));
671+
EXPECT_TRUE(i<dynamic>(MIN, 5, ca, c).overlaps({MIN, 16, ca, c}));
672+
673+
// both max
674+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({3, MAX, c, c}));
675+
EXPECT_TRUE(i<dynamic>(0, MAX, c, o).overlaps({3, MAX, c, c}));
676+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({3, MAX, c, c}));
677+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({3, MAX, c, o}));
678+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({3, MAX, c, c}));
679+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({3, MAX, c, ca}));
680+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({3, MAX, c, c}));
681+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({3, MAX, c, o}));
682+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({3, MAX, c, ca}));
683+
684+
// min-max overlap
685+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({MIN, 3, c, c}));
686+
EXPECT_TRUE(i<dynamic>(0, MAX, c, o).overlaps({MIN, 3, c, c}));
687+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({MIN, 3, c, c}));
688+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({MIN, 3, o, c}));
689+
EXPECT_TRUE(i<dynamic>(0, MAX, c, o).overlaps({MIN, 3, o, c}));
690+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({MIN, 3, o, c}));
691+
EXPECT_TRUE(i<dynamic>(0, MAX, c, c).overlaps({MIN, 3, ca, c}));
692+
EXPECT_TRUE(i<dynamic>(0, MAX, c, o).overlaps({MIN, 3, ca, c}));
693+
EXPECT_TRUE(i<dynamic>(0, MAX, c, ca).overlaps({MIN, 3, ca, c}));
694+
695+
// min-max not overlap
696+
EXPECT_FALSE(i<dynamic>(1, MAX, c, c).overlaps({MIN, 0, c, c}));
697+
EXPECT_FALSE(i<dynamic>(1, MAX, c, o).overlaps({MIN, 0, c, c}));
698+
EXPECT_FALSE(i<dynamic>(1, MAX, c, ca).overlaps({MIN, 0, c, c}));
699+
EXPECT_FALSE(i<dynamic>(1, MAX, c, c).overlaps({MIN, 0, o, c}));
700+
EXPECT_FALSE(i<dynamic>(1, MAX, c, o).overlaps({MIN, 0, o, c}));
701+
EXPECT_FALSE(i<dynamic>(1, MAX, c, ca).overlaps({MIN, 0, o, c}));
702+
EXPECT_FALSE(i<dynamic>(1, MAX, c, c).overlaps({MIN, 0, ca, c}));
703+
EXPECT_FALSE(i<dynamic>(1, MAX, c, o).overlaps({MIN, 0, ca, c}));
704+
EXPECT_FALSE(i<dynamic>(1, MAX, c, ca).overlaps({MIN, 0, ca, c}));
705+
706+
EXPECT_FALSE(i<dynamic>(MIN, -1, c, c).overlaps({MAX, MAX, c, c}));
707+
EXPECT_FALSE(i<dynamic>(MIN, -1, c, ca).overlaps({MAX, MAX, c, c}));
708+
EXPECT_FALSE(i<dynamic>(MIN, -1, c, c).overlaps({MAX, MAX, ca, c}));
709+
EXPECT_FALSE(i<dynamic>(MAX, MAX, c, c).overlaps({MIN, -1, c, c}));
710+
EXPECT_FALSE(i<dynamic>(MAX, MAX, ca, c).overlaps({MIN, -1, c, c}));
711+
EXPECT_FALSE(i<dynamic>(MAX, MAX, c, c).overlaps({MIN, -1, c, ca}));
712+
}
713+
714+
TEST_F(OverlapTests, ClosedAdjacentOverlapLimits)
715+
{
716+
const auto MIN = std::numeric_limits<int>::min();
717+
const auto MAX = std::numeric_limits<int>::max();
718+
719+
EXPECT_TRUE(i<closed_adjacent>(0, 5).overlaps({3, MAX}));
720+
EXPECT_TRUE(i<closed_adjacent>(0, MAX).overlaps({3, 16}));
721+
EXPECT_TRUE(i<closed_adjacent>(0, MAX).overlaps({3, MAX}));
722+
723+
EXPECT_TRUE(i<closed_adjacent>(0, MAX - 1).overlaps({MAX, MAX}));
724+
EXPECT_TRUE(i<closed_adjacent>(MAX, MAX).overlaps({0, MAX - 1}));
725+
726+
EXPECT_FALSE(i<closed_adjacent>(0, 5).overlaps({MAX, MAX}));
727+
EXPECT_FALSE(i<closed_adjacent>(MIN, -1).overlaps({MAX, MAX}));
728+
EXPECT_FALSE(i<closed_adjacent>(MAX, MAX).overlaps({MIN, -1}));
729+
}
730+
643731
TEST_F(IntervalTests, DynamicWithinTest)
644732
{
645733
auto base = i<dynamic>(-100, 100, interval_border::closed, interval_border::closed);

0 commit comments

Comments
 (0)