mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-09 05:10:23 +00:00
changes from r4341 through r4787 inclusive). That branch is now dead. Please do not commit anything else to it. For the most part the merge was not troublesome. The main areas of uncertainty are: - build system: I had to import by hand Makefile.core-AM_CPPFLAGS.am and include it in a couple of places. Building etc seems to still work, but I haven't tried building the documentation. - syscall wrappers: Following analysis by Greg & Nick, a whole lot of stuff was moved from -generic to -linux after the branch was created. I think that is satisfactorily glued back together now. - Regtests: although this appears to work, no .out files appear, which is strange, and makes it hard to diagnose regtest failures. In particular memcheck/tests/x86/scalar.stderr.exp remains in a conflicted state. - amd64 is broken (slightly), and ppc32 will be unbuildable. I'll attend to the former shortly. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@4789
512 lines
13 KiB
C
512 lines
13 KiB
C
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- A skiplist implementation. m_skiplist.c ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
/*
|
|
This file is part of Valgrind, a dynamic binary instrumentation
|
|
framework.
|
|
|
|
Copyright (C) 2002-2005 Jeremy Fitzhardinge
|
|
jeremy@goop.org
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program 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 for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
|
|
The GNU General Public License is contained in the file COPYING.
|
|
*/
|
|
|
|
/*
|
|
This file implements a generic skip-list type.
|
|
|
|
Skip-lists are described in William Pugh, Skip Lists: A
|
|
Probabilistic Alternative to Balanced Trees, CACM, 33(6):668-676,
|
|
June 1990.
|
|
http://www.cs.unc.edu/~baruah/Teaching/2002f/HandOuts/skiplist-CACM.pdf
|
|
|
|
Skip-lists are a randomized linked-list, where a node in the list
|
|
has at least one "next" pointer, but may have more. When
|
|
traversing the list, the "higher" next pointers allow you to skip
|
|
multiple list entries, allowing you to find the right place
|
|
quickly.
|
|
|
|
On average, 1/2 the entries have one next pointer, 1/4 have 2, 1/8
|
|
have 3, etc. This means that the skiplist has the same search
|
|
complexity as a balanced binary tree, but there is no need to
|
|
rebalance or do any other structural changes. The randomness also
|
|
means that it is invulnerable to pathalogical workloads.
|
|
|
|
Because the each node can be a different size, this implementation
|
|
puts the SkipNode structure at the end of the allocation, with the
|
|
node data starting at the beginning.
|
|
|
|
low address ->+---------------+ ^
|
|
| list data | |
|
|
key offset->: key : structure size
|
|
| | |
|
|
+---------------+ V
|
|
| struct |
|
|
| SkipNode |
|
|
+- - - - - - - -+
|
|
| next pointers |
|
|
: :
|
|
|
|
|
|
When you declare the list with VG_SKIPLIST_INIT, you specify the
|
|
structure for each list node, structure member you want to use as a
|
|
key, and the function for comparing keys.
|
|
|
|
Each node must be allocated with SkipNode_Alloc, which allocates
|
|
enough space for the client data, the SkipNode structure, and the
|
|
next pointers for this node.
|
|
|
|
The helper functions data_of_node and node_of_data do the correct
|
|
pointer arithmetic to sort all this out. The SkipNode also has a
|
|
magic number which is checked after each operation to make sure
|
|
that we're really operating on a SkipNode.
|
|
|
|
The first node of the list, the head node, is special. It contains
|
|
the maximum number of next pointers (SK_MAXHEIGHT). It has no data
|
|
associated with it, and it always compares less-than to all other
|
|
nodes. Because it has no data attached to it, it is always an
|
|
error to try and use data_of_node on it. To detect this problem,
|
|
it has a different magic number from all other SkipNodes so that it
|
|
won't be accidentally used.
|
|
*/
|
|
|
|
#include "pub_core_basics.h"
|
|
#include "pub_core_libcbase.h"
|
|
#include "pub_core_libcassert.h"
|
|
#include "pub_core_libcprint.h"
|
|
#include "pub_core_mallocfree.h"
|
|
#include "pub_core_skiplist.h"
|
|
|
|
#define SKIPLIST_DEBUG 0
|
|
|
|
#define SK_MAXHEIGHT 20 /* 2^20 elements */
|
|
#define SKIPLIST_MAGIC 0x5b1ff872
|
|
#define SKIPLIST_HEAD_MAGIC (~SKIPLIST_MAGIC)
|
|
|
|
|
|
#if SKIPLIST_DEBUG
|
|
#define inline
|
|
#endif /* SKIPLIST_DEBUG */
|
|
|
|
struct _SkipNode {
|
|
UInt magic;
|
|
UShort level; /* level is the max level (level == 0 means 1 next pointer) */
|
|
SkipNode *next[0];
|
|
};
|
|
|
|
|
|
/*
|
|
Compute the height of a new node. 1/2 will be 1, 1/4 2, 1/8 3,
|
|
etc.
|
|
*/
|
|
static inline Int get_height(void)
|
|
{
|
|
UInt ret = 0;
|
|
|
|
while((ret < SK_MAXHEIGHT - 1) && (VG_(random)(NULL) & 1))
|
|
ret++;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
Given a pointer to the node's data, return a pointer to the key.
|
|
*/
|
|
static inline void *key_of_data(const SkipList *l, void *d)
|
|
{
|
|
return (void *)((Char *)d + l->keyoff);
|
|
}
|
|
|
|
/*
|
|
Given a pointer to the node's data, return the pointer to the SkipNode
|
|
structure. If the node has a bad magic number, it will die with an
|
|
assertion failure.
|
|
*/
|
|
static inline SkipNode *node_of_data(const SkipList *l, const void *d)
|
|
{
|
|
SkipNode *n = (SkipNode *)((Char *)d + l->size);
|
|
|
|
if (SKIPLIST_DEBUG && n->magic != SKIPLIST_MAGIC)
|
|
VG_(printf)("bad magic on node %p = %x (not %x)\n",
|
|
n, n->magic, SKIPLIST_MAGIC);
|
|
|
|
vg_assert(n->magic == SKIPLIST_MAGIC);
|
|
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
Given a SkipNode structure, return the pointer to the node's data.
|
|
*/
|
|
static inline void *data_of_node(const SkipList *l, const SkipNode *n)
|
|
{
|
|
if (SKIPLIST_DEBUG && n->magic != SKIPLIST_MAGIC)
|
|
VG_(printf)("bad magic on node %p = %x (not %x)\n",
|
|
n, n->magic, SKIPLIST_MAGIC);
|
|
|
|
vg_assert(n->magic == SKIPLIST_MAGIC);
|
|
return (void *)((Char *)n - l->size);
|
|
}
|
|
|
|
/*
|
|
Given a SkipNode structure, return the pointer to the node's key.
|
|
*/
|
|
static inline void *key_of_node(const SkipList *l, const SkipNode *n)
|
|
{
|
|
return key_of_data(l, data_of_node(l, n));
|
|
}
|
|
|
|
static inline void validate_skiplist(const SkipList *l, const Char *where)
|
|
{
|
|
#if SKIPLIST_DEBUG
|
|
const SkipNode *prev[SK_MAXHEIGHT];
|
|
Int i;
|
|
const SkipNode *n, *next;
|
|
|
|
VG_(printf)("---------------- %s ----------------\n", where);
|
|
|
|
if (l->head == NULL)
|
|
return;
|
|
|
|
for(i = 0; i <= l->head->level; i++) {
|
|
VG_(printf)("l->head->next[%d]=%p\n",
|
|
i, l->head->next[i]);
|
|
prev[i] = l->head->next[i];
|
|
}
|
|
|
|
for(n = l->head->next[0]; n != NULL; n = next) {
|
|
next = n->next[0];
|
|
|
|
VG_(printf)("n=%p next=%p, n->level=%d k=%s\n",
|
|
n, next, n->level, (*l->strkey)(key_of_node(l, n)));
|
|
for(i = 0; i <= n->level; i++) {
|
|
VG_(printf)(" n->next[%d] = %p\n",
|
|
i, n->next[i]);
|
|
VG_(printf)(" prev[%d] = %p\n",
|
|
i, prev[i]);
|
|
}
|
|
|
|
vg_assert(l->head->level >= n->level);
|
|
|
|
for(i = 0; i <= n->level; i++)
|
|
vg_assert(prev[i] == n);
|
|
|
|
for(i = 0; i <= n->level; i++)
|
|
prev[i] = n->next[i];
|
|
|
|
vg_assert(next == NULL || (l->cmp)(key_of_node(l, n), key_of_node(l, next)) < 0);
|
|
}
|
|
#endif /* SKIPLIST_DEBUG */
|
|
}
|
|
|
|
void *VG_(SkipNode_Alloc)(const SkipList *l)
|
|
{
|
|
UInt size;
|
|
Int h;
|
|
SkipNode *n;
|
|
Char *ret;
|
|
|
|
h = get_height();
|
|
|
|
size = l->size;
|
|
size += sizeof(SkipNode) + (h+1)*sizeof(SkipNode *);
|
|
|
|
if (l->arena == -1)
|
|
*(Short *)&l->arena = VG_AR_TOOL;
|
|
|
|
ret = VG_(arena_malloc)(l->arena, size);
|
|
|
|
if (ret == NULL)
|
|
return NULL;
|
|
|
|
n = (SkipNode *)(ret + l->size);
|
|
n->level = h;
|
|
n->magic = SKIPLIST_MAGIC;
|
|
|
|
VG_(memset)(n->next, 0, sizeof(n->next[0]) * (h+1));
|
|
|
|
return ret;
|
|
}
|
|
|
|
void VG_(SkipNode_Free)(const SkipList *l, void *p)
|
|
{
|
|
if (SKIPLIST_DEBUG) {
|
|
SkipNode *n = node_of_data(l, p);
|
|
|
|
VG_(printf)("SkipNode_Free: freeing %p (node %p)\n",
|
|
p, n);
|
|
n->magic = 0x55ffaabb;
|
|
}
|
|
VG_(arena_free)(l->arena, p);
|
|
}
|
|
|
|
void *VG_(SkipNode_First)(const SkipList *l)
|
|
{
|
|
SkipNode *n = l->head ? l->head->next[0] : NULL;
|
|
|
|
if (n == NULL)
|
|
return NULL;
|
|
else
|
|
return data_of_node(l, n);
|
|
}
|
|
|
|
void *VG_(SkipNode_Next)(const SkipList *l, void *data)
|
|
{
|
|
SkipNode *n = node_of_data(l, data);
|
|
|
|
n = n->next[0];
|
|
|
|
if (n == NULL)
|
|
return NULL;
|
|
|
|
return data_of_node(l, n);
|
|
}
|
|
|
|
|
|
|
|
static Int cmp(const SkipList *l, SkipNode *n, void *k2)
|
|
{
|
|
void *k1 = key_of_node(l, n);
|
|
|
|
if (k1 == k2)
|
|
return 0;
|
|
|
|
if (l->head == n)
|
|
return -1;
|
|
|
|
return (l->cmp)(k1, k2);
|
|
}
|
|
|
|
/* Search the list for k; it either returns the k if it exists, or the
|
|
one before if not. */
|
|
static SkipNode *SkipList__Find(const SkipList *l, void *k, SkipNode **prevs)
|
|
{
|
|
SkipNode *n;
|
|
Int lvl;
|
|
|
|
if (SKIPLIST_DEBUG)
|
|
VG_(printf)("SkipList__Find: finding %s\n", (*l->strkey)(k));
|
|
|
|
validate_skiplist(l, "SkipList__Find");
|
|
|
|
if (l->head == NULL)
|
|
return NULL;
|
|
|
|
for(lvl = l->head->level, n = l->head; lvl >= 0; lvl--) {
|
|
while(n->next[lvl] != NULL && cmp(l, n->next[lvl], k) < 0) {
|
|
if (SKIPLIST_DEBUG)
|
|
VG_(printf)("SkipList__Find: n=%p n->next[%d]=%p\n",
|
|
n, lvl, n->next[lvl]);
|
|
n = n->next[lvl];
|
|
}
|
|
if (prevs)
|
|
prevs[lvl] = n;
|
|
}
|
|
|
|
/* XXX Is there a cleaner way of getting this?
|
|
|
|
If we get an exact match, return it.
|
|
If we get the head, return NULL.
|
|
Otherwise return the one before where the hit would be.
|
|
*/
|
|
if (n->next[0] != NULL && cmp(l, n->next[0], k) == 0)
|
|
n = n->next[0];
|
|
if (n == l->head)
|
|
n = NULL;
|
|
|
|
if (SKIPLIST_DEBUG) {
|
|
|
|
VG_(printf)("SkipList__Find returning node %p\n", n);
|
|
|
|
if (n == NULL) {
|
|
SkipNode *nn;
|
|
|
|
for(nn = l->head->next[0]; nn != NULL; nn = nn->next[0])
|
|
vg_assert(cmp(l, nn, k) != 0);
|
|
} else
|
|
vg_assert(cmp(l, n, k) <= 0);
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
/* Return list element which is <= k, or NULL if there is none. */
|
|
void *VG_(SkipList_Find_Before)(const SkipList *l, void *k)
|
|
{
|
|
SkipNode *n = SkipList__Find(l, k, NULL);
|
|
|
|
if (n != NULL)
|
|
return data_of_node(l, n);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the list element which == k, or NULL if none */
|
|
void *VG_(SkipList_Find_Exact)(const SkipList *l, void *k)
|
|
{
|
|
SkipNode *n = SkipList__Find(l, k, NULL);
|
|
|
|
if (n != NULL && (l->cmp)(key_of_node(l, n), k) == 0)
|
|
return data_of_node(l, n);
|
|
return NULL;
|
|
}
|
|
|
|
/* Return the list element which is >= k, or NULL if none */
|
|
void *VG_(SkipList_Find_After)(const SkipList *l, void *k)
|
|
{
|
|
SkipNode *n = SkipList__Find(l, k, NULL);
|
|
|
|
if (n != NULL && (l->cmp)(key_of_node(l, n), k) < 0)
|
|
n = n->next[0];
|
|
|
|
if (n != NULL)
|
|
return data_of_node(l, n);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void VG_(SkipList_Insert)(SkipList *l, void *data)
|
|
{
|
|
SkipNode *update[SK_MAXHEIGHT];
|
|
SkipNode *n, *nn;
|
|
void *k = key_of_data(l, data);
|
|
Int i;
|
|
|
|
if (SKIPLIST_DEBUG)
|
|
VG_(printf)("inserting node %p, key %s, height %d\n",
|
|
data, (*l->strkey)(key_of_data(l, data)), node_of_data(l, data)->level);
|
|
|
|
validate_skiplist(l, "SkipList_Insert before");
|
|
|
|
if (l->head == NULL) {
|
|
Int size = sizeof(SkipNode) + sizeof(SkipNode *) * SK_MAXHEIGHT;
|
|
|
|
if (l->arena == -1)
|
|
*(Short *)&l->arena = VG_AR_TOOL;
|
|
|
|
l->head = VG_(arena_malloc)(l->arena, size);
|
|
VG_(memset)(l->head, 0, size);
|
|
|
|
l->head->magic = SKIPLIST_HEAD_MAGIC;
|
|
l->head->level = 0;
|
|
}
|
|
|
|
n = node_of_data(l, data);
|
|
|
|
/* update size of head's next vector to fit this new node */
|
|
vg_assert(l->head != NULL);
|
|
if (l->head->level < n->level) {
|
|
for(i = l->head->level+1; i <= n->level; i++)
|
|
l->head->next[i] = NULL;
|
|
l->head->level = n->level;
|
|
}
|
|
|
|
/* Look for the node, but we're mostly interested in setting
|
|
"update", which is the set of previous nodes with next pointers
|
|
we need to fix up. */
|
|
nn = SkipList__Find(l, k, update);
|
|
|
|
/* check the new entry is unique */
|
|
vg_assert(nn == NULL || (l->cmp)(key_of_node(l, nn), k) != 0);
|
|
|
|
/* update the previous node's next pointers */
|
|
for(i = 0; i <= n->level; i++) {
|
|
n->next[i] = update[i]->next[i];
|
|
update[i]->next[i] = n;
|
|
}
|
|
|
|
validate_skiplist(l, "SkipList_Insert after");
|
|
}
|
|
|
|
void *VG_(SkipList_Remove)(SkipList *l, void *k)
|
|
{
|
|
SkipNode *update[SK_MAXHEIGHT];
|
|
SkipNode *n;
|
|
Int i;
|
|
|
|
validate_skiplist(l, "SkipList_Remove before");
|
|
|
|
n = SkipList__Find(l, k, update);
|
|
if (n == NULL)
|
|
return NULL;
|
|
|
|
vg_assert((l->cmp)(k, key_of_node(l, n)) == 0);
|
|
|
|
for(i = 0; i <= n->level; i++) {
|
|
update[i]->next[i] = n->next[i];
|
|
n->next[i] = NULL;
|
|
}
|
|
|
|
validate_skiplist(l, "SkipList_Remove after");
|
|
|
|
return data_of_node(l, n);
|
|
}
|
|
|
|
|
|
/* --------------------------------------------------
|
|
Comparison functions
|
|
-------------------------------------------------- */
|
|
Int VG_(cmp_Int)(const void *v1, const void *v2)
|
|
{
|
|
Int a = *(const Int *)v1;
|
|
Int b = *(const Int *)v2;
|
|
|
|
if (a < b)
|
|
return -1;
|
|
if (a == b)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
Int VG_(cmp_UInt)(const void *v1, const void *v2)
|
|
{
|
|
UInt a = *(const UInt *)v1;
|
|
UInt b = *(const UInt *)v2;
|
|
|
|
if (a < b)
|
|
return -1;
|
|
if (a == b)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
Int VG_(cmp_Addr)(const void *v1, const void *v2)
|
|
{
|
|
Addr a = *(const Addr *)v1;
|
|
Addr b = *(const Addr *)v2;
|
|
|
|
if (a < b)
|
|
return -1;
|
|
if (a == b)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
Int VG_(cmp_string)(const void *v1, const void *v2)
|
|
{
|
|
const Char *a = *(const Char **)v1;
|
|
const Char *b = *(const Char **)v2;
|
|
|
|
return VG_(strcmp)(a, b);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/*--- end ---*/
|
|
/*--------------------------------------------------------------------*/
|
|
|