/*--------------------------------------------------------------------*/
/*--- An sparse array (of words) implementation. ---*/
/*--- m_sparsewa.c ---*/
/*--------------------------------------------------------------------*/
/*
This file is part of Valgrind, a dynamic binary instrumentation
framework.
Copyright (C) 2008-2011 OpenWorks Ltd
info@open-works.co.uk
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.
*/
#include "pub_core_basics.h"
#include "pub_core_libcassert.h"
#include "pub_core_libcbase.h"
#include "pub_core_sparsewa.h" /* self */
/////////////////////////////////////////////////////////
// //
// SparseWA: Implementation //
// //
/////////////////////////////////////////////////////////
//////// SWA data structures
// (UInt) `echo "Level Zero Byte Map" | md5sum`
#define Level0_MAGIC 0x458ec222
// (UInt) `echo "Level N Byte Map" | md5sum`
#define LevelN_MAGIC 0x0a280a1a
/* It's important that the .magic field appears at offset zero in both
structs, so that we can reliably distinguish between them. */
typedef
struct {
UWord magic;
UWord words[256];
Int nInUse;
UChar inUse[256/8];
}
Level0;
typedef
struct {
UWord magic;
void* child[256]; /* either LevelN* or Level0* */
Int nInUse;
Int level; /* 3 .. 1 on 32-bit, 7 .. 1 on 64-bit */
}
LevelN;
typedef
struct {
UWord partial_key;
Int curr_ix;
void* curr_nd; /* LevelN* or Level0* */
Int resume_point; /* 1, 2 or 3 */
}
SWAStackElem;
struct _SparseWA {
void* (*alloc_nofail)(HChar*,SizeT);
HChar* cc;
void (*dealloc)(void*);
LevelN* root;
SWAStackElem iterStack[8];
Int isUsed;
};
//////// SWA helper functions (bitarray)
static inline UWord swa_bitarray_read ( UChar* arr, UWord ix ) {
UWord bix = ix >> 3;
UWord off = ix & 7;
return (arr[bix] >> off) & 1;
}
static inline UWord swa_bitarray_read_then_set ( UChar* arr, UWord ix ) {
UWord bix = ix >> 3;
UWord off = ix & 7;
UChar old = arr[bix];
UChar nyu = old | (1 << off);
arr[bix] = nyu;
return (old >> off) & 1;
}
static inline UWord swa_bitarray_read_then_clear ( UChar* arr, UWord ix ) {
UWord bix = ix >> 3;
UWord off = ix & 7;
UChar old = arr[bix];
UChar nyu = old & ~(1 << off);
arr[bix] = nyu;
return (old >> off) & 1;
}
//////// SWA helper functions (iteration)
static void swa_PUSH ( SparseWA* swa, UWord partial_key, Int curr_ix,
void* curr_nd, Int resume_point )
{
Int sp = swa->isUsed;
const Int _3_or_7 = sizeof(void*) - 1;
// if (0) VG_(printf)("PUSH, old sp = %d\n", sp);
vg_assert(sp >= 0 && sp <= _3_or_7);
swa->iterStack[sp].partial_key = partial_key;
swa->iterStack[sp].curr_ix = curr_ix;
swa->iterStack[sp].curr_nd = curr_nd;
swa->iterStack[sp].resume_point = resume_point;
swa->isUsed = sp+1;
}
static void swa_POP ( SparseWA* swa,
UWord* partial_key, Int* curr_ix,
void** curr_nd, Int* resume_point )
{
Int sp = swa->isUsed - 1;
const Int _3_or_7 = sizeof(void*) - 1;
// if (0) VG_(printf)("POP, old sp = %d\n", sp+1);
vg_assert(sp >= 0 && sp <= _3_or_7);
*partial_key = swa->iterStack[sp].partial_key;
*curr_ix = swa->iterStack[sp].curr_ix;
*curr_nd = swa->iterStack[sp].curr_nd;
*resume_point = swa->iterStack[sp].resume_point;
swa->isUsed = sp;
}
//////// SWA helper functions (allocation)
static LevelN* swa_new_LevelN ( SparseWA* swa, Int level )
{
LevelN* levelN = swa->alloc_nofail( swa->cc, sizeof(LevelN) );
VG_(memset)(levelN, 0, sizeof(*levelN));
levelN->magic = LevelN_MAGIC;
levelN->level = level;
return levelN;
}
static Level0* swa_new_Level0 ( SparseWA* swa )
{
Level0* level0 = swa->alloc_nofail( swa->cc, sizeof(Level0) );
VG_(memset)(level0, 0, sizeof(*level0));
level0->magic = Level0_MAGIC;
return level0;
}
//////// SWA public interface
void VG_(initIterSWA) ( SparseWA* swa )
{
swa->isUsed = 0;
if (swa->root) swa_PUSH(swa, 0, 0, swa->root, 1/*start_new_node*/);
}
Bool VG_(nextIterSWA)( SparseWA* swa,
/*OUT*/UWord* keyP, /*OUT*/UWord* valP )
{
UWord p_key;
Int curr_ix;
void* curr_nd;
Int resume_point;
/* dispatch whatever's on top of the stack; what that actually
means is to return to some previously-saved context. */
dispatch:
if (swa->isUsed == 0)
return False;
swa_POP(swa, &p_key, &curr_ix, &curr_nd, &resume_point);
switch (resume_point) {
case 1: goto start_new_node;
case 2: goto resume_leaf_node;
case 3: goto resume_nonleaf_node;
default: vg_assert(0);
}
start_new_node:
if (*(UWord*)curr_nd == Level0_MAGIC) {
/* curr_nd is a leaf node */
Level0* level0 = (Level0*)curr_nd;
for (curr_ix = 0; curr_ix < 256; curr_ix++) {
if (swa_bitarray_read(level0->inUse, curr_ix) == 1) {
swa_PUSH(swa, p_key, curr_ix, curr_nd, 2/*resume_leaf_node*/);
*keyP = (p_key << 8) + (UWord)curr_ix;
*valP = level0->words[curr_ix];
return True;
resume_leaf_node:
level0 = (Level0*)curr_nd;
}
}
} else {
/* curr_nd is a non-leaf node */
LevelN* levelN;
vg_assert(*(UWord*)curr_nd == LevelN_MAGIC);
levelN = (LevelN*)curr_nd;
for (curr_ix = 0; curr_ix < 256; curr_ix++) {
if (levelN->child[curr_ix]) {
swa_PUSH(swa, p_key, curr_ix, curr_nd, 3/*resume_nonleaf_node*/);
p_key = (p_key << 8) + (UWord)curr_ix;
curr_nd = levelN->child[curr_ix];
goto start_new_node;
resume_nonleaf_node:
levelN = (LevelN*)curr_nd;
}
}
}
goto dispatch;
}
SparseWA* VG_(newSWA) ( void*(*alloc_nofail)(HChar* cc, SizeT),
HChar* cc,
void(*dealloc)(void*) )
{
SparseWA* swa;
vg_assert(alloc_nofail);
vg_assert(cc);
vg_assert(dealloc);
swa = alloc_nofail( cc, sizeof(SparseWA) );
VG_(memset)(swa, 0, sizeof(*swa));
swa->alloc_nofail = alloc_nofail;
swa->cc = cc;
swa->dealloc = dealloc;
swa->root = NULL;
return swa;
}
static void swa_deleteSWA_wrk ( void(*dealloc)(void*), void* nd )
{
Int i;
vg_assert(nd);
if (*(UWord*)nd == LevelN_MAGIC) {
LevelN* levelN = (LevelN*)nd;
for (i = 0; i < 256; i++) {
if (levelN->child[i]) {
swa_deleteSWA_wrk( dealloc, levelN->child[i] );
}
}
} else {
vg_assert(*(UWord*)nd == Level0_MAGIC);
}
dealloc(nd);
}
void VG_(deleteSWA) ( SparseWA* swa )
{
if (swa->root)
swa_deleteSWA_wrk( swa->dealloc, swa->root );
swa->dealloc(swa);
}
Bool VG_(lookupSWA) ( SparseWA* swa,
/*OUT*/UWord* keyP, /*OUT*/UWord* valP,
UWord key )
{
Int i;
UWord ix;
Level0* level0;
LevelN* levelN;
const Int _3_or_7 = sizeof(void*) - 1;
vg_assert(swa);
levelN = swa->root;
/* levels 3/7 .. 1 */
for (i = _3_or_7; i >= 1; i--) {
if (!levelN) return False;
vg_assert(levelN->level == i);
vg_assert(levelN->nInUse > 0);
ix = (key >> (i*8)) & 0xFF;
levelN = levelN->child[ix];
}
/* level0 */
level0 = (Level0*)levelN;
if (!level0) return False;
vg_assert(level0->magic == Level0_MAGIC);
vg_assert(level0->nInUse > 0);
ix = key & 0xFF;
if (swa_bitarray_read(level0->inUse, ix) == 0) return False;
*keyP = key; /* this is stupid. only here to make it look like WordFM */
*valP = level0->words[ix];
return True;
}
Bool VG_(addToSWA) ( SparseWA* swa, UWord key, UWord val )
{
Int i;
UWord ix;
Level0* level0;
LevelN* levelN;
Bool already_present;
const Int _3_or_7 = sizeof(void*) - 1;
vg_assert(swa);
if (!swa->root)
swa->root = swa_new_LevelN(swa, _3_or_7);
levelN = swa->root;
/* levels 3/7 .. 2 */
for (i = _3_or_7; i >= 2; i--) {
/* levelN is the level-i map */
vg_assert(levelN);
vg_assert(levelN->level == i);
ix = (key >> (i*8)) & 0xFF;
if (levelN->child[ix] == NULL) {
levelN->child[ix] = swa_new_LevelN(swa, i-1);
levelN->nInUse++;
}
vg_assert(levelN->nInUse >= 1 && levelN->nInUse <= 256);
levelN = levelN->child[ix];
}
/* levelN is the level-1 map */
vg_assert(levelN);
vg_assert(levelN->level == 1);
ix = (key >> (1*8)) & 0xFF;
if (levelN->child[ix] == NULL) {
levelN->child[ix] = swa_new_Level0(swa);
levelN->nInUse++;
}
vg_assert(levelN->nInUse >= 1 && levelN->nInUse <= 256);
level0 = levelN->child[ix];
/* level0 is the level-0 map */
vg_assert(level0);
vg_assert(level0->magic == Level0_MAGIC);
ix = key & 0xFF;
if (swa_bitarray_read_then_set(level0->inUse, ix) == 0) {
level0->nInUse++;
already_present = False;
} else {
already_present = True;
}
vg_assert(level0->nInUse >= 1 && level0->nInUse <= 256);
level0->words[ix] = val;
return already_present;
}
Bool VG_(delFromSWA) ( SparseWA* swa,
/*OUT*/UWord* oldK, /*OUT*/UWord* oldV, UWord key )
{
Int i;
UWord ix;
Level0* level0;
LevelN* levelN;
const Int _3_or_7 = sizeof(void*) - 1;
LevelN* visited[_3_or_7];
UWord visitedIx[_3_or_7];
Int nVisited = 0;
vg_assert(swa);
levelN = swa->root;
/* levels 3/7 .. 1 */
for (i = _3_or_7; i >= 1; i--) {
/* level i */
if (!levelN) return False;
vg_assert(levelN->level == i);
vg_assert(levelN->nInUse > 0);
ix = (key >> (i*8)) & 0xFF;
visited[nVisited] = levelN;
visitedIx[nVisited++] = ix;
levelN = levelN->child[ix];
}
/* level 0 */
level0 = (Level0*)levelN;
if (!level0) return False;
vg_assert(level0->magic == Level0_MAGIC);
vg_assert(level0->nInUse > 0);
ix = key & 0xFF;
if (swa_bitarray_read_then_clear(level0->inUse, ix) == 0)
return False;
*oldK = key; /* this is silly */
*oldV = level0->words[ix];
level0->nInUse--;
if (level0->nInUse > 0)
return True;
vg_assert(nVisited == _3_or_7);
swa->dealloc( level0 );
/* levels 1 .. 3/7 */
for (i = 1; i <= _3_or_7; i++) {
/* level i */
nVisited--;
vg_assert(visited[nVisited]->child[ visitedIx[nVisited] ]);
visited[nVisited]->child[ visitedIx[nVisited] ] = NULL;
visited[nVisited]->nInUse--;
vg_assert(visited[nVisited]->nInUse >= 0);
if (visited[nVisited]->nInUse > 0)
return True;
swa->dealloc(visited[nVisited]);
}
vg_assert(nVisited == 0);
swa->root = NULL;
return True;
}
static UWord swa_sizeSWA_wrk ( void* nd )
{
Int i;
UWord sum = 0;
if (*(UWord*)nd == LevelN_MAGIC) {
LevelN* levelN = (LevelN*)nd;
for (i = 0; i < 256; i++) {
if (levelN->child[i]) {
sum += swa_sizeSWA_wrk( levelN->child[i] );
}
}
} else {
Level0* level0;
vg_assert(*(UWord*)nd == Level0_MAGIC);
level0 = (Level0*)nd;
for (i = 0; i < 256/8; i += 2) {
UWord x = level0->inUse[i+0]; /* assume zero-extend */
UWord y = level0->inUse[i+1]; /* assume zero-extend */
/* do 'sum += popcount(x) + popcount(y)' for byte-sized x, y */
/* unroll the loop twice so as to expose more ILP */
x = (x & 0x55) + ((x >> 1) & 0x55);
y = (y & 0x55) + ((y >> 1) & 0x55);
x = (x & 0x33) + ((x >> 2) & 0x33);
y = (y & 0x33) + ((y >> 2) & 0x33);
x = (x & 0x0F) + ((x >> 4) & 0x0F);
y = (y & 0x0F) + ((y >> 4) & 0x0F);
sum += x + y;
}
}
return sum;
}
UWord VG_(sizeSWA) ( SparseWA* swa )
{
if (swa->root)
return swa_sizeSWA_wrk ( swa->root );
else
return 0;
}
/*--------------------------------------------------------------------*/
/*--- end m_sparsewa.c ---*/
/*--------------------------------------------------------------------*/