eabfc6e4d9
Reviewed-by: dholmes, coleenp, jsjolen
693 lines
16 KiB
C++
693 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code 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
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include "precompiled.hpp"
|
|
#include "memory/resourceArea.hpp"
|
|
#include "utilities/growableArray.hpp"
|
|
#include "unittest.hpp"
|
|
|
|
struct WithEmbeddedArray {
|
|
// Array embedded in another class
|
|
GrowableArray<int> _a;
|
|
|
|
// Resource allocated data array
|
|
WithEmbeddedArray(int initial_max) : _a(initial_max) {}
|
|
// Arena allocated data array
|
|
WithEmbeddedArray(Arena* arena, int initial_max) : _a(arena, initial_max, 0, 0) {}
|
|
// CHeap allocated data array
|
|
WithEmbeddedArray(int initial_max, MemTag mem_tag) : _a(initial_max, mem_tag) {
|
|
assert(mem_tag != mtNone, "test requirement");
|
|
}
|
|
WithEmbeddedArray(const GrowableArray<int>& other) : _a(other) {}
|
|
};
|
|
|
|
// Test fixture to work with TEST_VM_F
|
|
class GrowableArrayTest : public ::testing::Test {
|
|
protected:
|
|
// friend -> private accessors
|
|
template <typename E>
|
|
static bool elements_on_C_heap(const GrowableArray<E>* array) {
|
|
return array->on_C_heap();
|
|
}
|
|
template <typename E>
|
|
static bool elements_on_resource_area(const GrowableArray<E>* array) {
|
|
return array->on_resource_area();
|
|
}
|
|
template <typename E>
|
|
static bool elements_on_arena(const GrowableArray<E>* array) {
|
|
return array->on_arena();
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_append(ArrayClass* a) {
|
|
// Add elements
|
|
for (int i = 0; i < 10; i++) {
|
|
a->append(i);
|
|
}
|
|
|
|
// Check size
|
|
ASSERT_EQ(a->length(), 10);
|
|
|
|
// Check elements
|
|
for (int i = 0; i < 10; i++) {
|
|
EXPECT_EQ(a->at(i), i);
|
|
}
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_clear(ArrayClass* a) {
|
|
// Add elements
|
|
for (int i = 0; i < 10; i++) {
|
|
a->append(i);
|
|
}
|
|
|
|
// Check size
|
|
ASSERT_EQ(a->length(), 10);
|
|
ASSERT_EQ(a->is_empty(), false);
|
|
|
|
// Clear elements
|
|
a->clear();
|
|
|
|
// Check size
|
|
ASSERT_EQ(a->length(), 0);
|
|
ASSERT_EQ(a->is_empty(), true);
|
|
|
|
// Add element
|
|
a->append(11);
|
|
|
|
// Check size
|
|
ASSERT_EQ(a->length(), 1);
|
|
ASSERT_EQ(a->is_empty(), false);
|
|
|
|
// Clear elements
|
|
a->clear();
|
|
|
|
// Check size
|
|
ASSERT_EQ(a->length(), 0);
|
|
ASSERT_EQ(a->is_empty(), true);
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_iterator(ArrayClass* a) {
|
|
// Add elements
|
|
for (int i = 0; i < 10; i++) {
|
|
a->append(i);
|
|
}
|
|
|
|
// Iterate
|
|
int counter = 0;
|
|
for (GrowableArrayIterator<int> i = a->begin(); i != a->end(); ++i) {
|
|
ASSERT_EQ(*i, counter++);
|
|
}
|
|
|
|
// Check count
|
|
ASSERT_EQ(counter, 10);
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_capacity(ArrayClass* a) {
|
|
ASSERT_EQ(a->length(), 0);
|
|
a->reserve(50);
|
|
ASSERT_EQ(a->length(), 0);
|
|
ASSERT_EQ(a->capacity(), 50);
|
|
for (int i = 0; i < 50; ++i) {
|
|
a->append(i);
|
|
}
|
|
ASSERT_EQ(a->length(), 50);
|
|
ASSERT_EQ(a->capacity(), 50);
|
|
a->append(50);
|
|
ASSERT_EQ(a->length(), 51);
|
|
int capacity = a->capacity();
|
|
ASSERT_GE(capacity, 51);
|
|
for (int i = 0; i < 30; ++i) {
|
|
a->pop();
|
|
}
|
|
ASSERT_EQ(a->length(), 21);
|
|
ASSERT_EQ(a->capacity(), capacity);
|
|
a->shrink_to_fit();
|
|
ASSERT_EQ(a->length(), 21);
|
|
ASSERT_EQ(a->capacity(), 21);
|
|
|
|
a->reserve(50);
|
|
ASSERT_EQ(a->length(), 21);
|
|
ASSERT_EQ(a->capacity(), 50);
|
|
|
|
a->clear();
|
|
ASSERT_EQ(a->length(), 0);
|
|
ASSERT_EQ(a->capacity(), 50);
|
|
|
|
a->shrink_to_fit();
|
|
ASSERT_EQ(a->length(), 0);
|
|
ASSERT_EQ(a->capacity(), 0);
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_copy1(ArrayClass* a) {
|
|
ASSERT_EQ(a->length(), 1);
|
|
ASSERT_EQ(a->at(0), 1);
|
|
|
|
// Only allowed to copy to stack and embedded ResourceObjs
|
|
|
|
// Copy to stack
|
|
{
|
|
GrowableArray<int> c(*a);
|
|
|
|
ASSERT_EQ(c.length(), 1);
|
|
ASSERT_EQ(c.at(0), 1);
|
|
}
|
|
|
|
// Copy to embedded
|
|
{
|
|
WithEmbeddedArray c(*a);
|
|
|
|
ASSERT_EQ(c._a.length(), 1);
|
|
ASSERT_EQ(c._a.at(0), 1);
|
|
}
|
|
}
|
|
|
|
template <typename ArrayClass>
|
|
static void test_assignment1(ArrayClass* a) {
|
|
ASSERT_EQ(a->length(), 1);
|
|
ASSERT_EQ(a->at(0), 1);
|
|
|
|
// Only allowed to assign to stack and embedded ResourceObjs
|
|
|
|
// Copy to embedded/resource
|
|
{
|
|
ResourceMark rm;
|
|
GrowableArray<int> c(1);
|
|
c = *a;
|
|
|
|
ASSERT_EQ(c.length(), 1);
|
|
ASSERT_EQ(c.at(0), 1);
|
|
}
|
|
|
|
// Copy to embedded/arena
|
|
{
|
|
Arena arena(mtTest);
|
|
GrowableArray<int> c(&arena, 1, 0, 0);
|
|
c = *a;
|
|
|
|
ASSERT_EQ(c.length(), 1);
|
|
ASSERT_EQ(c.at(0), 1);
|
|
}
|
|
|
|
// Copy to embedded/resource
|
|
{
|
|
ResourceMark rm;
|
|
WithEmbeddedArray c(1);
|
|
c._a = *a;
|
|
|
|
ASSERT_EQ(c._a.length(), 1);
|
|
ASSERT_EQ(c._a.at(0), 1);
|
|
}
|
|
|
|
// Copy to embedded/arena
|
|
{
|
|
Arena arena(mtTest);
|
|
WithEmbeddedArray c(&arena, 1);
|
|
c._a = *a;
|
|
|
|
ASSERT_EQ(c._a.length(), 1);
|
|
ASSERT_EQ(c._a.at(0), 1);
|
|
}
|
|
}
|
|
|
|
// Supported by all GrowableArrays
|
|
enum TestEnum {
|
|
Append,
|
|
Clear,
|
|
Capacity,
|
|
Iterator
|
|
};
|
|
|
|
template <typename ArrayClass>
|
|
static void do_test(ArrayClass* a, TestEnum test) {
|
|
switch (test) {
|
|
case Append:
|
|
test_append(a);
|
|
break;
|
|
|
|
case Clear:
|
|
test_clear(a);
|
|
break;
|
|
|
|
case Capacity:
|
|
test_capacity(a);
|
|
break;
|
|
|
|
case Iterator:
|
|
test_iterator(a);
|
|
break;
|
|
|
|
default:
|
|
fatal("Missing dispatch");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Only supported by GrowableArrays without CHeap data arrays
|
|
enum TestNoCHeapEnum {
|
|
Copy1,
|
|
Assignment1,
|
|
};
|
|
|
|
template <typename ArrayClass>
|
|
static void do_test(ArrayClass* a, TestNoCHeapEnum test) {
|
|
switch (test) {
|
|
case Copy1:
|
|
test_copy1(a);
|
|
break;
|
|
|
|
case Assignment1:
|
|
test_assignment1(a);
|
|
break;
|
|
|
|
default:
|
|
fatal("Missing dispatch");
|
|
break;
|
|
}
|
|
}
|
|
|
|
enum ModifyEnum {
|
|
Append1,
|
|
Append1Clear,
|
|
Append1ClearAndDeallocate,
|
|
NoModify
|
|
};
|
|
|
|
template <typename ArrayClass>
|
|
static void do_modify(ArrayClass* a, ModifyEnum modify) {
|
|
switch (modify) {
|
|
case Append1:
|
|
a->append(1);
|
|
break;
|
|
|
|
case Append1Clear:
|
|
a->append(1);
|
|
a->clear();
|
|
break;
|
|
|
|
case Append1ClearAndDeallocate:
|
|
a->append(1);
|
|
a->clear_and_deallocate();
|
|
break;
|
|
|
|
case NoModify:
|
|
// Nothing to do
|
|
break;
|
|
|
|
default:
|
|
fatal("Missing dispatch");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const int Max0 = 0;
|
|
static const int Max1 = 1;
|
|
|
|
template <typename ArrayClass, typename T>
|
|
static void modify_and_test(ArrayClass* array, ModifyEnum modify, T test) {
|
|
do_modify(array, modify);
|
|
do_test(array, test);
|
|
}
|
|
|
|
template <typename T>
|
|
static void with_no_cheap_array(int max, ModifyEnum modify, T test) {
|
|
// Resource/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
GrowableArray<int>* a = new GrowableArray<int>(max);
|
|
modify_and_test(a, modify, test);
|
|
}
|
|
|
|
// Resource/Arena allocated
|
|
// Combination not supported
|
|
|
|
// CHeap/Resource allocated
|
|
// Combination not supported
|
|
|
|
// CHeap/Arena allocated
|
|
// Combination not supported
|
|
|
|
// Stack/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
GrowableArray<int> a(max);
|
|
modify_and_test(&a, modify, test);
|
|
}
|
|
|
|
// Stack/Arena allocated
|
|
{
|
|
Arena arena(mtTest);
|
|
GrowableArray<int> a(&arena, max, 0, 0);
|
|
modify_and_test(&a, modify, test);
|
|
}
|
|
|
|
// Embedded/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
WithEmbeddedArray w(max);
|
|
modify_and_test(&w._a, modify, test);
|
|
}
|
|
|
|
// Embedded/Arena allocated
|
|
{
|
|
Arena arena(mtTest);
|
|
WithEmbeddedArray w(&arena, max);
|
|
modify_and_test(&w._a, modify, test);
|
|
}
|
|
}
|
|
|
|
static void with_cheap_array(int max, ModifyEnum modify, TestEnum test) {
|
|
// Resource/CHeap allocated
|
|
// Combination not supported
|
|
|
|
// CHeap/CHeap allocated
|
|
{
|
|
GrowableArray<int>* a = new (mtTest) GrowableArray<int>(max, mtTest);
|
|
modify_and_test(a, modify, test);
|
|
delete a;
|
|
}
|
|
|
|
// Stack/CHeap allocated
|
|
{
|
|
GrowableArray<int> a(max, mtTest);
|
|
modify_and_test(&a, modify, test);
|
|
}
|
|
|
|
// Embedded/CHeap allocated
|
|
{
|
|
WithEmbeddedArray w(max, mtTest);
|
|
modify_and_test(&w._a, modify, test);
|
|
}
|
|
}
|
|
|
|
static void with_all_types(int max, ModifyEnum modify, TestEnum test) {
|
|
with_no_cheap_array(max, modify, test);
|
|
with_cheap_array(max, modify, test);
|
|
}
|
|
|
|
static void with_all_types_empty(TestEnum test) {
|
|
with_all_types(Max0, NoModify, test);
|
|
}
|
|
|
|
static void with_all_types_max_set(TestEnum test) {
|
|
with_all_types(Max1, NoModify, test);
|
|
}
|
|
|
|
static void with_all_types_cleared(TestEnum test) {
|
|
with_all_types(Max1, Append1Clear, test);
|
|
}
|
|
|
|
static void with_all_types_clear_and_deallocated(TestEnum test) {
|
|
with_all_types(Max1, Append1ClearAndDeallocate, test);
|
|
}
|
|
|
|
static void with_all_types_all_0(TestEnum test) {
|
|
with_all_types_empty(test);
|
|
with_all_types_max_set(test);
|
|
with_all_types_cleared(test);
|
|
with_all_types_clear_and_deallocated(test);
|
|
}
|
|
|
|
static void with_no_cheap_array_append1(TestNoCHeapEnum test) {
|
|
with_no_cheap_array(Max0, Append1, test);
|
|
}
|
|
};
|
|
|
|
TEST_VM_F(GrowableArrayTest, append) {
|
|
with_all_types_all_0(Append);
|
|
}
|
|
|
|
TEST_VM_F(GrowableArrayTest, clear) {
|
|
with_all_types_all_0(Clear);
|
|
}
|
|
|
|
TEST_VM_F(GrowableArrayTest, capacity) {
|
|
with_all_types_all_0(Capacity);
|
|
}
|
|
|
|
TEST_VM_F(GrowableArrayTest, iterator) {
|
|
with_all_types_all_0(Iterator);
|
|
}
|
|
|
|
TEST_VM_F(GrowableArrayTest, copy) {
|
|
with_no_cheap_array_append1(Copy1);
|
|
}
|
|
|
|
TEST_VM_F(GrowableArrayTest, assignment) {
|
|
with_no_cheap_array_append1(Assignment1);
|
|
}
|
|
|
|
#ifdef ASSERT
|
|
TEST_VM_F(GrowableArrayTest, where) {
|
|
WithEmbeddedArray s(1, mtTest);
|
|
ASSERT_FALSE(s._a.allocated_on_C_heap());
|
|
ASSERT_TRUE(elements_on_C_heap(&s._a));
|
|
|
|
// Resource/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
GrowableArray<int>* a = new GrowableArray<int>();
|
|
ASSERT_TRUE(a->allocated_on_res_area());
|
|
ASSERT_TRUE(elements_on_resource_area(a));
|
|
}
|
|
|
|
// Resource/CHeap allocated
|
|
// Combination not supported
|
|
|
|
// Resource/Arena allocated
|
|
// Combination not supported
|
|
|
|
// CHeap/Resource allocated
|
|
// Combination not supported
|
|
|
|
// CHeap/CHeap allocated
|
|
{
|
|
GrowableArray<int>* a = new (mtTest) GrowableArray<int>(0, mtTest);
|
|
ASSERT_TRUE(a->allocated_on_C_heap());
|
|
ASSERT_TRUE(elements_on_C_heap(a));
|
|
delete a;
|
|
}
|
|
|
|
// CHeap/Arena allocated
|
|
// Combination not supported
|
|
|
|
// Stack/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
GrowableArray<int> a(0);
|
|
ASSERT_TRUE(a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_resource_area(&a));
|
|
}
|
|
|
|
// Stack/CHeap allocated
|
|
{
|
|
GrowableArray<int> a(0, mtTest);
|
|
ASSERT_TRUE(a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_C_heap(&a));
|
|
}
|
|
|
|
// Stack/Arena allocated
|
|
{
|
|
Arena arena(mtTest);
|
|
GrowableArray<int> a(&arena, 0, 0, 0);
|
|
ASSERT_TRUE(a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_arena(&a));
|
|
}
|
|
|
|
// Embedded/Resource allocated
|
|
{
|
|
ResourceMark rm;
|
|
WithEmbeddedArray w(0);
|
|
ASSERT_TRUE(w._a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_resource_area(&w._a));
|
|
}
|
|
|
|
// Embedded/CHeap allocated
|
|
{
|
|
WithEmbeddedArray w(0, mtTest);
|
|
ASSERT_TRUE(w._a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_C_heap(&w._a));
|
|
}
|
|
|
|
// Embedded/Arena allocated
|
|
{
|
|
Arena arena(mtTest);
|
|
WithEmbeddedArray w(&arena, 0);
|
|
ASSERT_TRUE(w._a.allocated_on_stack_or_embedded());
|
|
ASSERT_TRUE(elements_on_arena(&w._a));
|
|
}
|
|
}
|
|
|
|
TEST_VM_ASSERT_MSG(GrowableArrayAssertingTest, copy_with_embedded_cheap,
|
|
"assert.!on_C_heap... failed: Copying of CHeap arrays not supported") {
|
|
WithEmbeddedArray s(1, mtTest);
|
|
// Intentionally asserts that copy of CHeap arrays are not allowed
|
|
WithEmbeddedArray c(s);
|
|
}
|
|
|
|
TEST_VM_ASSERT_MSG(GrowableArrayAssertingTest, assignment_with_embedded_cheap,
|
|
"assert.!on_C_heap... failed: Assignment of CHeap arrays not supported") {
|
|
WithEmbeddedArray s(1, mtTest);
|
|
WithEmbeddedArray c(1, mtTest);
|
|
|
|
// Intentionally asserts that assignment of CHeap arrays are not allowed
|
|
c = s;
|
|
}
|
|
|
|
#endif
|
|
|
|
TEST(GrowableArrayCHeap, sanity) {
|
|
// Stack/CHeap
|
|
{
|
|
GrowableArrayCHeap<int, mtTest> a(0);
|
|
#ifdef ASSERT
|
|
ASSERT_TRUE(a.allocated_on_stack_or_embedded());
|
|
#endif
|
|
ASSERT_TRUE(a.is_empty());
|
|
|
|
a.append(1);
|
|
ASSERT_FALSE(a.is_empty());
|
|
ASSERT_EQ(a.at(0), 1);
|
|
}
|
|
|
|
// CHeap/CHeap
|
|
{
|
|
GrowableArrayCHeap<int, mtTest>* a = new GrowableArrayCHeap<int, mtTest>(0);
|
|
#ifdef ASSERT
|
|
ASSERT_TRUE(a->allocated_on_C_heap());
|
|
#endif
|
|
ASSERT_TRUE(a->is_empty());
|
|
|
|
a->append(1);
|
|
ASSERT_FALSE(a->is_empty());
|
|
ASSERT_EQ(a->at(0), 1);
|
|
delete a;
|
|
}
|
|
|
|
// CHeap/CHeap - nothrow new operator
|
|
{
|
|
GrowableArrayCHeap<int, mtTest>* a = new (std::nothrow) GrowableArrayCHeap<int, mtTest>(0);
|
|
#ifdef ASSERT
|
|
ASSERT_TRUE(a->allocated_on_C_heap());
|
|
#endif
|
|
ASSERT_TRUE(a->is_empty());
|
|
|
|
a->append(1);
|
|
ASSERT_FALSE(a->is_empty());
|
|
ASSERT_EQ(a->at(0), 1);
|
|
delete a;
|
|
}
|
|
}
|
|
|
|
TEST(GrowableArrayCHeap, find_if) {
|
|
struct Element {
|
|
int value;
|
|
};
|
|
GrowableArrayCHeap<Element, mtTest> array;
|
|
array.push({1});
|
|
array.push({2});
|
|
array.push({3});
|
|
|
|
{
|
|
int index = array.find_if([&](const Element& elem) {
|
|
return elem.value == 1;
|
|
});
|
|
ASSERT_EQ(index, 0);
|
|
}
|
|
|
|
{
|
|
int index = array.find_if([&](const Element& elem) {
|
|
return elem.value > 1;
|
|
});
|
|
ASSERT_EQ(index, 1);
|
|
}
|
|
|
|
{
|
|
int index = array.find_if([&](const Element& elem) {
|
|
return elem.value == 4;
|
|
});
|
|
ASSERT_EQ(index, -1);
|
|
}
|
|
}
|
|
|
|
TEST(GrowableArrayCHeap, find_from_end_if) {
|
|
struct Element {
|
|
int value;
|
|
};
|
|
GrowableArrayCHeap<Element, mtTest> array;
|
|
array.push({1});
|
|
array.push({2});
|
|
array.push({3});
|
|
|
|
{
|
|
int index = array.find_from_end_if([&](const Element& elem) {
|
|
return elem.value == 1;
|
|
});
|
|
ASSERT_EQ(index, 0);
|
|
}
|
|
|
|
{
|
|
int index = array.find_from_end_if([&](const Element& elem) {
|
|
return elem.value > 1;
|
|
});
|
|
ASSERT_EQ(index, 2);
|
|
}
|
|
|
|
{
|
|
int index = array.find_from_end_if([&](const Element& elem) {
|
|
return elem.value == 4;
|
|
});
|
|
ASSERT_EQ(index, -1);
|
|
}
|
|
}
|
|
|
|
TEST(GrowableArrayCHeap, returning_references_works_as_expected) {
|
|
GrowableArrayCHeap<int, mtTest> arr(8, 8, -1); // Pre-fill with 8 -1s
|
|
int& x = arr.at_grow(9, -1);
|
|
EXPECT_EQ(-1, arr.at(9));
|
|
EXPECT_EQ(-1, x);
|
|
x = 2;
|
|
EXPECT_EQ(2, arr.at(9));
|
|
int& x2 = arr.top();
|
|
EXPECT_EQ(2, arr.at(9));
|
|
x2 = 5;
|
|
EXPECT_EQ(5, arr.at(9));
|
|
|
|
int y = arr.at_grow(10, -1);
|
|
EXPECT_EQ(-1, arr.at(10));
|
|
y = arr.top();
|
|
EXPECT_EQ(-1, arr.at(10));
|
|
|
|
GrowableArrayCHeap<int, mtTest> arr2(1, 1, -1);
|
|
int& first = arr2.first();
|
|
int& last = arr2.last();
|
|
EXPECT_EQ(-1, first);
|
|
EXPECT_EQ(-1, last);
|
|
first = 5;
|
|
EXPECT_EQ(5, first);
|
|
EXPECT_EQ(5, last);
|
|
}
|