mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8247755: Leaner and more versatile GrowableArray classes
Reviewed-by: kbarrett, coleenp
This commit is contained in:
parent
9d6aa42a7c
commit
ef3b0ec567
13 changed files with 1124 additions and 353 deletions
|
@ -347,6 +347,12 @@ class SymbolClosure : public StackObj {
|
|||
}
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
class CompareClosure : public Closure {
|
||||
public:
|
||||
virtual int do_compare(const E&, const E&) = 0;
|
||||
};
|
||||
|
||||
// Dispatches to the non-virtual functions if OopClosureType has
|
||||
// a concrete implementation, otherwise a virtual call is taken.
|
||||
class Devirtualizer {
|
||||
|
|
|
@ -456,10 +456,10 @@ void MetaspaceShared::post_initialize(TRAPS) {
|
|||
}
|
||||
}
|
||||
|
||||
static GrowableArray<Handle>* _extra_interned_strings = NULL;
|
||||
static GrowableArrayCHeap<Handle, mtClassShared>* _extra_interned_strings = NULL;
|
||||
|
||||
void MetaspaceShared::read_extra_data(const char* filename, TRAPS) {
|
||||
_extra_interned_strings = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray<Handle>(10000, mtClassShared);
|
||||
_extra_interned_strings = new GrowableArrayCHeap<Handle, mtClassShared>(10000);
|
||||
|
||||
HashtableTextDump reader(filename);
|
||||
reader.check_version("VERSION: 1.0");
|
||||
|
|
|
@ -3307,7 +3307,7 @@ class ScopedVMInitArgs : public StackObj {
|
|||
// allocated memory is deleted by the destructor. If this method
|
||||
// returns anything other than JNI_OK, then this object is in a
|
||||
// partially constructed state, and should be abandoned.
|
||||
jint set_args(GrowableArray<JavaVMOption>* options) {
|
||||
jint set_args(const GrowableArrayView<JavaVMOption>* options) {
|
||||
_is_set = true;
|
||||
JavaVMOption* options_arr = NEW_C_HEAP_ARRAY_RETURN_NULL(
|
||||
JavaVMOption, options->length(), mtArguments);
|
||||
|
@ -3365,23 +3365,21 @@ class ScopedVMInitArgs : public StackObj {
|
|||
assert(vm_options_file_pos != -1, "vm_options_file_pos should be set");
|
||||
|
||||
int length = args->nOptions + args_to_insert->nOptions - 1;
|
||||
GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtArguments)
|
||||
GrowableArray<JavaVMOption>(length, mtArguments); // Construct new option array
|
||||
// Construct new option array
|
||||
GrowableArrayCHeap<JavaVMOption, mtArguments> options(length);
|
||||
for (int i = 0; i < args->nOptions; i++) {
|
||||
if (i == vm_options_file_pos) {
|
||||
// insert the new options starting at the same place as the
|
||||
// -XX:VMOptionsFile option
|
||||
for (int j = 0; j < args_to_insert->nOptions; j++) {
|
||||
options->push(args_to_insert->options[j]);
|
||||
options.push(args_to_insert->options[j]);
|
||||
}
|
||||
} else {
|
||||
options->push(args->options[i]);
|
||||
options.push(args->options[i]);
|
||||
}
|
||||
}
|
||||
// make into options array
|
||||
jint result = set_args(options);
|
||||
delete options;
|
||||
return result;
|
||||
return set_args(&options);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3478,7 +3476,8 @@ jint Arguments::parse_vm_options_file(const char* file_name, ScopedVMInitArgs* v
|
|||
}
|
||||
|
||||
jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_t buf_len, ScopedVMInitArgs* vm_args) {
|
||||
GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtArguments) GrowableArray<JavaVMOption>(2, mtArguments); // Construct option array
|
||||
// Construct option array
|
||||
GrowableArrayCHeap<JavaVMOption, mtArguments> options(2);
|
||||
|
||||
// some pointers to help with parsing
|
||||
char *buffer_end = buffer + buf_len;
|
||||
|
@ -3518,7 +3517,6 @@ jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_
|
|||
// did not see closing quote
|
||||
jio_fprintf(defaultStream::error_stream(),
|
||||
"Unmatched quote in %s\n", name);
|
||||
delete options;
|
||||
return JNI_ERR;
|
||||
}
|
||||
} else {
|
||||
|
@ -3534,16 +3532,13 @@ jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_
|
|||
option.optionString = opt_hd;
|
||||
option.extraInfo = NULL;
|
||||
|
||||
options->append(option); // Fill in option
|
||||
options.append(option); // Fill in option
|
||||
|
||||
rd++; // Advance to next character
|
||||
}
|
||||
|
||||
// Fill out JavaVMInitArgs structure.
|
||||
jint status = vm_args->set_args(options);
|
||||
|
||||
delete options;
|
||||
return status;
|
||||
return vm_args->set_args(&options);
|
||||
}
|
||||
|
||||
jint Arguments::set_shared_spaces_flags_and_archive_paths() {
|
||||
|
|
|
@ -532,9 +532,8 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
|||
/* GrowableArrays */ \
|
||||
/*******************/ \
|
||||
\
|
||||
nonstatic_field(GenericGrowableArray, _len, int) \
|
||||
nonstatic_field(GenericGrowableArray, _max, int) \
|
||||
nonstatic_field(GenericGrowableArray, _arena, Arena*) \
|
||||
nonstatic_field(GrowableArrayBase, _len, int) \
|
||||
nonstatic_field(GrowableArrayBase, _max, int) \
|
||||
nonstatic_field(GrowableArray<int>, _data, int*) \
|
||||
\
|
||||
/********************************/ \
|
||||
|
@ -1339,7 +1338,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
|||
declare_toplevel_type(SystemDictionary) \
|
||||
declare_toplevel_type(vmSymbols) \
|
||||
\
|
||||
declare_toplevel_type(GenericGrowableArray) \
|
||||
declare_toplevel_type(GrowableArrayBase) \
|
||||
declare_toplevel_type(GrowableArray<int>) \
|
||||
declare_toplevel_type(Arena) \
|
||||
declare_type(ResourceArea, Arena) \
|
||||
|
|
|
@ -432,6 +432,11 @@ inline size_t pointer_delta(const MetaWord* left, const MetaWord* right) {
|
|||
#define CAST_TO_FN_PTR(func_type, value) (reinterpret_cast<func_type>(value))
|
||||
#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr)))
|
||||
|
||||
// Need the correct linkage to call qsort without warnings
|
||||
extern "C" {
|
||||
typedef int (*_sort_Fn)(const void *, const void *);
|
||||
}
|
||||
|
||||
// Unsigned byte types for os and stream.hpp
|
||||
|
||||
// Unsigned one, two, four and eigth byte quantities used for describing
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, 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
|
||||
|
@ -28,36 +28,65 @@
|
|||
#include "runtime/thread.inline.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
#ifdef ASSERT
|
||||
void GenericGrowableArray::set_nesting() {
|
||||
if (on_stack()) {
|
||||
_nesting = Thread::current()->resource_area()->nesting();
|
||||
}
|
||||
void* GrowableArrayResourceAllocator::allocate(int max, int elementSize) {
|
||||
assert(max >= 0, "integer overflow");
|
||||
size_t byte_size = elementSize * (size_t) max;
|
||||
|
||||
return (void*)resource_allocate_bytes(byte_size);
|
||||
}
|
||||
|
||||
void GenericGrowableArray::check_nesting() {
|
||||
void* GrowableArrayArenaAllocator::allocate(int max, int element_size, Arena* arena) {
|
||||
assert(max >= 0, "integer overflow");
|
||||
size_t byte_size = element_size * (size_t) max;
|
||||
|
||||
return arena->Amalloc(byte_size);
|
||||
}
|
||||
|
||||
void* GrowableArrayCHeapAllocator::allocate(int max, int element_size, MEMFLAGS memflags) {
|
||||
assert(max >= 0, "integer overflow");
|
||||
size_t byte_size = element_size * (size_t) max;
|
||||
|
||||
// memory type has to be specified for C heap allocation
|
||||
assert(memflags != mtNone, "memory type not specified for C heap object");
|
||||
return (void*)AllocateHeap(byte_size, memflags);
|
||||
}
|
||||
|
||||
void GrowableArrayCHeapAllocator::deallocate(void* elements) {
|
||||
FreeHeap(elements);
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
|
||||
GrowableArrayNestingCheck::GrowableArrayNestingCheck(bool on_stack) :
|
||||
_nesting(on_stack ? Thread::current()->resource_area()->nesting() : 0) {
|
||||
}
|
||||
|
||||
void GrowableArrayNestingCheck::on_stack_alloc() const {
|
||||
// Check for insidious allocation bug: if a GrowableArray overflows, the
|
||||
// grown array must be allocated under the same ResourceMark as the original.
|
||||
// Otherwise, the _data array will be deallocated too early.
|
||||
if (on_stack() &&
|
||||
_nesting != Thread::current()->resource_area()->nesting()) {
|
||||
if (_nesting != Thread::current()->resource_area()->nesting()) {
|
||||
fatal("allocation bug: GrowableArray could grow within nested ResourceMark");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void* GenericGrowableArray::raw_allocate(int elementSize) {
|
||||
assert(_max >= 0, "integer overflow");
|
||||
size_t byte_size = elementSize * (size_t) _max;
|
||||
if (on_stack()) {
|
||||
return (void*)resource_allocate_bytes(byte_size);
|
||||
} else if (on_C_heap()) {
|
||||
return (void*)AllocateHeap(byte_size, _memflags);
|
||||
} else {
|
||||
return _arena->Amalloc(byte_size);
|
||||
void GrowableArrayMetadata::init_checks(const GrowableArrayBase* array) const {
|
||||
// Stack allocated arrays support all three element allocation locations
|
||||
if (array->allocated_on_stack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise there's a strict one-to-one mapping
|
||||
assert(on_C_heap() == array->allocated_on_C_heap(),
|
||||
"growable array must be C heap allocated if elements are");
|
||||
assert(on_stack() == array->allocated_on_res_area(),
|
||||
"growable array must be resource allocated if elements are");
|
||||
assert(on_arena() == array->allocated_on_arena(),
|
||||
"growable array must be arena allocated if elements are");
|
||||
}
|
||||
|
||||
void GenericGrowableArray::free_C_heap(void* elements) {
|
||||
FreeHeap(elements);
|
||||
void GrowableArrayMetadata::on_stack_alloc_check() const {
|
||||
_nesting_check.on_stack_alloc();
|
||||
}
|
||||
|
||||
#endif // ASSERT
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2020, 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
|
||||
|
@ -26,7 +26,7 @@
|
|||
#define SHARE_UTILITIES_GROWABLEARRAY_HPP
|
||||
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/oop.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "utilities/debug.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/ostream.hpp"
|
||||
|
@ -39,7 +39,7 @@
|
|||
/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */
|
||||
/* */
|
||||
/* Should you use GrowableArrays to contain handles you must be certain */
|
||||
/* the the GrowableArray does not outlive the HandleMark that contains */
|
||||
/* that the GrowableArray does not outlive the HandleMark that contains */
|
||||
/* the handles. Since GrowableArrays are typically resource allocated */
|
||||
/* the following is an example of INCORRECT CODE, */
|
||||
/* */
|
||||
|
@ -59,174 +59,71 @@
|
|||
/* } */
|
||||
/* */
|
||||
/* If the GrowableArrays you are creating is C_Heap allocated then it */
|
||||
/* hould not old handles since the handles could trivially try and */
|
||||
/* should not hold handles since the handles could trivially try and */
|
||||
/* outlive their HandleMark. In some situations you might need to do */
|
||||
/* this and it would be legal but be very careful and see if you can do */
|
||||
/* the code in some other manner. */
|
||||
/* */
|
||||
/*************************************************************************/
|
||||
|
||||
// To call default constructor the placement operator new() is used.
|
||||
// It should be empty (it only returns the passed void* pointer).
|
||||
// The definition of placement operator new(size_t, void*) in the <new>.
|
||||
// Non-template base class responsible for handling the length and max.
|
||||
|
||||
#include <new>
|
||||
|
||||
// Need the correct linkage to call qsort without warnings
|
||||
extern "C" {
|
||||
typedef int (*_sort_Fn)(const void *, const void *);
|
||||
}
|
||||
|
||||
class GenericGrowableArray : public ResourceObj {
|
||||
class GrowableArrayBase : public ResourceObj {
|
||||
friend class VMStructs;
|
||||
|
||||
protected:
|
||||
int _len; // current length
|
||||
int _max; // maximum length
|
||||
Arena* _arena; // Indicates where allocation occurs:
|
||||
// 0 means default ResourceArea
|
||||
// 1 means on C heap
|
||||
// otherwise, allocate in _arena
|
||||
|
||||
MEMFLAGS _memflags; // memory type if allocation in C heap
|
||||
|
||||
#ifdef ASSERT
|
||||
int _nesting; // resource area nesting at creation
|
||||
void set_nesting();
|
||||
void check_nesting();
|
||||
#else
|
||||
#define set_nesting();
|
||||
#define check_nesting();
|
||||
#endif
|
||||
|
||||
// Where are we going to allocate memory?
|
||||
bool on_C_heap() { return _arena == (Arena*)1; }
|
||||
bool on_stack () { return _arena == NULL; }
|
||||
bool on_arena () { return _arena > (Arena*)1; }
|
||||
|
||||
// This GA will use the resource stack for storage if c_heap==false,
|
||||
// Else it will use the C heap. Use clear_and_deallocate to avoid leaks.
|
||||
GenericGrowableArray(int initial_size, int initial_len, MEMFLAGS flags) {
|
||||
_len = initial_len;
|
||||
_max = initial_size;
|
||||
_memflags = flags;
|
||||
protected:
|
||||
// Current number of accessible elements
|
||||
int _len;
|
||||
// Current number of allocated elements
|
||||
int _max;
|
||||
|
||||
GrowableArrayBase(int initial_max, int initial_len) :
|
||||
_len(initial_len),
|
||||
_max(initial_max) {
|
||||
assert(_len >= 0 && _len <= _max, "initial_len too big");
|
||||
|
||||
const bool c_heap = flags != mtNone;
|
||||
_arena = (c_heap ? (Arena*)1 : NULL);
|
||||
set_nesting();
|
||||
assert(!on_C_heap() || allocated_on_C_heap(), "growable array must be on C heap if elements are");
|
||||
assert(!on_stack() ||
|
||||
(allocated_on_res_area() || allocated_on_stack()),
|
||||
"growable array must be on stack if elements are not on arena and not on C heap");
|
||||
}
|
||||
|
||||
// This GA will use the given arena for storage.
|
||||
// Consider using new(arena) GrowableArray<T> to allocate the header.
|
||||
GenericGrowableArray(Arena* arena, int initial_size, int initial_len) {
|
||||
_len = initial_len;
|
||||
_max = initial_size;
|
||||
assert(_len >= 0 && _len <= _max, "initial_len too big");
|
||||
_arena = arena;
|
||||
_memflags = mtNone;
|
||||
|
||||
assert(on_arena(), "arena has taken on reserved value 0 or 1");
|
||||
// Relax next assert to allow object allocation on resource area,
|
||||
// on stack or embedded into an other object.
|
||||
assert(allocated_on_arena() || allocated_on_stack(),
|
||||
"growable array must be on arena or on stack if elements are on arena");
|
||||
}
|
||||
|
||||
void* raw_allocate(int elementSize);
|
||||
|
||||
void free_C_heap(void* elements);
|
||||
};
|
||||
|
||||
template<class E> class GrowableArrayIterator;
|
||||
template<class E, class UnaryPredicate> class GrowableArrayFilterIterator;
|
||||
|
||||
template<class E>
|
||||
class CompareClosure : public Closure {
|
||||
public:
|
||||
virtual int do_compare(const E&, const E&) = 0;
|
||||
};
|
||||
|
||||
template<class E> class GrowableArray : public GenericGrowableArray {
|
||||
friend class VMStructs;
|
||||
|
||||
private:
|
||||
E* _data; // data array
|
||||
|
||||
void grow(int j);
|
||||
void raw_at_put_grow(int i, const E& p, const E& fill);
|
||||
void clear_and_deallocate();
|
||||
~GrowableArrayBase() {}
|
||||
|
||||
public:
|
||||
GrowableArray(int initial_size, MEMFLAGS F = mtNone)
|
||||
: GenericGrowableArray(initial_size, 0, F) {
|
||||
_data = (E*)raw_allocate(sizeof(E));
|
||||
// Needed for Visual Studio 2012 and older
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(suppress: 4345)
|
||||
#endif
|
||||
for (int i = 0; i < _max; i++) ::new ((void*)&_data[i]) E();
|
||||
}
|
||||
|
||||
GrowableArray(int initial_size, int initial_len, const E& filler, MEMFLAGS memflags = mtNone)
|
||||
: GenericGrowableArray(initial_size, initial_len, memflags) {
|
||||
_data = (E*)raw_allocate(sizeof(E));
|
||||
int i = 0;
|
||||
for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler);
|
||||
for (; i < _max; i++) ::new ((void*)&_data[i]) E();
|
||||
}
|
||||
|
||||
// Watch out, if filler was generated by a constructor, the destuctor might
|
||||
// be called on the original object invalidating all the copies made here.
|
||||
// Carefully design the copy constructor.
|
||||
GrowableArray(Arena* arena, int initial_size, int initial_len, const E& filler) :
|
||||
GenericGrowableArray(arena, initial_size, initial_len) {
|
||||
_data = (E*)raw_allocate(sizeof(E));
|
||||
int i = 0;
|
||||
for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler);
|
||||
for (; i < _max; i++) ::new ((void*)&_data[i]) E();
|
||||
}
|
||||
|
||||
GrowableArray() : GenericGrowableArray(2, 0, mtNone) {
|
||||
_data = (E*)raw_allocate(sizeof(E));
|
||||
::new ((void*)&_data[0]) E();
|
||||
::new ((void*)&_data[1]) E();
|
||||
}
|
||||
|
||||
// Does nothing for resource and arena objects
|
||||
~GrowableArray() { if (on_C_heap()) clear_and_deallocate(); }
|
||||
|
||||
void clear() { _len = 0; }
|
||||
int length() const { return _len; }
|
||||
int max_length() const { return _max; }
|
||||
void trunc_to(int l) { assert(l <= _len,"cannot increase length"); _len = l; }
|
||||
|
||||
bool is_empty() const { return _len == 0; }
|
||||
bool is_nonempty() const { return _len != 0; }
|
||||
bool is_full() const { return _len == _max; }
|
||||
DEBUG_ONLY(E* data_addr() const { return _data; })
|
||||
|
||||
void print();
|
||||
|
||||
int append(const E& elem) {
|
||||
check_nesting();
|
||||
if (_len == _max) grow(_len);
|
||||
int idx = _len++;
|
||||
_data[idx] = elem;
|
||||
return idx;
|
||||
void clear() { _len = 0; }
|
||||
void trunc_to(int length) {
|
||||
assert(length <= _len,"cannot increase length");
|
||||
_len = length;
|
||||
}
|
||||
};
|
||||
|
||||
bool append_if_missing(const E& elem) {
|
||||
// Returns TRUE if elem is added.
|
||||
bool missed = !contains(elem);
|
||||
if (missed) append(elem);
|
||||
return missed;
|
||||
}
|
||||
template <typename E> class GrowableArrayIterator;
|
||||
template <typename E, typename UnaryPredicate> class GrowableArrayFilterIterator;
|
||||
|
||||
// Extends GrowableArrayBase with a typed data array.
|
||||
//
|
||||
// E: Element type
|
||||
//
|
||||
// The "view" adds function that don't grow or deallocate
|
||||
// the _data array, so there's no need for an allocator.
|
||||
//
|
||||
// The "view" can be used to type erase the allocator classes
|
||||
// of GrowableArrayWithAllocator.
|
||||
template <typename E>
|
||||
class GrowableArrayView : public GrowableArrayBase {
|
||||
protected:
|
||||
E* _data; // data array
|
||||
|
||||
GrowableArrayView<E>(E* data, int initial_max, int initial_len) :
|
||||
GrowableArrayBase(initial_max, initial_len), _data(data) {}
|
||||
|
||||
~GrowableArrayView() {}
|
||||
|
||||
public:
|
||||
E& at(int i) {
|
||||
assert(0 <= i && i < _len, "illegal index");
|
||||
return _data[i];
|
||||
|
@ -264,8 +161,6 @@ public:
|
|||
return GrowableArrayIterator<E>(this, length());
|
||||
}
|
||||
|
||||
void push(const E& elem) { append(elem); }
|
||||
|
||||
E pop() {
|
||||
assert(_len > 0, "empty list");
|
||||
return _data[--_len];
|
||||
|
@ -276,24 +171,6 @@ public:
|
|||
_data[i] = elem;
|
||||
}
|
||||
|
||||
E at_grow(int i, const E& fill = E()) {
|
||||
assert(0 <= i, "negative index");
|
||||
check_nesting();
|
||||
if (i >= _len) {
|
||||
if (i >= _max) grow(i);
|
||||
for (int j = _len; j <= i; j++)
|
||||
_data[j] = fill;
|
||||
_len = i+1;
|
||||
}
|
||||
return _data[i];
|
||||
}
|
||||
|
||||
void at_put_grow(int i, const E& elem, const E& fill = E()) {
|
||||
assert(0 <= i, "negative index");
|
||||
check_nesting();
|
||||
raw_at_put_grow(i, elem, fill);
|
||||
}
|
||||
|
||||
bool contains(const E& elem) const {
|
||||
for (int i = 0; i < _len; i++) {
|
||||
if (_data[i] == elem) return true;
|
||||
|
@ -357,63 +234,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// inserts the given element before the element at index i
|
||||
void insert_before(const int idx, const E& elem) {
|
||||
assert(0 <= idx && idx <= _len, "illegal index");
|
||||
check_nesting();
|
||||
if (_len == _max) grow(_len);
|
||||
for (int j = _len - 1; j >= idx; j--) {
|
||||
_data[j + 1] = _data[j];
|
||||
}
|
||||
_len++;
|
||||
_data[idx] = elem;
|
||||
}
|
||||
|
||||
void insert_before(const int idx, const GrowableArray<E>* array) {
|
||||
assert(0 <= idx && idx <= _len, "illegal index");
|
||||
check_nesting();
|
||||
int array_len = array->length();
|
||||
int new_len = _len + array_len;
|
||||
if (new_len >= _max) grow(new_len);
|
||||
|
||||
for (int j = _len - 1; j >= idx; j--) {
|
||||
_data[j + array_len] = _data[j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < array_len; j++) {
|
||||
_data[idx + j] = array->_data[j];
|
||||
}
|
||||
|
||||
_len += array_len;
|
||||
}
|
||||
|
||||
void appendAll(const GrowableArray<E>* l) {
|
||||
for (int i = 0; i < l->_len; i++) {
|
||||
raw_at_put_grow(_len, l->_data[i], E());
|
||||
}
|
||||
}
|
||||
|
||||
void sort(int f(E*,E*)) {
|
||||
void sort(int f(E*, E*)) {
|
||||
qsort(_data, length(), sizeof(E), (_sort_Fn)f);
|
||||
}
|
||||
// sort by fixed-stride sub arrays:
|
||||
void sort(int f(E*,E*), int stride) {
|
||||
void sort(int f(E*, E*), int stride) {
|
||||
qsort(_data, length() / stride, sizeof(E) * stride, (_sort_Fn)f);
|
||||
}
|
||||
|
||||
// Binary search and insertion utility. Search array for element
|
||||
// matching key according to the static compare function. Insert
|
||||
// that element is not already in the list. Assumes the list is
|
||||
// already sorted according to compare function.
|
||||
template <int compare(const E&, const E&)> E insert_sorted(const E& key) {
|
||||
bool found;
|
||||
int location = find_sorted<E, compare>(key, found);
|
||||
if (!found) {
|
||||
insert_before(location, key);
|
||||
}
|
||||
return at(location);
|
||||
}
|
||||
|
||||
template <typename K, int compare(const K&, const E&)> int find_sorted(const K& key, bool& found) {
|
||||
found = false;
|
||||
int min = 0;
|
||||
|
@ -435,16 +263,7 @@ public:
|
|||
return min;
|
||||
}
|
||||
|
||||
E insert_sorted(CompareClosure<E>* cc, const E& key) {
|
||||
bool found;
|
||||
int location = find_sorted(cc, key, found);
|
||||
if (!found) {
|
||||
insert_before(location, key);
|
||||
}
|
||||
return at(location);
|
||||
}
|
||||
|
||||
template<typename K>
|
||||
template <typename K>
|
||||
int find_sorted(CompareClosure<E>* cc, const K& key, bool& found) {
|
||||
found = false;
|
||||
int min = 0;
|
||||
|
@ -465,80 +284,442 @@ public:
|
|||
}
|
||||
return min;
|
||||
}
|
||||
};
|
||||
|
||||
// Global GrowableArray methods (one instance in the library per each 'E' type).
|
||||
|
||||
template<class E> void GrowableArray<E>::grow(int j) {
|
||||
int old_max = _max;
|
||||
// grow the array by increasing _max to the first power of two larger than the size we need
|
||||
_max = next_power_of_2((uint32_t)j);
|
||||
// j < _max
|
||||
E* newData = (E*)raw_allocate(sizeof(E));
|
||||
int i = 0;
|
||||
for ( ; i < _len; i++) ::new ((void*)&newData[i]) E(_data[i]);
|
||||
// Needed for Visual Studio 2012 and older
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(suppress: 4345)
|
||||
#endif
|
||||
for ( ; i < _max; i++) ::new ((void*)&newData[i]) E();
|
||||
for (i = 0; i < old_max; i++) _data[i].~E();
|
||||
if (on_C_heap() && _data != NULL) {
|
||||
free_C_heap(_data);
|
||||
}
|
||||
_data = newData;
|
||||
}
|
||||
|
||||
template<class E> void GrowableArray<E>::raw_at_put_grow(int i, const E& p, const E& fill) {
|
||||
if (i >= _len) {
|
||||
if (i >= _max) grow(i);
|
||||
for (int j = _len; j < i; j++)
|
||||
_data[j] = fill;
|
||||
_len = i+1;
|
||||
}
|
||||
_data[i] = p;
|
||||
}
|
||||
|
||||
// This function clears and deallocate the data in the growable array that
|
||||
// has been allocated on the C heap. It's not public - called by the
|
||||
// destructor.
|
||||
template<class E> void GrowableArray<E>::clear_and_deallocate() {
|
||||
assert(on_C_heap(),
|
||||
"clear_and_deallocate should only be called when on C heap");
|
||||
clear();
|
||||
if (_data != NULL) {
|
||||
for (int i = 0; i < _max; i++) _data[i].~E();
|
||||
free_C_heap(_data);
|
||||
_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
template<class E> void GrowableArray<E>::print() {
|
||||
void print() {
|
||||
tty->print("Growable Array " INTPTR_FORMAT, this);
|
||||
tty->print(": length %ld (_max %ld) { ", _len, _max);
|
||||
for (int i = 0; i < _len; i++) tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i]));
|
||||
for (int i = 0; i < _len; i++) {
|
||||
tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i]));
|
||||
}
|
||||
tty->print("}\n");
|
||||
}
|
||||
};
|
||||
|
||||
// GrowableArrayWithAllocator extends the "view" with
|
||||
// the capability to grow and deallocate the data array.
|
||||
//
|
||||
// The allocator responsibility is delegated to the sub-class.
|
||||
//
|
||||
// Derived: The sub-class responsible for allocation / deallocation
|
||||
// - E* Derived::allocate() - member function responsible for allocation
|
||||
// - void Derived::deallocate(E*) - member function responsible for deallocation
|
||||
template <typename E, typename Derived>
|
||||
class GrowableArrayWithAllocator : public GrowableArrayView<E> {
|
||||
friend class VMStructs;
|
||||
|
||||
void grow(int j);
|
||||
|
||||
protected:
|
||||
GrowableArrayWithAllocator(E* data, int initial_max) :
|
||||
GrowableArrayView<E>(data, initial_max, 0) {
|
||||
for (int i = 0; i < initial_max; i++) {
|
||||
::new ((void*)&data[i]) E();
|
||||
}
|
||||
}
|
||||
|
||||
GrowableArrayWithAllocator(E* data, int initial_max, int initial_len, const E& filler) :
|
||||
GrowableArrayView<E>(data, initial_max, initial_len) {
|
||||
int i = 0;
|
||||
for (; i < initial_len; i++) {
|
||||
::new ((void*)&data[i]) E(filler);
|
||||
}
|
||||
for (; i < initial_max; i++) {
|
||||
::new ((void*)&data[i]) E();
|
||||
}
|
||||
}
|
||||
|
||||
~GrowableArrayWithAllocator() {}
|
||||
|
||||
public:
|
||||
int append(const E& elem) {
|
||||
if (this->_len == this->_max) grow(this->_len);
|
||||
int idx = this->_len++;
|
||||
this->_data[idx] = elem;
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool append_if_missing(const E& elem) {
|
||||
// Returns TRUE if elem is added.
|
||||
bool missed = !this->contains(elem);
|
||||
if (missed) append(elem);
|
||||
return missed;
|
||||
}
|
||||
|
||||
void push(const E& elem) { append(elem); }
|
||||
|
||||
E at_grow(int i, const E& fill = E()) {
|
||||
assert(0 <= i, "negative index");
|
||||
if (i >= this->_len) {
|
||||
if (i >= this->_max) grow(i);
|
||||
for (int j = this->_len; j <= i; j++)
|
||||
this->_data[j] = fill;
|
||||
this->_len = i+1;
|
||||
}
|
||||
return this->_data[i];
|
||||
}
|
||||
|
||||
void at_put_grow(int i, const E& elem, const E& fill = E()) {
|
||||
assert(0 <= i, "negative index");
|
||||
if (i >= this->_len) {
|
||||
if (i >= this->_max) grow(i);
|
||||
for (int j = this->_len; j < i; j++)
|
||||
this->_data[j] = fill;
|
||||
this->_len = i+1;
|
||||
}
|
||||
this->_data[i] = elem;
|
||||
}
|
||||
|
||||
// inserts the given element before the element at index i
|
||||
void insert_before(const int idx, const E& elem) {
|
||||
assert(0 <= idx && idx <= this->_len, "illegal index");
|
||||
if (this->_len == this->_max) grow(this->_len);
|
||||
for (int j = this->_len - 1; j >= idx; j--) {
|
||||
this->_data[j + 1] = this->_data[j];
|
||||
}
|
||||
this->_len++;
|
||||
this->_data[idx] = elem;
|
||||
}
|
||||
|
||||
void insert_before(const int idx, const GrowableArrayView<E>* array) {
|
||||
assert(0 <= idx && idx <= this->_len, "illegal index");
|
||||
int array_len = array->length();
|
||||
int new_len = this->_len + array_len;
|
||||
if (new_len >= this->_max) grow(new_len);
|
||||
|
||||
for (int j = this->_len - 1; j >= idx; j--) {
|
||||
this->_data[j + array_len] = this->_data[j];
|
||||
}
|
||||
|
||||
for (int j = 0; j < array_len; j++) {
|
||||
this->_data[idx + j] = array->at(j);
|
||||
}
|
||||
|
||||
this->_len += array_len;
|
||||
}
|
||||
|
||||
void appendAll(const GrowableArrayView<E>* l) {
|
||||
for (int i = 0; i < l->length(); i++) {
|
||||
this->at_put_grow(this->_len, l->at(i), E());
|
||||
}
|
||||
}
|
||||
|
||||
// Binary search and insertion utility. Search array for element
|
||||
// matching key according to the static compare function. Insert
|
||||
// that element is not already in the list. Assumes the list is
|
||||
// already sorted according to compare function.
|
||||
template <int compare(const E&, const E&)> E insert_sorted(const E& key) {
|
||||
bool found;
|
||||
int location = GrowableArrayView<E>::template find_sorted<E, compare>(key, found);
|
||||
if (!found) {
|
||||
insert_before(location, key);
|
||||
}
|
||||
return this->at(location);
|
||||
}
|
||||
|
||||
E insert_sorted(CompareClosure<E>* cc, const E& key) {
|
||||
bool found;
|
||||
int location = find_sorted(cc, key, found);
|
||||
if (!found) {
|
||||
insert_before(location, key);
|
||||
}
|
||||
return this->at(location);
|
||||
}
|
||||
|
||||
void clear_and_deallocate();
|
||||
};
|
||||
|
||||
template <typename E, typename Derived>
|
||||
void GrowableArrayWithAllocator<E, Derived>::grow(int j) {
|
||||
int old_max = this->_max;
|
||||
// grow the array by increasing _max to the first power of two larger than the size we need
|
||||
this->_max = next_power_of_2((uint32_t)j);
|
||||
// j < _max
|
||||
E* newData = static_cast<Derived*>(this)->allocate();
|
||||
int i = 0;
|
||||
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 = 0; i < old_max; i++) this->_data[i].~E();
|
||||
if (this->_data != NULL) {
|
||||
static_cast<Derived*>(this)->deallocate(this->_data);
|
||||
}
|
||||
this->_data = newData;
|
||||
}
|
||||
|
||||
template <typename E, typename Derived>
|
||||
void GrowableArrayWithAllocator<E, Derived>::clear_and_deallocate() {
|
||||
if (this->_data != NULL) {
|
||||
for (int i = 0; i < this->_max; i++) {
|
||||
this->_data[i].~E();
|
||||
}
|
||||
static_cast<Derived*>(this)->deallocate(this->_data);
|
||||
this->_data = NULL;
|
||||
}
|
||||
this->_len = 0;
|
||||
this->_max = 0;
|
||||
}
|
||||
|
||||
class GrowableArrayResourceAllocator {
|
||||
public:
|
||||
static void* allocate(int max, int element_size);
|
||||
};
|
||||
|
||||
// Arena allocator
|
||||
class GrowableArrayArenaAllocator {
|
||||
public:
|
||||
static void* allocate(int max, int element_size, Arena* arena);
|
||||
};
|
||||
|
||||
// CHeap allocator
|
||||
class GrowableArrayCHeapAllocator {
|
||||
public:
|
||||
static void* allocate(int max, int element_size, MEMFLAGS memflags);
|
||||
static void deallocate(void* mem);
|
||||
};
|
||||
|
||||
#ifdef ASSERT
|
||||
|
||||
// Checks resource allocation nesting
|
||||
class GrowableArrayNestingCheck {
|
||||
// resource area nesting at creation
|
||||
int _nesting;
|
||||
|
||||
public:
|
||||
GrowableArrayNestingCheck(bool on_stack);
|
||||
|
||||
void on_stack_alloc() const;
|
||||
};
|
||||
|
||||
#endif // ASSERT
|
||||
|
||||
// Encodes where the backing array is allocated
|
||||
// and performs necessary checks.
|
||||
class GrowableArrayMetadata {
|
||||
uintptr_t _bits;
|
||||
|
||||
// resource area nesting at creation
|
||||
debug_only(GrowableArrayNestingCheck _nesting_check;)
|
||||
|
||||
uintptr_t bits(MEMFLAGS memflags) const {
|
||||
if (memflags == mtNone) {
|
||||
// Stack allocation
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CHeap allocation
|
||||
return (uintptr_t(memflags) << 1) | 1;
|
||||
}
|
||||
|
||||
uintptr_t bits(Arena* arena) const {
|
||||
return uintptr_t(arena);
|
||||
}
|
||||
|
||||
public:
|
||||
GrowableArrayMetadata(Arena* arena) :
|
||||
_bits(bits(arena))
|
||||
debug_only(COMMA _nesting_check(on_stack())) {
|
||||
}
|
||||
|
||||
GrowableArrayMetadata(MEMFLAGS memflags) :
|
||||
_bits(bits(memflags))
|
||||
debug_only(COMMA _nesting_check(on_stack())) {
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
GrowableArrayMetadata(const GrowableArrayMetadata& other) :
|
||||
_bits(other._bits),
|
||||
_nesting_check(other._nesting_check) {
|
||||
assert(!on_C_heap(), "Copying of CHeap arrays not supported");
|
||||
assert(!other.on_C_heap(), "Copying of CHeap arrays not supported");
|
||||
}
|
||||
|
||||
GrowableArrayMetadata& operator=(const GrowableArrayMetadata& other) {
|
||||
_bits = other._bits;
|
||||
_nesting_check = other._nesting_check;
|
||||
assert(!on_C_heap(), "Assignment of CHeap arrays not supported");
|
||||
assert(!other.on_C_heap(), "Assignment of CHeap arrays not supported");
|
||||
return *this;
|
||||
}
|
||||
|
||||
void init_checks(const GrowableArrayBase* array) const;
|
||||
void on_stack_alloc_check() const;
|
||||
#endif // ASSERT
|
||||
|
||||
bool on_C_heap() const { return (_bits & 1) == 1; }
|
||||
bool on_stack () const { return _bits == 0; }
|
||||
bool on_arena () const { return (_bits & 1) == 0 && _bits != 0; }
|
||||
|
||||
Arena* arena() const { return (Arena*)_bits; }
|
||||
MEMFLAGS memflags() const { return MEMFLAGS(_bits >> 1); }
|
||||
};
|
||||
|
||||
// THE GrowableArray.
|
||||
//
|
||||
// Supports multiple allocation strategies:
|
||||
// - Resource stack allocation: if memflags == mtNone
|
||||
// - CHeap allocation: if memflags != mtNone
|
||||
// - Arena allocation: if an arena is provided
|
||||
//
|
||||
// There are some drawbacks of using GrowableArray, that are removed in some
|
||||
// of the other implementations of GrowableArrayWithAllocator sub-classes:
|
||||
//
|
||||
// Memory overhead: The multiple allocation strategies uses extra metadata
|
||||
// embedded in the instance.
|
||||
//
|
||||
// Strict allocation locations: There are rules about where the GrowableArray
|
||||
// instance is allocated, that depends on where the data array is allocated.
|
||||
// See: init_checks.
|
||||
|
||||
template <typename E>
|
||||
class GrowableArray : public GrowableArrayWithAllocator<E, GrowableArray<E> > {
|
||||
friend class GrowableArrayWithAllocator<E, GrowableArray<E> >;
|
||||
friend class GrowableArrayTest;
|
||||
|
||||
static E* allocate(int max) {
|
||||
return (E*)GrowableArrayResourceAllocator::allocate(max, sizeof(E));
|
||||
}
|
||||
|
||||
static E* allocate(int max, MEMFLAGS memflags) {
|
||||
if (memflags != mtNone) {
|
||||
return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), memflags);
|
||||
}
|
||||
|
||||
return (E*)GrowableArrayResourceAllocator::allocate(max, sizeof(E));
|
||||
}
|
||||
|
||||
static E* allocate(int max, Arena* arena) {
|
||||
return (E*)GrowableArrayArenaAllocator::allocate(max, sizeof(E), arena);
|
||||
}
|
||||
|
||||
GrowableArrayMetadata _metadata;
|
||||
|
||||
void init_checks() const { debug_only(_metadata.init_checks(this);) }
|
||||
|
||||
// Where are we going to allocate memory?
|
||||
bool on_C_heap() const { return _metadata.on_C_heap(); }
|
||||
bool on_stack () const { return _metadata.on_stack(); }
|
||||
bool on_arena () const { return _metadata.on_arena(); }
|
||||
|
||||
E* allocate() {
|
||||
if (on_stack()) {
|
||||
debug_only(_metadata.on_stack_alloc_check());
|
||||
return allocate(this->_max);
|
||||
}
|
||||
|
||||
if (on_C_heap()) {
|
||||
return allocate(this->_max, _metadata.memflags());
|
||||
}
|
||||
|
||||
assert(on_arena(), "Sanity");
|
||||
return allocate(this->_max, _metadata.arena());
|
||||
}
|
||||
|
||||
void deallocate(E* mem) {
|
||||
if (on_C_heap()) {
|
||||
GrowableArrayCHeapAllocator::deallocate(mem);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
GrowableArray(int initial_max = 2, MEMFLAGS memflags = mtNone) :
|
||||
GrowableArrayWithAllocator<E, GrowableArray<E> >(
|
||||
allocate(initial_max, memflags),
|
||||
initial_max),
|
||||
_metadata(memflags) {
|
||||
init_checks();
|
||||
}
|
||||
|
||||
GrowableArray(int initial_max, int initial_len, const E& filler, MEMFLAGS memflags = mtNone) :
|
||||
GrowableArrayWithAllocator<E, GrowableArray<E> >(
|
||||
allocate(initial_max, memflags),
|
||||
initial_max, initial_len, filler),
|
||||
_metadata(memflags) {
|
||||
init_checks();
|
||||
}
|
||||
|
||||
GrowableArray(Arena* arena, int initial_max, int initial_len, const E& filler) :
|
||||
GrowableArrayWithAllocator<E, GrowableArray<E> >(
|
||||
allocate(initial_max, arena),
|
||||
initial_max, initial_len, filler),
|
||||
_metadata(arena) {
|
||||
init_checks();
|
||||
}
|
||||
|
||||
~GrowableArray() {
|
||||
if (on_C_heap()) {
|
||||
this->clear_and_deallocate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Leaner GrowableArray for CHeap backed data arrays, with compile-time decided MEMFLAGS.
|
||||
template <typename E, MEMFLAGS F>
|
||||
class GrowableArrayCHeap : public GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> > {
|
||||
friend class GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >;
|
||||
|
||||
STATIC_ASSERT(F != mtNone);
|
||||
|
||||
static E* allocate(int max, MEMFLAGS flags) {
|
||||
if (max == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), flags);
|
||||
}
|
||||
|
||||
NONCOPYABLE(GrowableArrayCHeap);
|
||||
|
||||
E* allocate() {
|
||||
return allocate(this->_max, F);
|
||||
}
|
||||
|
||||
void deallocate(E* mem) {
|
||||
GrowableArrayCHeapAllocator::deallocate(mem);
|
||||
}
|
||||
|
||||
public:
|
||||
GrowableArrayCHeap(int initial_max) :
|
||||
GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >(
|
||||
allocate(initial_max, F),
|
||||
initial_max) {}
|
||||
|
||||
GrowableArrayCHeap(int initial_max, int initial_len, const E& filler) :
|
||||
GrowableArrayWithAllocator<E, GrowableArrayCHeap<E, F> >(
|
||||
allocate(initial_max, F),
|
||||
initial_max, initial_len, filler) {}
|
||||
|
||||
~GrowableArrayCHeap() {
|
||||
this->clear_and_deallocate();
|
||||
}
|
||||
|
||||
void* operator new(size_t size) throw() {
|
||||
return ResourceObj::operator new(size, ResourceObj::C_HEAP, F);
|
||||
}
|
||||
|
||||
void* operator new(size_t size, const std::nothrow_t& nothrow_constant) throw() {
|
||||
return ResourceObj::operator new(size, nothrow_constant, ResourceObj::C_HEAP, F);
|
||||
}
|
||||
};
|
||||
|
||||
// Custom STL-style iterator to iterate over GrowableArrays
|
||||
// It is constructed by invoking GrowableArray::begin() and GrowableArray::end()
|
||||
template<class E> class GrowableArrayIterator : public StackObj {
|
||||
friend class GrowableArray<E>;
|
||||
template<class F, class UnaryPredicate> friend class GrowableArrayFilterIterator;
|
||||
template <typename E>
|
||||
class GrowableArrayIterator : public StackObj {
|
||||
friend class GrowableArrayView<E>;
|
||||
template <typename F, typename UnaryPredicate> friend class GrowableArrayFilterIterator;
|
||||
|
||||
private:
|
||||
const GrowableArray<E>* _array; // GrowableArray we iterate over
|
||||
int _position; // The current position in the GrowableArray
|
||||
const GrowableArrayView<E>* _array; // GrowableArray we iterate over
|
||||
int _position; // The current position in the GrowableArray
|
||||
|
||||
// Private constructor used in GrowableArray::begin() and GrowableArray::end()
|
||||
GrowableArrayIterator(const GrowableArray<E>* array, int position) : _array(array), _position(position) {
|
||||
GrowableArrayIterator(const GrowableArrayView<E>* array, int position) : _array(array), _position(position) {
|
||||
assert(0 <= position && position <= _array->length(), "illegal position");
|
||||
}
|
||||
|
||||
public:
|
||||
GrowableArrayIterator() : _array(NULL), _position(0) { }
|
||||
GrowableArrayIterator<E>& operator++() { ++_position; return *this; }
|
||||
E operator*() { return _array->at(_position); }
|
||||
GrowableArrayIterator<E>& operator++() { ++_position; return *this; }
|
||||
E operator*() { return _array->at(_position); }
|
||||
|
||||
bool operator==(const GrowableArrayIterator<E>& rhs) {
|
||||
assert(_array == rhs._array, "iterator belongs to different array");
|
||||
|
@ -552,17 +733,18 @@ template<class E> class GrowableArrayIterator : public StackObj {
|
|||
};
|
||||
|
||||
// Custom STL-style iterator to iterate over elements of a GrowableArray that satisfy a given predicate
|
||||
template<class E, class UnaryPredicate> class GrowableArrayFilterIterator : public StackObj {
|
||||
friend class GrowableArray<E>;
|
||||
template <typename E, class UnaryPredicate>
|
||||
class GrowableArrayFilterIterator : public StackObj {
|
||||
friend class GrowableArrayView<E>;
|
||||
|
||||
private:
|
||||
const GrowableArray<E>* _array; // GrowableArray we iterate over
|
||||
int _position; // Current position in the GrowableArray
|
||||
UnaryPredicate _predicate; // Unary predicate the elements of the GrowableArray should satisfy
|
||||
const GrowableArrayView<E>* _array; // GrowableArray we iterate over
|
||||
int _position; // Current position in the GrowableArray
|
||||
UnaryPredicate _predicate; // Unary predicate the elements of the GrowableArray should satisfy
|
||||
|
||||
public:
|
||||
GrowableArrayFilterIterator(const GrowableArrayIterator<E>& begin, UnaryPredicate filter_predicate)
|
||||
: _array(begin._array), _position(begin._position), _predicate(filter_predicate) {
|
||||
GrowableArrayFilterIterator(const GrowableArrayIterator<E>& begin, UnaryPredicate filter_predicate) :
|
||||
_array(begin._array), _position(begin._position), _predicate(filter_predicate) {
|
||||
// Advance to first element satisfying the predicate
|
||||
while(_position != _array->length() && !_predicate(_array->at(_position))) {
|
||||
++_position;
|
||||
|
@ -577,7 +759,7 @@ template<class E, class UnaryPredicate> class GrowableArrayFilterIterator : publ
|
|||
return *this;
|
||||
}
|
||||
|
||||
E operator*() { return _array->at(_position); }
|
||||
E operator*() { return _array->at(_position); }
|
||||
|
||||
bool operator==(const GrowableArrayIterator<E>& rhs) {
|
||||
assert(_array == rhs._array, "iterator belongs to different array");
|
||||
|
|
|
@ -67,7 +67,7 @@ template <MEMFLAGS F> BasicHashtableEntry<F>* BasicHashtable<F>::new_entry(unsig
|
|||
len = 1 << log2_int(len); // round down to power of 2
|
||||
assert(len >= _entry_size, "");
|
||||
_first_free_entry = NEW_C_HEAP_ARRAY2(char, len, F, CURRENT_PC);
|
||||
_entry_blocks->append(_first_free_entry);
|
||||
_entry_blocks.append(_first_free_entry);
|
||||
_end_block = _first_free_entry + len;
|
||||
}
|
||||
entry = (BasicHashtableEntry<F>*)_first_free_entry;
|
||||
|
|
|
@ -158,14 +158,14 @@ public:
|
|||
|
||||
private:
|
||||
// Instance variables
|
||||
int _table_size;
|
||||
HashtableBucket<F>* _buckets;
|
||||
int _table_size;
|
||||
HashtableBucket<F>* _buckets;
|
||||
BasicHashtableEntry<F>* volatile _free_list;
|
||||
char* _first_free_entry;
|
||||
char* _end_block;
|
||||
int _entry_size;
|
||||
volatile int _number_of_entries;
|
||||
GrowableArray<char*>* _entry_blocks;
|
||||
char* _first_free_entry;
|
||||
char* _end_block;
|
||||
int _entry_size;
|
||||
volatile int _number_of_entries;
|
||||
GrowableArrayCHeap<char*, F> _entry_blocks;
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@
|
|||
|
||||
// Initialize a table.
|
||||
|
||||
template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, int entry_size) {
|
||||
template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, int entry_size) :
|
||||
_entry_blocks(4) {
|
||||
// Called on startup, no locking needed
|
||||
initialize(table_size, entry_size, 0);
|
||||
_buckets = NEW_C_HEAP_ARRAY2(HashtableBucket<F>, table_size, F, CURRENT_PC);
|
||||
|
@ -49,7 +50,8 @@ template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, i
|
|||
|
||||
template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, int entry_size,
|
||||
HashtableBucket<F>* buckets,
|
||||
int number_of_entries) {
|
||||
int number_of_entries) :
|
||||
_entry_blocks(4) {
|
||||
// Called on startup, no locking needed
|
||||
initialize(table_size, entry_size, number_of_entries);
|
||||
_buckets = buckets;
|
||||
|
@ -57,10 +59,9 @@ template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, i
|
|||
}
|
||||
|
||||
template <MEMFLAGS F> inline BasicHashtable<F>::~BasicHashtable() {
|
||||
for (int i = 0; i < _entry_blocks->length(); i++) {
|
||||
FREE_C_HEAP_ARRAY(char, _entry_blocks->at(i));
|
||||
for (int i = 0; i < _entry_blocks.length(); i++) {
|
||||
FREE_C_HEAP_ARRAY(char, _entry_blocks.at(i));
|
||||
}
|
||||
delete _entry_blocks;
|
||||
free_buckets();
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,6 @@ template <MEMFLAGS F> inline void BasicHashtable<F>::initialize(int table_size,
|
|||
_first_free_entry = NULL;
|
||||
_end_block = NULL;
|
||||
_number_of_entries = number_of_entries;
|
||||
_entry_blocks = new(ResourceObj::C_HEAP, F) GrowableArray<char*>(4, F);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -116,8 +116,8 @@ public class HotSpotTypeDataBase extends BasicTypeDataBase {
|
|||
|
||||
BasicType basicTargetType = createBasicType(cTypeName, false, false, false);
|
||||
|
||||
// transfer fields from GenericGrowableArray to template instance
|
||||
BasicType generic = lookupOrFail("GenericGrowableArray");
|
||||
// transfer fields from GrowableArrayBase to template instance
|
||||
BasicType generic = lookupOrFail("GrowableArrayBase");
|
||||
BasicType specific = lookupOrFail("GrowableArray<int>");
|
||||
basicTargetType.setSize(specific.getSize());
|
||||
Iterator fields = generic.getFields();
|
||||
|
|
|
@ -42,13 +42,11 @@ public class GenericGrowableArray extends VMObject {
|
|||
}
|
||||
|
||||
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
|
||||
Type type = db.lookupType("GenericGrowableArray");
|
||||
_arena_field = type.getAddressField("_arena");
|
||||
Type type = db.lookupType("GrowableArrayBase");
|
||||
_max_field = new CIntField(type.getCIntegerField("_max"), 0);
|
||||
_len_field = new CIntField(type.getCIntegerField("_len"), 0);
|
||||
}
|
||||
|
||||
private static AddressField _arena_field;
|
||||
private static CIntField _max_field;
|
||||
private static CIntField _len_field;
|
||||
|
||||
|
|
557
test/hotspot/gtest/utilities/test_growableArray.cpp
Normal file
557
test/hotspot/gtest/utilities/test_growableArray.cpp
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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, MEMFLAGS memflags) : _a(initial_max, memflags) {
|
||||
assert(memflags != 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_stack(const GrowableArray<E>* array) {
|
||||
return array->on_stack();
|
||||
}
|
||||
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_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,
|
||||
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 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 (ResourceObj::C_HEAP, 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, 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_stack(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 (ResourceObj::C_HEAP, 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());
|
||||
ASSERT_TRUE(elements_on_stack(&a));
|
||||
}
|
||||
|
||||
// Stack/CHeap allocated
|
||||
{
|
||||
GrowableArray<int> a(0, mtTest);
|
||||
ASSERT_TRUE(a.allocated_on_stack());
|
||||
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());
|
||||
ASSERT_TRUE(elements_on_arena(&a));
|
||||
}
|
||||
|
||||
// Embedded/Resource allocated
|
||||
{
|
||||
ResourceMark rm;
|
||||
WithEmbeddedArray w(0);
|
||||
ASSERT_TRUE(w._a.allocated_on_stack());
|
||||
ASSERT_TRUE(elements_on_stack(&w._a));
|
||||
}
|
||||
|
||||
// Embedded/CHeap allocated
|
||||
{
|
||||
WithEmbeddedArray w(0, mtTest);
|
||||
ASSERT_TRUE(w._a.allocated_on_stack());
|
||||
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());
|
||||
ASSERT_TRUE(elements_on_arena(&w._a));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_VM_ASSERT_MSG(GrowableArrayAssertingTest, copy_with_embedded_cheap,
|
||||
"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,
|
||||
"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());
|
||||
#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;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue