8295808: GrowableArray should support capacity management

Reviewed-by: aboldtch, tschatzl, sspitsyn
This commit is contained in:
Kim Barrett 2022-10-25 17:42:48 +00:00
parent 6289600fe8
commit 3a873d3c5b
8 changed files with 176 additions and 105 deletions

View file

@ -1902,7 +1902,7 @@ void GraphBuilder::check_args_for_profiling(Values* obj_args, int expected) {
bool ignored_will_link; bool ignored_will_link;
ciSignature* declared_signature = NULL; ciSignature* declared_signature = NULL;
ciMethod* real_target = method()->get_method_at_bci(bci(), ignored_will_link, &declared_signature); ciMethod* real_target = method()->get_method_at_bci(bci(), ignored_will_link, &declared_signature);
assert(expected == obj_args->max_length() || real_target->is_method_handle_intrinsic(), "missed on arg?"); assert(expected == obj_args->capacity() || real_target->is_method_handle_intrinsic(), "missed on arg?");
#endif #endif
} }
@ -1913,7 +1913,7 @@ Values* GraphBuilder::collect_args_for_profiling(Values* args, ciMethod* target,
if (obj_args == NULL) { if (obj_args == NULL) {
return NULL; return NULL;
} }
int s = obj_args->max_length(); int s = obj_args->capacity();
// if called through method handle invoke, some arguments may have been popped // if called through method handle invoke, some arguments may have been popped
for (int i = start, j = 0; j < s && i < args->length(); i++) { for (int i = start, j = 0; j < s && i < args->length(); i++) {
if (args->at(i)->type()->is_object_kind()) { if (args->at(i)->type()->is_object_kind()) {
@ -3968,9 +3968,9 @@ bool GraphBuilder::try_inline_full(ciMethod* callee, bool holder_known, bool ign
int start = 0; int start = 0;
Values* obj_args = args_list_for_profiling(callee, start, has_receiver); Values* obj_args = args_list_for_profiling(callee, start, has_receiver);
if (obj_args != NULL) { if (obj_args != NULL) {
int s = obj_args->max_length(); int s = obj_args->capacity();
// if called through method handle invoke, some arguments may have been popped // if called through method handle invoke, some arguments may have been popped
for (int i = args_base+start, j = 0; j < obj_args->max_length() && i < state()->stack_size(); ) { for (int i = args_base+start, j = 0; j < obj_args->capacity() && i < state()->stack_size(); ) {
Value v = state()->stack_at_inc(i); Value v = state()->stack_at_inc(i);
if (v->type()->is_object_kind()) { if (v->type()->is_object_kind()) {
obj_args->push(v); obj_args->push(v);

View file

@ -56,10 +56,11 @@
// the vectors. The size of the table is the size of either vector. // the vectors. The size of the table is the size of either vector.
// //
// The capacity of the vectors is explicitly controlled, based on the size. // The capacity of the vectors is explicitly controlled, based on the size.
// Given N > 0 and 2^N <= size < 2^(N+1), then capacity = 2^N + k * 2^(N-1) // Given N > 0 and 2^N <= size < 2^(N+1), then capacity <= 2^N + k * 2^(N-1)
// for the smallest integer k in [0,2] such that size <= capacity. That is, // for the smallest integer k in [0,2] such that size <= capacity. That is,
// use a power of 2 or the midpoint between consecutive powers of 2 that is // use a power of 2 or the midpoint between consecutive powers of 2 that is
// minimally at least size. // minimally at least size. When adding an entry and the capacity has been
// reached, capacity is increased to the next of those values.
// //
// The main benefit of this representation is that it uses less space than a // The main benefit of this representation is that it uses less space than a
// more traditional linked-list of entry nodes representation. Such a // more traditional linked-list of entry nodes representation. Such a
@ -89,12 +90,11 @@ class StringDedup::Table::Bucket {
GrowableArrayCHeap<uint, mtStringDedup> _hashes; GrowableArrayCHeap<uint, mtStringDedup> _hashes;
GrowableArrayCHeap<TableValue, mtStringDedup> _values; GrowableArrayCHeap<TableValue, mtStringDedup> _values;
void adjust_capacity(int new_capacity);
void expand_if_full(); void expand_if_full();
public: public:
// precondition: reserve == 0 or is the result of needed_capacity. // precondition: reserve == 0 or is the result of needed_capacity.
Bucket(int reserve = 0); explicit Bucket(int reserve = 0);
~Bucket() { ~Bucket() {
while (!_values.is_empty()) { while (!_values.is_empty()) {
@ -107,7 +107,7 @@ public:
const GrowableArrayView<uint>& hashes() const { return _hashes; } const GrowableArrayView<uint>& hashes() const { return _hashes; }
const GrowableArrayView<TableValue>& values() const { return _values; } const GrowableArrayView<TableValue>& values() const { return _values; }
bool is_empty() const { return _hashes.length() == 0; } bool is_empty() const { return _hashes.is_empty(); }
int length() const { return _hashes.length(); } int length() const { return _hashes.length(); }
void add(uint hash_code, TableValue value) { void add(uint hash_code, TableValue value) {
@ -150,33 +150,17 @@ int StringDedup::Table::Bucket::needed_capacity(int needed) {
return (needed <= low) ? low : high; return (needed <= low) ? low : high;
} }
void StringDedup::Table::Bucket::adjust_capacity(int new_capacity) {
GrowableArrayCHeap<uint, mtStringDedup> new_hashes{new_capacity};
GrowableArrayCHeap<TableValue, mtStringDedup> new_values{new_capacity};
while (!_hashes.is_empty()) {
new_hashes.push(_hashes.pop());
new_values.push(_values.pop());
}
_hashes.swap(&new_hashes);
_values.swap(&new_values);
}
void StringDedup::Table::Bucket::expand_if_full() { void StringDedup::Table::Bucket::expand_if_full() {
if (_hashes.length() == _hashes.max_length()) { if (_hashes.is_full()) {
adjust_capacity(needed_capacity(_hashes.max_length() + 1)); int needed = needed_capacity(_hashes.capacity() + 1);
_hashes.reserve(needed);
_values.reserve(needed);
} }
} }
void StringDedup::Table::Bucket::shrink() { void StringDedup::Table::Bucket::shrink() {
if (_hashes.is_empty()) { _hashes.shrink_to_fit();
_hashes.clear_and_deallocate(); _values.shrink_to_fit();
_values.clear_and_deallocate();
} else {
int target = needed_capacity(_hashes.length());
if (target < _hashes.max_length()) {
adjust_capacity(target);
}
}
} }
StringDedup::Table::TableValue StringDedup::Table::TableValue

View file

@ -256,7 +256,7 @@ static void initialize_dummy_descriptors(GrowableArray<DCmdArgumentInfo*>* array
false, false,
true, // a DcmdFramework "option" true, // a DcmdFramework "option"
false); false);
for (int i = 0; i < array->max_length(); ++i) { for (int i = 0; i < array->capacity(); ++i) {
array->append(dummy); array->append(dummy);
} }
} }

View file

@ -487,7 +487,7 @@
/*******************/ \ /*******************/ \
\ \
nonstatic_field(GrowableArrayBase, _len, int) \ nonstatic_field(GrowableArrayBase, _len, int) \
nonstatic_field(GrowableArrayBase, _max, int) \ nonstatic_field(GrowableArrayBase, _capacity, int) \
nonstatic_field(GrowableArray<int>, _data, int*) \ nonstatic_field(GrowableArray<int>, _data, int*) \
\ \
/********************************/ \ /********************************/ \

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -76,23 +76,23 @@ protected:
// Current number of accessible elements // Current number of accessible elements
int _len; int _len;
// Current number of allocated elements // Current number of allocated elements
int _max; int _capacity;
GrowableArrayBase(int initial_max, int initial_len) : GrowableArrayBase(int capacity, int initial_len) :
_len(initial_len), _len(initial_len),
_max(initial_max) { _capacity(capacity) {
assert(_len >= 0 && _len <= _max, "initial_len too big"); assert(_len >= 0 && _len <= _capacity, "initial_len too big");
} }
~GrowableArrayBase() {} ~GrowableArrayBase() {}
public: public:
int length() const { return _len; } int length() const { return _len; }
int max_length() const { return _max; } int capacity() const { return _capacity; }
bool is_empty() const { return _len == 0; } bool is_empty() const { return _len == 0; }
bool is_nonempty() const { return _len != 0; } bool is_nonempty() const { return _len != 0; }
bool is_full() const { return _len == _max; } bool is_full() const { return _len == _capacity; }
void clear() { _len = 0; } void clear() { _len = 0; }
void trunc_to(int length) { void trunc_to(int length) {
@ -118,8 +118,8 @@ class GrowableArrayView : public GrowableArrayBase {
protected: protected:
E* _data; // data array E* _data; // data array
GrowableArrayView<E>(E* data, int initial_max, int initial_len) : GrowableArrayView<E>(E* data, int capacity, int initial_len) :
GrowableArrayBase(initial_max, initial_len), _data(data) {} GrowableArrayBase(capacity, initial_len), _data(data) {}
~GrowableArrayView() {} ~GrowableArrayView() {}
@ -157,12 +157,12 @@ public:
} }
E first() const { E first() const {
assert(_len > 0, "empty list"); assert(_len > 0, "empty");
return _data[0]; return _data[0];
} }
E top() const { E top() const {
assert(_len > 0, "empty list"); assert(_len > 0, "empty");
return _data[_len-1]; return _data[_len-1];
} }
@ -337,7 +337,7 @@ public:
void print() const { void print() const {
tty->print("Growable Array " PTR_FORMAT, p2i(this)); tty->print("Growable Array " PTR_FORMAT, p2i(this));
tty->print(": length %d (_max %d) { ", _len, _max); tty->print(": length %d (capacity %d) { ", _len, _capacity);
for (int i = 0; i < _len; i++) { for (int i = 0; i < _len; i++) {
tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i])); tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i]));
} }
@ -360,23 +360,24 @@ template <typename E, typename Derived>
class GrowableArrayWithAllocator : public GrowableArrayView<E> { class GrowableArrayWithAllocator : public GrowableArrayView<E> {
friend class VMStructs; friend class VMStructs;
void expand_to(int j);
void grow(int j); void grow(int j);
protected: protected:
GrowableArrayWithAllocator(E* data, int initial_max) : GrowableArrayWithAllocator(E* data, int capacity) :
GrowableArrayView<E>(data, initial_max, 0) { GrowableArrayView<E>(data, capacity, 0) {
for (int i = 0; i < initial_max; i++) { for (int i = 0; i < capacity; i++) {
::new ((void*)&data[i]) E(); ::new ((void*)&data[i]) E();
} }
} }
GrowableArrayWithAllocator(E* data, int initial_max, int initial_len, const E& filler) : GrowableArrayWithAllocator(E* data, int capacity, int initial_len, const E& filler) :
GrowableArrayView<E>(data, initial_max, initial_len) { GrowableArrayView<E>(data, capacity, initial_len) {
int i = 0; int i = 0;
for (; i < initial_len; i++) { for (; i < initial_len; i++) {
::new ((void*)&data[i]) E(filler); ::new ((void*)&data[i]) E(filler);
} }
for (; i < initial_max; i++) { for (; i < capacity; i++) {
::new ((void*)&data[i]) E(); ::new ((void*)&data[i]) E();
} }
} }
@ -385,7 +386,7 @@ protected:
public: public:
int append(const E& elem) { int append(const E& elem) {
if (this->_len == this->_max) grow(this->_len); if (this->_len == this->_capacity) grow(this->_len);
int idx = this->_len++; int idx = this->_len++;
this->_data[idx] = elem; this->_data[idx] = elem;
return idx; return idx;
@ -403,7 +404,7 @@ public:
E at_grow(int i, const E& fill = E()) { E at_grow(int i, const E& fill = E()) {
assert(0 <= i, "negative index"); assert(0 <= i, "negative index");
if (i >= this->_len) { if (i >= this->_len) {
if (i >= this->_max) grow(i); if (i >= this->_capacity) grow(i);
for (int j = this->_len; j <= i; j++) for (int j = this->_len; j <= i; j++)
this->_data[j] = fill; this->_data[j] = fill;
this->_len = i+1; this->_len = i+1;
@ -414,7 +415,7 @@ public:
void at_put_grow(int i, const E& elem, const E& fill = E()) { void at_put_grow(int i, const E& elem, const E& fill = E()) {
assert(0 <= i, "negative index"); assert(0 <= i, "negative index");
if (i >= this->_len) { if (i >= this->_len) {
if (i >= this->_max) grow(i); if (i >= this->_capacity) grow(i);
for (int j = this->_len; j < i; j++) for (int j = this->_len; j < i; j++)
this->_data[j] = fill; this->_data[j] = fill;
this->_len = i+1; this->_len = i+1;
@ -425,7 +426,7 @@ public:
// inserts the given element before the element at index i // inserts the given element before the element at index i
void insert_before(const int idx, const E& elem) { void insert_before(const int idx, const E& elem) {
assert(0 <= idx && idx <= this->_len, "illegal index"); assert(0 <= idx && idx <= this->_len, "illegal index");
if (this->_len == this->_max) grow(this->_len); if (this->_len == this->_capacity) grow(this->_len);
for (int j = this->_len - 1; j >= idx; j--) { for (int j = this->_len - 1; j >= idx; j--) {
this->_data[j + 1] = this->_data[j]; this->_data[j + 1] = this->_data[j];
} }
@ -437,7 +438,7 @@ public:
assert(0 <= idx && idx <= this->_len, "illegal index"); assert(0 <= idx && idx <= this->_len, "illegal index");
int array_len = array->length(); int array_len = array->length();
int new_len = this->_len + array_len; int new_len = this->_len + array_len;
if (new_len >= this->_max) grow(new_len); if (new_len >= this->_capacity) grow(new_len);
for (int j = this->_len - 1; j >= idx; j--) { for (int j = this->_len - 1; j >= idx; j--) {
this->_data[j + array_len] = this->_data[j]; this->_data[j + array_len] = this->_data[j];
@ -481,23 +482,29 @@ public:
void swap(GrowableArrayWithAllocator<E, Derived>* other) { void swap(GrowableArrayWithAllocator<E, Derived>* other) {
::swap(this->_data, other->_data); ::swap(this->_data, other->_data);
::swap(this->_len, other->_len); ::swap(this->_len, other->_len);
::swap(this->_max, other->_max); ::swap(this->_capacity, other->_capacity);
} }
// Ensure capacity is at least new_capacity.
void reserve(int new_capacity);
// Reduce capacity to length.
void shrink_to_fit();
void clear_and_deallocate(); void clear_and_deallocate();
}; };
template <typename E, typename Derived> template <typename E, typename Derived>
void GrowableArrayWithAllocator<E, Derived>::grow(int j) { void GrowableArrayWithAllocator<E, Derived>::expand_to(int new_capacity) {
int old_max = this->_max; int old_capacity = this->_capacity;
// grow the array by increasing _max to the first power of two larger than the size we need assert(new_capacity > old_capacity,
this->_max = next_power_of_2((uint32_t)j); "expected growth but %d <= %d", new_capacity, old_capacity);
// j < _max this->_capacity = new_capacity;
E* newData = static_cast<Derived*>(this)->allocate(); E* newData = static_cast<Derived*>(this)->allocate();
int i = 0; int i = 0;
for ( ; i < this->_len; i++) ::new ((void*)&newData[i]) E(this->_data[i]); for ( ; i < this->_len; i++) ::new ((void*)&newData[i]) E(this->_data[i]);
for ( ; i < this->_max; i++) ::new ((void*)&newData[i]) E(); for ( ; i < this->_capacity; i++) ::new ((void*)&newData[i]) E();
for (i = 0; i < old_max; i++) this->_data[i].~E(); for (i = 0; i < old_capacity; i++) this->_data[i].~E();
if (this->_data != NULL) { if (this->_data != NULL) {
static_cast<Derived*>(this)->deallocate(this->_data); static_cast<Derived*>(this)->deallocate(this->_data);
} }
@ -505,16 +512,50 @@ void GrowableArrayWithAllocator<E, Derived>::grow(int j) {
} }
template <typename E, typename Derived> template <typename E, typename Derived>
void GrowableArrayWithAllocator<E, Derived>::clear_and_deallocate() { void GrowableArrayWithAllocator<E, Derived>::grow(int j) {
if (this->_data != NULL) { // grow the array by increasing _capacity to the first power of two larger than the size we need
for (int i = 0; i < this->_max; i++) { expand_to(next_power_of_2((uint32_t)j));
this->_data[i].~E(); }
}
static_cast<Derived*>(this)->deallocate(this->_data); template <typename E, typename Derived>
this->_data = NULL; void GrowableArrayWithAllocator<E, Derived>::reserve(int new_capacity) {
if (new_capacity > this->_capacity) {
expand_to(new_capacity);
} }
this->_len = 0; }
this->_max = 0;
template <typename E, typename Derived>
void GrowableArrayWithAllocator<E, Derived>::shrink_to_fit() {
int old_capacity = this->_capacity;
int len = this->_len;
assert(len <= old_capacity, "invariant");
// If already at full capacity, nothing to do.
if (len == old_capacity) {
return;
}
// If not empty, allocate new, smaller, data, and copy old data to it.
E* old_data = this->_data;
E* new_data = nullptr;
this->_capacity = len; // Must preceed allocate().
if (len > 0) {
new_data = static_cast<Derived*>(this)->allocate();
for (int i = 0; i < len; ++i) ::new (&new_data[i]) E(old_data[i]);
}
// Destroy contents of old data, and deallocate it.
for (int i = 0; i < old_capacity; ++i) old_data[i].~E();
if (old_data != nullptr) {
static_cast<Derived*>(this)->deallocate(old_data);
}
// Install new data, which might be nullptr.
this->_data = new_data;
}
template <typename E, typename Derived>
void GrowableArrayWithAllocator<E, Derived>::clear_and_deallocate() {
this->clear();
this->shrink_to_fit();
} }
class GrowableArrayResourceAllocator { class GrowableArrayResourceAllocator {
@ -661,15 +702,15 @@ class GrowableArray : public GrowableArrayWithAllocator<E, GrowableArray<E> > {
E* allocate() { E* allocate() {
if (on_stack()) { if (on_stack()) {
debug_only(_metadata.on_stack_alloc_check()); debug_only(_metadata.on_stack_alloc_check());
return allocate(this->_max); return allocate(this->_capacity);
} }
if (on_C_heap()) { if (on_C_heap()) {
return allocate(this->_max, _metadata.memflags()); return allocate(this->_capacity, _metadata.memflags());
} }
assert(on_arena(), "Sanity"); assert(on_arena(), "Sanity");
return allocate(this->_max, _metadata.arena()); return allocate(this->_capacity, _metadata.arena());
} }
void deallocate(E* mem) { void deallocate(E* mem) {
@ -679,26 +720,26 @@ class GrowableArray : public GrowableArrayWithAllocator<E, GrowableArray<E> > {
} }
public: public:
GrowableArray(int initial_max = 2, MEMFLAGS memflags = mtNone) : GrowableArray(int initial_capacity = 2, MEMFLAGS memflags = mtNone) :
GrowableArrayWithAllocator<E, GrowableArray<E> >( GrowableArrayWithAllocator<E, GrowableArray<E> >(
allocate(initial_max, memflags), allocate(initial_capacity, memflags),
initial_max), initial_capacity),
_metadata(memflags) { _metadata(memflags) {
init_checks(); init_checks();
} }
GrowableArray(int initial_max, int initial_len, const E& filler, MEMFLAGS memflags = mtNone) : GrowableArray(int initial_capacity, int initial_len, const E& filler, MEMFLAGS memflags = mtNone) :
GrowableArrayWithAllocator<E, GrowableArray<E> >( GrowableArrayWithAllocator<E, GrowableArray<E> >(
allocate(initial_max, memflags), allocate(initial_capacity, memflags),
initial_max, initial_len, filler), initial_capacity, initial_len, filler),
_metadata(memflags) { _metadata(memflags) {
init_checks(); init_checks();
} }
GrowableArray(Arena* arena, int initial_max, int initial_len, const E& filler) : GrowableArray(Arena* arena, int initial_capacity, int initial_len, const E& filler) :
GrowableArrayWithAllocator<E, GrowableArray<E> >( GrowableArrayWithAllocator<E, GrowableArray<E> >(
allocate(initial_max, arena), allocate(initial_capacity, arena),
initial_max, initial_len, filler), initial_capacity, initial_len, filler),
_metadata(arena) { _metadata(arena) {
init_checks(); init_checks();
} }
@ -728,7 +769,7 @@ class GrowableArrayCHeap : public GrowableArrayWithAllocator<E, GrowableArrayCHe
NONCOPYABLE(GrowableArrayCHeap); NONCOPYABLE(GrowableArrayCHeap);
E* allocate() { E* allocate() {
return allocate(this->_max, F); return allocate(this->_capacity, F);
} }
void deallocate(E* mem) { void deallocate(E* mem) {
@ -736,15 +777,15 @@ class GrowableArrayCHeap : public GrowableArrayWithAllocator<E, GrowableArrayCHe
} }
public: public:
GrowableArrayCHeap(int initial_max = 0) : GrowableArrayCHeap(int initial_capacity = 0) :
GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >( GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >(
allocate(initial_max, F), allocate(initial_capacity, F),
initial_max) {} initial_capacity) {}
GrowableArrayCHeap(int initial_max, int initial_len, const E& filler) : GrowableArrayCHeap(int initial_capacity, int initial_len, const E& filler) :
GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >( GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >(
allocate(initial_max, F), allocate(initial_capacity, F),
initial_max, initial_len, filler) {} initial_capacity, initial_len, filler) {}
~GrowableArrayCHeap() { ~GrowableArrayCHeap() {
this->clear_and_deallocate(); this->clear_and_deallocate();

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -42,16 +42,16 @@ public class GenericGrowableArray extends VMObject {
} }
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("GrowableArrayBase"); Type type = db.lookupType("GrowableArrayBase");
_max_field = new CIntField(type.getCIntegerField("_max"), 0); _capacity_field = new CIntField(type.getCIntegerField("_capacity"), 0);
_len_field = new CIntField(type.getCIntegerField("_len"), 0); _len_field = new CIntField(type.getCIntegerField("_len"), 0);
} }
private static CIntField _max_field; private static CIntField _capacity_field;
private static CIntField _len_field; private static CIntField _len_field;
public int max() { public int capacity() {
return (int)_max_field.getValue(getAddress()); return (int)_capacity_field.getValue(getAddress());
} }
public int length() { public int length() {

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -39,11 +39,11 @@ TEST(ZArray, sanity) {
// Check size // Check size
ASSERT_EQ(a.length(), 0); ASSERT_EQ(a.length(), 0);
ASSERT_EQ(a.max_length(), 0); ASSERT_EQ(a.capacity(), 0);
ASSERT_EQ(a.is_empty(), true); ASSERT_EQ(a.is_empty(), true);
ASSERT_EQ(b.length(), 10); ASSERT_EQ(b.length(), 10);
ASSERT_GE(b.max_length(), 10); ASSERT_GE(b.capacity(), 10);
ASSERT_EQ(b.is_empty(), false); ASSERT_EQ(b.is_empty(), false);
// Clear elements // Clear elements
@ -51,14 +51,14 @@ TEST(ZArray, sanity) {
// Check that b is unaffected // Check that b is unaffected
ASSERT_EQ(b.length(), 10); ASSERT_EQ(b.length(), 10);
ASSERT_GE(b.max_length(), 10); ASSERT_GE(b.capacity(), 10);
ASSERT_EQ(b.is_empty(), false); ASSERT_EQ(b.is_empty(), false);
a.append(1); a.append(1);
// Check that b is unaffected // Check that b is unaffected
ASSERT_EQ(b.length(), 10); ASSERT_EQ(b.length(), 10);
ASSERT_GE(b.max_length(), 10); ASSERT_GE(b.capacity(), 10);
ASSERT_EQ(b.is_empty(), false); ASSERT_EQ(b.is_empty(), false);
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -124,6 +124,43 @@ protected:
ASSERT_EQ(counter, 10); 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> template <typename ArrayClass>
static void test_copy1(ArrayClass* a) { static void test_copy1(ArrayClass* a) {
ASSERT_EQ(a->length(), 1); ASSERT_EQ(a->length(), 1);
@ -200,7 +237,8 @@ protected:
enum TestEnum { enum TestEnum {
Append, Append,
Clear, Clear,
Iterator, Capacity,
Iterator
}; };
template <typename ArrayClass> template <typename ArrayClass>
@ -214,6 +252,10 @@ protected:
test_clear(a); test_clear(a);
break; break;
case Capacity:
test_capacity(a);
break;
case Iterator: case Iterator:
test_iterator(a); test_iterator(a);
break; break;
@ -402,6 +444,10 @@ TEST_VM_F(GrowableArrayTest, clear) {
with_all_types_all_0(Clear); with_all_types_all_0(Clear);
} }
TEST_VM_F(GrowableArrayTest, capacity) {
with_all_types_all_0(Capacity);
}
TEST_VM_F(GrowableArrayTest, iterator) { TEST_VM_F(GrowableArrayTest, iterator) {
with_all_types_all_0(Iterator); with_all_types_all_0(Iterator);
} }