/* Copyright (c) 2018, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include // Define a custom stack type for testing. using TEST_INT = int; static void TEST_INT_free(TEST_INT *x) { OPENSSL_free(x); } namespace bssl { BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free) } // namespace bssl static bssl::UniquePtr TEST_INT_new(int x) { bssl::UniquePtr ret( static_cast(OPENSSL_malloc(sizeof(TEST_INT)))); if (!ret) { return nullptr; } *ret = x; return ret; } DEFINE_STACK_OF(TEST_INT) struct ShallowStackDeleter { void operator()(STACK_OF(TEST_INT) *sk) const { sk_TEST_INT_free(sk); } }; using ShallowStack = std::unique_ptr; // kNull is treated as a nullptr expectation for purposes of ExpectStackEquals. // The tests in this file will never use it as a test value. static const int kNull = INT_MIN; static void ExpectStackEquals(const STACK_OF(TEST_INT) *sk, const std::vector &vec) { EXPECT_EQ(vec.size(), sk_TEST_INT_num(sk)); for (size_t i = 0; i < vec.size(); i++) { SCOPED_TRACE(i); const TEST_INT *obj = sk_TEST_INT_value(sk, i); if (vec[i] == kNull) { EXPECT_FALSE(obj); } else { EXPECT_TRUE(obj); if (obj) { EXPECT_EQ(vec[i], *obj); } } } // Reading out-of-bounds fails. EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size())); EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size() + 1)); } TEST(StackTest, Basic) { bssl::UniquePtr sk(sk_TEST_INT_new_null()); ASSERT_TRUE(sk); // The stack starts out empty. ExpectStackEquals(sk.get(), {}); // Removing elements from an empty stack does nothing. EXPECT_FALSE(sk_TEST_INT_pop(sk.get())); EXPECT_FALSE(sk_TEST_INT_shift(sk.get())); EXPECT_FALSE(sk_TEST_INT_delete(sk.get(), 0)); // Push some elements. for (int i = 0; i < 6; i++) { auto value = TEST_INT_new(i); ASSERT_TRUE(value); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value))); } ExpectStackEquals(sk.get(), {0, 1, 2, 3, 4, 5}); // Items may be inserted in the middle. auto value = TEST_INT_new(6); ASSERT_TRUE(value); // Hold on to the object for later. TEST_INT *raw = value.get(); ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 4)); value.release(); // sk_TEST_INT_insert takes ownership on success. ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5}); // Without a comparison function, find searches by pointer. value = TEST_INT_new(6); ASSERT_TRUE(value); size_t index; EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, value.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, raw)); EXPECT_EQ(4u, index); // sk_TEST_INT_insert can also insert values at the end. value = TEST_INT_new(7); ASSERT_TRUE(value); ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 7)); value.release(); // sk_TEST_INT_insert takes ownership on success. ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7}); // Out-of-bounds indices are clamped. value = TEST_INT_new(8); ASSERT_TRUE(value); ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 999)); value.release(); // sk_TEST_INT_insert takes ownership on success. ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7, 8}); // Test removing elements from various places. bssl::UniquePtr removed(sk_TEST_INT_pop(sk.get())); EXPECT_EQ(8, *removed); ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7}); removed.reset(sk_TEST_INT_shift(sk.get())); EXPECT_EQ(0, *removed); ExpectStackEquals(sk.get(), {1, 2, 3, 6, 4, 5, 7}); removed.reset(sk_TEST_INT_delete(sk.get(), 2)); EXPECT_EQ(3, *removed); ExpectStackEquals(sk.get(), {1, 2, 6, 4, 5, 7}); // Objects may also be deleted by pointer. removed.reset(sk_TEST_INT_delete_ptr(sk.get(), raw)); EXPECT_EQ(raw, removed.get()); ExpectStackEquals(sk.get(), {1, 2, 4, 5, 7}); // Deleting is a no-op is the object is not found. value = TEST_INT_new(100); ASSERT_TRUE(value); EXPECT_FALSE(sk_TEST_INT_delete_ptr(sk.get(), value.get())); // Insert nullptr to test deep copy handling of it. ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), nullptr, 0)); ExpectStackEquals(sk.get(), {kNull, 1, 2, 4, 5, 7}); // Test both deep and shallow copies. bssl::UniquePtr copy(sk_TEST_INT_deep_copy( sk.get(), [](TEST_INT *x) -> TEST_INT * { return x == nullptr ? nullptr : TEST_INT_new(*x).release(); }, TEST_INT_free)); ASSERT_TRUE(copy); ExpectStackEquals(copy.get(), {kNull, 1, 2, 4, 5, 7}); ShallowStack shallow(sk_TEST_INT_dup(sk.get())); ASSERT_TRUE(shallow); ASSERT_EQ(sk_TEST_INT_num(sk.get()), sk_TEST_INT_num(shallow.get())); for (size_t i = 0; i < sk_TEST_INT_num(sk.get()); i++) { EXPECT_EQ(sk_TEST_INT_value(sk.get(), i), sk_TEST_INT_value(shallow.get(), i)); } // Deep copies may fail. This should clean up temporaries. EXPECT_FALSE(sk_TEST_INT_deep_copy(sk.get(), [](TEST_INT *x) -> TEST_INT * { return x == nullptr || *x == 4 ? nullptr : TEST_INT_new(*x).release(); }, TEST_INT_free)); // sk_TEST_INT_zero clears a stack, but does not free the elements. ShallowStack shallow2(sk_TEST_INT_dup(sk.get())); ASSERT_TRUE(shallow2); sk_TEST_INT_zero(shallow2.get()); ExpectStackEquals(shallow2.get(), {}); } TEST(StackTest, BigStack) { bssl::UniquePtr sk(sk_TEST_INT_new_null()); ASSERT_TRUE(sk); std::vector expected; static const int kCount = 100000; for (int i = 0; i < kCount; i++) { auto value = TEST_INT_new(i); ASSERT_TRUE(value); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value))); expected.push_back(i); } ExpectStackEquals(sk.get(), expected); } static uint64_t g_compare_count = 0; static int compare(const TEST_INT **a, const TEST_INT **b) { g_compare_count++; if (**a < **b) { return -1; } if (**a > **b) { return 1; } return 0; } static int compare_reverse(const TEST_INT **a, const TEST_INT **b) { return -compare(a, b); } TEST(StackTest, Sorted) { std::vector vec_sorted = {0, 1, 2, 3, 4, 5, 6}; std::vector vec = vec_sorted; do { bssl::UniquePtr sk(sk_TEST_INT_new(compare)); ASSERT_TRUE(sk); for (int v : vec) { auto value = TEST_INT_new(v); ASSERT_TRUE(value); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value))); } // The stack is not (known to be) sorted. EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); // With a comparison function, find matches by value. auto ten = TEST_INT_new(10); ASSERT_TRUE(ten); size_t index; EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get())); auto three = TEST_INT_new(3); ASSERT_TRUE(three); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); EXPECT_EQ(3, *sk_TEST_INT_value(sk.get(), index)); sk_TEST_INT_sort(sk.get()); EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); ExpectStackEquals(sk.get(), vec_sorted); // Sorting an already-sorted list is a no-op. uint64_t old_compare_count = g_compare_count; sk_TEST_INT_sort(sk.get()); EXPECT_EQ(old_compare_count, g_compare_count); EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); ExpectStackEquals(sk.get(), vec_sorted); // When sorted, find uses binary search. ASSERT_TRUE(ten); EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get())); ASSERT_TRUE(three); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); EXPECT_EQ(3u, index); // Copies preserve comparison and sorted information. bssl::UniquePtr copy(sk_TEST_INT_deep_copy( sk.get(), [](TEST_INT *x) -> TEST_INT * { return TEST_INT_new(*x).release(); }, TEST_INT_free)); ASSERT_TRUE(copy); EXPECT_TRUE(sk_TEST_INT_is_sorted(copy.get())); ASSERT_TRUE(sk_TEST_INT_find(copy.get(), &index, three.get())); EXPECT_EQ(3u, index); ShallowStack copy2(sk_TEST_INT_dup(sk.get())); ASSERT_TRUE(copy2); EXPECT_TRUE(sk_TEST_INT_is_sorted(copy2.get())); ASSERT_TRUE(sk_TEST_INT_find(copy2.get(), &index, three.get())); EXPECT_EQ(3u, index); // Removing elements does not affect sortedness. TEST_INT_free(sk_TEST_INT_delete(sk.get(), 0)); EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); // Changing the comparison function invalidates sortedness. sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse); EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); EXPECT_EQ(2u, index); sk_TEST_INT_sort(sk.get()); ExpectStackEquals(sk.get(), {6, 5, 4, 3, 2, 1}); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get())); EXPECT_EQ(3u, index); // Inserting a new element invalidates sortedness. auto tmp = TEST_INT_new(10); ASSERT_TRUE(tmp); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(tmp))); EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, ten.get())); EXPECT_EQ(6u, index); } while (std::next_permutation(vec.begin(), vec.end())); } // sk_*_find should return the first matching element in all cases. TEST(StackTest, FindFirst) { bssl::UniquePtr sk(sk_TEST_INT_new(compare)); auto value = TEST_INT_new(1); ASSERT_TRUE(value); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value))); for (int i = 0; i < 10; i++) { value = TEST_INT_new(2); ASSERT_TRUE(value); ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value))); } const TEST_INT *two = sk_TEST_INT_value(sk.get(), 1); // Pointer-based equality. size_t index; ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); EXPECT_EQ(1u, index); // Comparator-based equality, unsorted. sk_TEST_INT_set_cmp_func(sk.get(), compare); EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); EXPECT_EQ(1u, index); // Comparator-based equality, sorted. sk_TEST_INT_sort(sk.get()); EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); EXPECT_EQ(1u, index); // Comparator-based equality, sorted and at the front. sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse); sk_TEST_INT_sort(sk.get()); EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get())); ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two)); EXPECT_EQ(0u, index); }