mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
9bb3d6b972
git-subtree-dir: sgx-jvm/avian git-subtree-mainline:f978eab8d1
git-subtree-split:09e4fe60d0
605 lines
15 KiB
C++
605 lines
15 KiB
C++
/* Copyright (c) 2008-2015, Avian Contributors
|
|
|
|
Permission to use, copy, modify, and/or distribute this software
|
|
for any purpose with or without fee is hereby granted, provided
|
|
that the above copyright notice and this permission notice appear
|
|
in all copies.
|
|
|
|
There is NO WARRANTY for this software. See license.txt for
|
|
details. */
|
|
|
|
#include "avian/util.h"
|
|
#include <avian/util/list.h>
|
|
|
|
using namespace vm;
|
|
|
|
namespace {
|
|
|
|
class TreeContext {
|
|
public:
|
|
class MyProtector : public Thread::Protector {
|
|
public:
|
|
MyProtector(Thread* thread, TreeContext* context)
|
|
: Protector(thread), context(context)
|
|
{
|
|
}
|
|
|
|
virtual void visit(Heap::Visitor* v)
|
|
{
|
|
v->visit(&(context->root));
|
|
v->visit(&(context->node));
|
|
|
|
for (List<GcTreeNode*>* p = context->ancestors; p; p = p->next) {
|
|
v->visit(&(p->item));
|
|
}
|
|
}
|
|
|
|
TreeContext* context;
|
|
};
|
|
|
|
TreeContext(Thread* thread, Zone* zone)
|
|
: zone(zone),
|
|
root(0),
|
|
node(0),
|
|
ancestors(0),
|
|
protector(thread, this),
|
|
fresh(false)
|
|
{
|
|
}
|
|
|
|
Zone* zone;
|
|
GcTreeNode* root;
|
|
GcTreeNode* node;
|
|
List<GcTreeNode*>* ancestors;
|
|
MyProtector protector;
|
|
bool fresh;
|
|
};
|
|
|
|
List<GcTreeNode*>* path(TreeContext* c,
|
|
GcTreeNode* node,
|
|
List<GcTreeNode*>* next)
|
|
{
|
|
return new (c->zone) List<GcTreeNode*>(node, next);
|
|
}
|
|
|
|
inline object getTreeNodeValue(Thread*, GcTreeNode* n)
|
|
{
|
|
return reinterpret_cast<object>(alias(n, TreeNodeValue) & PointerMask);
|
|
}
|
|
|
|
inline void setTreeNodeValue(Thread* t, GcTreeNode* n, object value)
|
|
{
|
|
intptr_t red = alias(n, TreeNodeValue) & (~PointerMask);
|
|
|
|
n->setValue(t, value);
|
|
|
|
alias(n, TreeNodeValue) |= red;
|
|
}
|
|
|
|
inline bool treeNodeRed(Thread*, GcTreeNode* n)
|
|
{
|
|
return (alias(n, TreeNodeValue) & (~PointerMask)) == 1;
|
|
}
|
|
|
|
inline void setTreeNodeRed(Thread*, GcTreeNode* n, bool red)
|
|
{
|
|
if (red) {
|
|
alias(n, TreeNodeValue) |= 1;
|
|
} else {
|
|
alias(n, TreeNodeValue) &= PointerMask;
|
|
}
|
|
}
|
|
|
|
inline GcTreeNode* cloneTreeNode(Thread* t, GcTreeNode* n)
|
|
{
|
|
PROTECT(t, n);
|
|
|
|
GcTreeNode* newNode
|
|
= makeTreeNode(t, getTreeNodeValue(t, n), n->left(), n->right());
|
|
setTreeNodeRed(t, newNode, treeNodeRed(t, n));
|
|
return newNode;
|
|
}
|
|
|
|
GcTreeNode* treeFind(Thread* t,
|
|
GcTreeNode* tree,
|
|
intptr_t key,
|
|
GcTreeNode* sentinal,
|
|
intptr_t (*compare)(Thread* t, intptr_t key, object b))
|
|
{
|
|
GcTreeNode* node = tree;
|
|
while (node != sentinal) {
|
|
intptr_t difference = compare(t, key, getTreeNodeValue(t, node));
|
|
if (difference < 0) {
|
|
node = node->left();
|
|
} else if (difference > 0) {
|
|
node = node->right();
|
|
} else {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void treeFind(Thread* t,
|
|
TreeContext* c,
|
|
GcTreeNode* old,
|
|
intptr_t key,
|
|
GcTreeNode* node,
|
|
GcTreeNode* sentinal,
|
|
intptr_t (*compare)(Thread* t, intptr_t key, object b))
|
|
{
|
|
PROTECT(t, old);
|
|
PROTECT(t, node);
|
|
PROTECT(t, sentinal);
|
|
|
|
GcTreeNode* newRoot = cloneTreeNode(t, old);
|
|
PROTECT(t, newRoot);
|
|
|
|
GcTreeNode* new_ = newRoot;
|
|
PROTECT(t, new_);
|
|
|
|
int count = 0;
|
|
while (old != sentinal) {
|
|
c->ancestors = path(c, new_, c->ancestors);
|
|
|
|
intptr_t difference = compare(t, key, getTreeNodeValue(t, old));
|
|
|
|
if (difference < 0) {
|
|
old = old->left();
|
|
GcTreeNode* n = cloneTreeNode(t, old);
|
|
new_->setLeft(t, n);
|
|
new_ = n;
|
|
} else if (difference > 0) {
|
|
old = old->right();
|
|
GcTreeNode* n = cloneTreeNode(t, old);
|
|
new_->setRight(t, n);
|
|
new_ = n;
|
|
} else {
|
|
c->fresh = false;
|
|
c->root = newRoot;
|
|
c->node = new_;
|
|
c->ancestors = c->ancestors->next;
|
|
return;
|
|
}
|
|
|
|
if (++count > 100) {
|
|
// if we've gone this deep, we probably have an unbalanced tree,
|
|
// which should only happen if there's a serious bug somewhere
|
|
// in our insertion process
|
|
abort(t);
|
|
}
|
|
}
|
|
|
|
setTreeNodeValue(t, new_, getTreeNodeValue(t, node));
|
|
|
|
c->fresh = true;
|
|
c->root = newRoot;
|
|
c->node = new_;
|
|
c->ancestors = c->ancestors;
|
|
}
|
|
|
|
GcTreeNode* leftRotate(Thread* t, GcTreeNode* n)
|
|
{
|
|
PROTECT(t, n);
|
|
|
|
GcTreeNode* child = cloneTreeNode(t, n->right());
|
|
n->setRight(t, child->left());
|
|
child->setLeft(t, n);
|
|
return child;
|
|
}
|
|
|
|
GcTreeNode* rightRotate(Thread* t, GcTreeNode* n)
|
|
{
|
|
PROTECT(t, n);
|
|
|
|
GcTreeNode* child = cloneTreeNode(t, n->left());
|
|
n->setLeft(t, child->right());
|
|
child->setRight(t, n);
|
|
return child;
|
|
}
|
|
|
|
GcTreeNode* treeAdd(Thread* t, TreeContext* c)
|
|
{
|
|
GcTreeNode* new_ = c->node;
|
|
PROTECT(t, new_);
|
|
|
|
GcTreeNode* newRoot = c->root;
|
|
PROTECT(t, newRoot);
|
|
|
|
// rebalance
|
|
setTreeNodeRed(t, new_, true);
|
|
while (c->ancestors != 0 and treeNodeRed(t, c->ancestors->item)) {
|
|
if (c->ancestors->item == c->ancestors->next->item->left()) {
|
|
if (treeNodeRed(t, c->ancestors->next->item->right())) {
|
|
setTreeNodeRed(t, c->ancestors->item, false);
|
|
|
|
GcTreeNode* n = cloneTreeNode(t, c->ancestors->next->item->right());
|
|
|
|
c->ancestors->next->item->setRight(t, n);
|
|
|
|
setTreeNodeRed(t, c->ancestors->next->item->right(), false);
|
|
|
|
setTreeNodeRed(t, c->ancestors->next->item, true);
|
|
|
|
new_ = c->ancestors->next->item;
|
|
c->ancestors = c->ancestors->next->next;
|
|
} else {
|
|
if (new_ == c->ancestors->item->right()) {
|
|
new_ = c->ancestors->item;
|
|
c->ancestors = c->ancestors->next;
|
|
|
|
GcTreeNode* n = leftRotate(t, new_);
|
|
|
|
if (new_ == c->ancestors->item->right()) {
|
|
c->ancestors->item->setRight(t, n);
|
|
} else {
|
|
c->ancestors->item->setLeft(t, n);
|
|
}
|
|
c->ancestors = path(c, n, c->ancestors);
|
|
}
|
|
setTreeNodeRed(t, c->ancestors->item, false);
|
|
setTreeNodeRed(t, c->ancestors->next->item, true);
|
|
|
|
GcTreeNode* n = rightRotate(t, c->ancestors->next->item);
|
|
if (c->ancestors->next->next == 0) {
|
|
newRoot = n;
|
|
} else if (c->ancestors->next->next->item->right()
|
|
== c->ancestors->next->item) {
|
|
c->ancestors->next->next->item->setRight(t, n);
|
|
} else {
|
|
c->ancestors->next->next->item->setLeft(t, n);
|
|
}
|
|
// done
|
|
}
|
|
} else { // this is just the reverse of the code above (right and
|
|
// left swapped):
|
|
if (treeNodeRed(t, c->ancestors->next->item->left())) {
|
|
setTreeNodeRed(t, c->ancestors->item, false);
|
|
|
|
GcTreeNode* n = cloneTreeNode(t, c->ancestors->next->item->left());
|
|
|
|
c->ancestors->next->item->setLeft(t, n);
|
|
|
|
setTreeNodeRed(t, c->ancestors->next->item->left(), false);
|
|
|
|
setTreeNodeRed(t, c->ancestors->next->item, true);
|
|
|
|
new_ = c->ancestors->next->item;
|
|
c->ancestors = c->ancestors->next->next;
|
|
} else {
|
|
if (new_ == c->ancestors->item->left()) {
|
|
new_ = c->ancestors->item;
|
|
c->ancestors = c->ancestors->next;
|
|
|
|
GcTreeNode* n = rightRotate(t, new_);
|
|
|
|
if (new_ == c->ancestors->item->left()) {
|
|
c->ancestors->item->setLeft(t, n);
|
|
} else {
|
|
c->ancestors->item->setRight(t, n);
|
|
}
|
|
c->ancestors = path(c, n, c->ancestors);
|
|
}
|
|
setTreeNodeRed(t, c->ancestors->item, false);
|
|
setTreeNodeRed(t, c->ancestors->next->item, true);
|
|
|
|
GcTreeNode* n = leftRotate(t, c->ancestors->next->item);
|
|
if (c->ancestors->next->next == 0) {
|
|
newRoot = n;
|
|
} else if (c->ancestors->next->next->item->left()
|
|
== c->ancestors->next->item) {
|
|
c->ancestors->next->next->item->setLeft(t, n);
|
|
} else {
|
|
c->ancestors->next->next->item->setRight(t, n);
|
|
}
|
|
// done
|
|
}
|
|
}
|
|
}
|
|
|
|
setTreeNodeRed(t, newRoot, false);
|
|
|
|
return newRoot;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace vm {
|
|
|
|
GcTriple* hashMapFindNode(Thread* t,
|
|
GcHashMap* map,
|
|
object key,
|
|
uint32_t (*hash)(Thread*, object),
|
|
bool (*equal)(Thread*, object, object))
|
|
{
|
|
bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type);
|
|
|
|
GcArray* array = map->array();
|
|
if (array) {
|
|
unsigned index = hash(t, key) & (array->length() - 1);
|
|
for (GcTriple* n = cast<GcTriple>(t, array->body()[index]); n;
|
|
n = cast<GcTriple>(t, n->third())) {
|
|
object k = n->first();
|
|
if (weak) {
|
|
k = cast<GcJreference>(t, k)->target();
|
|
if (k == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (equal(t, key, k)) {
|
|
return n;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void hashMapResize(Thread* t,
|
|
GcHashMap* map,
|
|
uint32_t (*hash)(Thread*, object),
|
|
unsigned size)
|
|
{
|
|
PROTECT(t, map);
|
|
|
|
GcArray* newArray = 0;
|
|
|
|
if (size) {
|
|
GcArray* oldArray = map->array();
|
|
PROTECT(t, oldArray);
|
|
|
|
unsigned newLength = nextPowerOfTwo(size);
|
|
if (oldArray and oldArray->length() == newLength) {
|
|
return;
|
|
}
|
|
|
|
newArray = makeArray(t, newLength);
|
|
|
|
if (oldArray != map->array()) {
|
|
// a resize was performed during a GC via the makeArray call
|
|
// above; nothing left to do
|
|
return;
|
|
}
|
|
|
|
if (oldArray) {
|
|
bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type);
|
|
for (unsigned i = 0; i < oldArray->length(); ++i) {
|
|
GcTriple* next;
|
|
for (GcTriple* p = cast<GcTriple>(t, oldArray->body()[i]); p;
|
|
p = next) {
|
|
next = cast<GcTriple>(t, p->third());
|
|
|
|
object k = p->first();
|
|
if (weak) {
|
|
k = cast<GcJreference>(t, k)->target();
|
|
if (k == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
unsigned index = hash(t, k) & (newLength - 1);
|
|
|
|
p->setThird(t, newArray->body()[index]);
|
|
newArray->setBodyElement(t, index, p);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
map->setArray(t, newArray);
|
|
}
|
|
|
|
void hashMapInsert(Thread* t,
|
|
GcHashMap* map,
|
|
object key,
|
|
object value,
|
|
uint32_t (*hash)(Thread*, object))
|
|
{
|
|
// note that we reinitialize the array variable whenever an
|
|
// allocation (and thus possibly a collection) occurs, in case the
|
|
// array changes due to a table resize.
|
|
|
|
PROTECT(t, map);
|
|
|
|
uint32_t h = hash(t, key);
|
|
|
|
bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type);
|
|
|
|
GcArray* array = map->array();
|
|
|
|
++map->size();
|
|
|
|
if (array == 0 or map->size() >= array->length() * 2) {
|
|
PROTECT(t, key);
|
|
PROTECT(t, value);
|
|
|
|
hashMapResize(t, map, hash, array ? array->length() * 2 : 16);
|
|
|
|
array = map->array();
|
|
}
|
|
|
|
object k = key;
|
|
|
|
if (weak) {
|
|
PROTECT(t, key);
|
|
PROTECT(t, value);
|
|
|
|
GcWeakReference* r = makeWeakReference(t, 0, 0, 0, 0);
|
|
|
|
r->setTarget(t, key);
|
|
r->setVmNext(t, t->m->weakReferences);
|
|
t->m->weakReferences = r->as<GcJreference>(t);
|
|
k = r;
|
|
|
|
array = map->array();
|
|
}
|
|
|
|
GcTriple* n = makeTriple(t, k, value, 0);
|
|
|
|
array = map->array();
|
|
|
|
unsigned index = h & (array->length() - 1);
|
|
|
|
n->setThird(t, array->body()[index]);
|
|
array->setBodyElement(t, index, n);
|
|
|
|
if (map->size() <= array->length() / 3) {
|
|
// this might happen if nodes were removed during GC in which case
|
|
// we weren't able to resize at the time
|
|
hashMapResize(t, map, hash, array->length() / 2);
|
|
}
|
|
}
|
|
|
|
GcTriple* hashMapRemoveNode(Thread* t,
|
|
GcHashMap* map,
|
|
unsigned index,
|
|
GcTriple* p,
|
|
GcTriple* n)
|
|
{
|
|
if (p) {
|
|
p->setThird(t, n->third());
|
|
} else {
|
|
map->array()->setBodyElement(t, index, n->third());
|
|
}
|
|
--map->size();
|
|
return n;
|
|
}
|
|
|
|
object hashMapRemove(Thread* t,
|
|
GcHashMap* map,
|
|
object key,
|
|
uint32_t (*hash)(Thread*, object),
|
|
bool (*equal)(Thread*, object, object))
|
|
{
|
|
bool weak = objectClass(t, map) == type(t, GcWeakHashMap::Type);
|
|
|
|
GcArray* array = map->array();
|
|
object o = 0;
|
|
if (array) {
|
|
unsigned index = hash(t, key) & (array->length() - 1);
|
|
GcTriple* p = 0;
|
|
for (GcTriple* n = cast<GcTriple>(t, array->body()[index]); n;) {
|
|
object k = n->first();
|
|
if (weak) {
|
|
k = cast<GcJreference>(t, k)->target();
|
|
if (k == 0) {
|
|
n = cast<GcTriple>(t,
|
|
hashMapRemoveNode(t, map, index, p, n)->third());
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (equal(t, key, k)) {
|
|
o = hashMapRemoveNode(t, map, index, p, n)->second();
|
|
break;
|
|
} else {
|
|
p = n;
|
|
n = cast<GcTriple>(t, n->third());
|
|
}
|
|
}
|
|
|
|
if ((not t->m->collecting) and map->size() <= array->length() / 3) {
|
|
PROTECT(t, o);
|
|
hashMapResize(t, map, hash, array->length() / 2);
|
|
}
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
void listAppend(Thread* t, GcList* list, object value)
|
|
{
|
|
PROTECT(t, list);
|
|
|
|
++list->size();
|
|
|
|
object p = makePair(t, value, 0);
|
|
if (list->front()) {
|
|
cast<GcPair>(t, list->rear())->setSecond(t, p);
|
|
} else {
|
|
list->setFront(t, p);
|
|
}
|
|
list->setRear(t, p);
|
|
}
|
|
|
|
GcVector* vectorAppend(Thread* t, GcVector* vector, object value)
|
|
{
|
|
if (vector->length() == vector->size()) {
|
|
PROTECT(t, vector);
|
|
PROTECT(t, value);
|
|
|
|
GcVector* newVector
|
|
= makeVector(t, vector->size(), max(16, vector->size() * 2));
|
|
|
|
if (vector->size()) {
|
|
for (size_t i = 0; i < vector->size(); i++) {
|
|
newVector->setBodyElement(t, i, vector->body()[i]);
|
|
}
|
|
}
|
|
|
|
vector = newVector;
|
|
}
|
|
|
|
vector->setBodyElement(t, vector->size(), value);
|
|
++vector->size();
|
|
return vector;
|
|
}
|
|
|
|
GcArray* growArray(Thread* t, GcArray* array)
|
|
{
|
|
PROTECT(t, array);
|
|
|
|
GcArray* newArray = makeArray(t, array == 0 ? 16 : (array->length() * 2));
|
|
|
|
if (array) {
|
|
for (size_t i = 0; i < array->length(); i++) {
|
|
newArray->setBodyElement(t, i, array->body()[i]);
|
|
}
|
|
}
|
|
|
|
return newArray;
|
|
}
|
|
|
|
object treeQuery(Thread* t,
|
|
GcTreeNode* tree,
|
|
intptr_t key,
|
|
GcTreeNode* sentinal,
|
|
intptr_t (*compare)(Thread* t, intptr_t key, object b))
|
|
{
|
|
GcTreeNode* node = treeFind(t, tree, key, sentinal, compare);
|
|
return (node ? getTreeNodeValue(t, node) : 0);
|
|
}
|
|
|
|
GcTreeNode* treeInsert(Thread* t,
|
|
Zone* zone,
|
|
GcTreeNode* tree,
|
|
intptr_t key,
|
|
object value,
|
|
GcTreeNode* sentinal,
|
|
intptr_t (*compare)(Thread* t, intptr_t key, object b))
|
|
{
|
|
PROTECT(t, tree);
|
|
PROTECT(t, sentinal);
|
|
|
|
GcTreeNode* node = makeTreeNode(t, value, sentinal, sentinal);
|
|
|
|
TreeContext c(t, zone);
|
|
treeFind(t, &c, tree, key, node, sentinal, compare);
|
|
expect(t, c.fresh);
|
|
|
|
return treeAdd(t, &c);
|
|
}
|
|
|
|
void treeUpdate(Thread* t,
|
|
GcTreeNode* tree,
|
|
intptr_t key,
|
|
object value,
|
|
GcTreeNode* sentinal,
|
|
intptr_t (*compare)(Thread* t, intptr_t key, object b))
|
|
{
|
|
setTreeNodeValue(t, treeFind(t, tree, key, sentinal, compare), value);
|
|
}
|
|
|
|
} // namespace vm
|