blob: 1ff44b9a4bf51d65f3b263c1bff9d6c18dc6df6b [file] [log] [blame]
/* 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 <openssl/stack.h>
#include <limits.h>
#include <algorithm>
#include <memory>
#include <utility>
#include <vector>
#include <gtest/gtest.h>
#include <openssl/mem.h>
// Define a custom stack type for testing.
using TEST_INT = int;
static void TEST_INT_free(TEST_INT *x) { OPENSSL_free(x); }
BSSL_NAMESPACE_BEGIN
BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free)
BSSL_NAMESPACE_END
static bssl::UniquePtr<TEST_INT> TEST_INT_new(int x) {
bssl::UniquePtr<TEST_INT> ret(
static_cast<TEST_INT *>(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<STACK_OF(TEST_INT), ShallowStackDeleter>;
// 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<int> &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<STACK_OF(TEST_INT)> 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<TEST_INT> 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<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
sk.get(),
[](const 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(),
[](const 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<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
ASSERT_TRUE(sk);
std::vector<int> 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<int> vec_sorted = {0, 1, 2, 3, 4, 5, 6};
std::vector<int> vec = vec_sorted;
do {
bssl::UniquePtr<STACK_OF(TEST_INT)> 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<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
sk.get(),
[](const 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()));
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<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
ASSERT_TRUE(sk);
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);
}
// Exhaustively test the binary search.
TEST(StackTest, BinarySearch) {
static const size_t kCount = 100;
for (size_t i = 0; i < kCount; i++) {
SCOPED_TRACE(i);
for (size_t j = i; j <= kCount; j++) {
SCOPED_TRACE(j);
// Make a stack where [0, i) are below, [i, j) match, and [j, kCount) are
// above.
bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
ASSERT_TRUE(sk);
for (size_t k = 0; k < i; k++) {
auto value = TEST_INT_new(-1);
ASSERT_TRUE(value);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
}
for (size_t k = i; k < j; k++) {
auto value = TEST_INT_new(0);
ASSERT_TRUE(value);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
}
for (size_t k = j; k < kCount; k++) {
auto value = TEST_INT_new(1);
ASSERT_TRUE(value);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
}
sk_TEST_INT_sort(sk.get());
auto key = TEST_INT_new(0);
ASSERT_TRUE(key);
size_t idx;
int found = sk_TEST_INT_find(sk.get(), &idx, key.get());
if (i == j) {
EXPECT_FALSE(found);
} else {
ASSERT_TRUE(found);
EXPECT_EQ(i, idx);
}
}
}
}
TEST(StackTest, DeleteIf) {
bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
ASSERT_TRUE(sk);
for (int v : {1, 9, 2, 8, 3, 7, 4, 6, 5}) {
auto obj = TEST_INT_new(v);
ASSERT_TRUE(obj);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(obj)));
}
auto keep_only_multiples = [](TEST_INT *x, void *data) {
auto d = static_cast<const int *>(data);
if (*x % *d == 0) {
return 0;
}
TEST_INT_free(x);
return 1;
};
int d = 2;
sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d);
ExpectStackEquals(sk.get(), {2, 8, 4, 6});
EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
sk_TEST_INT_sort(sk.get());
ExpectStackEquals(sk.get(), {2, 4, 6, 8});
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// Keep only multiples of four.
d = 4;
sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d);
ExpectStackEquals(sk.get(), {4, 8});
// Removing elements preserves the sorted bit.
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// Delete everything.
d = 16;
sk_TEST_INT_delete_if(sk.get(), keep_only_multiples, &d);
ExpectStackEquals(sk.get(), {});
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
}
TEST(StackTest, IsSorted) {
bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
ASSERT_TRUE(sk);
EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
// Empty lists are always known to be sorted.
sk_TEST_INT_set_cmp_func(sk.get(), compare);
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// As are one-element lists.
auto value = TEST_INT_new(2);
ASSERT_TRUE(value);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// Two-element lists require an explicit sort.
value = TEST_INT_new(1);
ASSERT_TRUE(value);
ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
// The list is now sorted.
sk_TEST_INT_sort(sk.get());
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// After changing the comparison function, it no longer is sorted.
sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
sk_TEST_INT_sort(sk.get());
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// But, starting from one element, switching the comparison function preserves
// the sorted bit.
TEST_INT_free(sk_TEST_INT_pop(sk.get()));
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
sk_TEST_INT_set_cmp_func(sk.get(), compare);
EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
// Without a comparison function, the list cannot be sorted.
sk_TEST_INT_set_cmp_func(sk.get(), nullptr);
EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
}