]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add a new container data type, a Sparse Word Array: an array of UWord
authorJulian Seward <jseward@acm.org>
Sat, 6 Dec 2008 22:07:35 +0000 (22:07 +0000)
committerJulian Seward <jseward@acm.org>
Sat, 6 Dec 2008 22:07:35 +0000 (22:07 +0000)
which is also indexed by UWord.  This can be used as a replacement for
WordFM with unboxed keys, when the key ranges are dense.  It is
implemented as a 256-way radix tree (4-deep on 32-bit platforms,
8-deep on 64-bit platforms).

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@8806

coregrind/Makefile.am
coregrind/m_sparsewa.c [new file with mode: 0644]
coregrind/pub_core_sparsewa.h [new file with mode: 0644]
include/Makefile.am
include/pub_tool_sparsewa.h [new file with mode: 0644]

index ac3e1191cfda9fe88dd39871fd4b98c2d01fa890..619653b9a30e2159f5de5e026f9f657498731435 100644 (file)
@@ -123,6 +123,7 @@ noinst_HEADERS = \
        pub_core_scheduler.h    \
        pub_core_sigframe.h     \
        pub_core_signals.h      \
+       pub_core_sparsewa.h     \
        pub_core_stacks.h       \
        pub_core_stacktrace.h   \
        pub_core_syscall.h      \
@@ -191,6 +192,7 @@ COREGRIND_SOURCES_COMMON = \
        m_oset.c \
        m_redir.c \
        m_signals.c \
+       m_sparsewa.c \
        m_stacks.c \
        m_stacktrace.c \
        m_syscall.c \
diff --git a/coregrind/m_sparsewa.c b/coregrind/m_sparsewa.c
new file mode 100644 (file)
index 0000000..9fad5ba
--- /dev/null
@@ -0,0 +1,432 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An sparse array (of words) implementation.                   ---*/
+/*---                                                 m_sparsewa.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 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)
+
+inline
+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;
+}
+
+inline
+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;
+}
+
+/*--------------------------------------------------------------------*/
+/*--- end                                             m_sparsewa.c ---*/
+/*--------------------------------------------------------------------*/
diff --git a/coregrind/pub_core_sparsewa.h b/coregrind/pub_core_sparsewa.h
new file mode 100644 (file)
index 0000000..95b0899
--- /dev/null
@@ -0,0 +1,51 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An sparse array (of words) implementation.                   ---*/
+/*---                                          pub_core_sparsewa.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 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.
+*/
+
+#ifndef __PUB_CORE_SPARSEWA_H
+#define __PUB_CORE_SPARSEWA_H
+
+//--------------------------------------------------------------------
+// PURPOSE: Provides an implementation of a sparse array of host words
+// (UWord).  The indices are themselves host words.  The implementation
+// uses a 256-way radix tree, which is therefore 4 levels deep on a 
+// 32-bit platform and 8 levels deep on a 64-bit platform.
+//--------------------------------------------------------------------
+
+// No core-only exports; everything in this module is visible to both
+// the core and tools.
+
+#include "pub_tool_sparsewa.h"
+
+#endif   // __PUB_CORE_SPARSEWA_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                      pub_core_sparsewa.h ---*/
+/*--------------------------------------------------------------------*/
index 70d32a698506a602bb32cfe74d6b96e60d929d63..8e45202be5918e0d1e2cfda0ed06b54c04e5dddb 100644 (file)
@@ -27,6 +27,7 @@ incinc_HEADERS = \
        pub_tool_redir.h                \
        pub_tool_replacemalloc.h        \
        pub_tool_signals.h              \
+       pub_tool_sparsewa.h             \
        pub_tool_stacktrace.h           \
        pub_tool_threadstate.h          \
        pub_tool_tooliface.h            \
diff --git a/include/pub_tool_sparsewa.h b/include/pub_tool_sparsewa.h
new file mode 100644 (file)
index 0000000..91f5b58
--- /dev/null
@@ -0,0 +1,95 @@
+
+/*--------------------------------------------------------------------*/
+/*--- An sparse array (of words) implementation.                   ---*/
+/*---                                          pub_tool_sparsewa.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2008-2008 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.
+*/
+
+#ifndef __PUB_TOOL_SPARSEWA_H
+#define __PUB_TOOL_SPARSEWA_H
+
+//--------------------------------------------------------------------
+// PURPOSE: (see coregrind/pub_core_sparsewa.h for details)
+//--------------------------------------------------------------------
+
+/////////////////////////////////////////////////////////
+//                                                     //
+// SparseWA: Interface                                 //
+//                                                     //
+/////////////////////////////////////////////////////////
+
+// This interface is a very cut-down version of WordFM.
+// If you understand how to use WordFM then it should be
+// trivial to use SparseWA.
+
+typedef  struct _SparseWA  SparseWA; /* opaque */
+
+// Create a new one, using the specified allocator/deallocator
+SparseWA* VG_(newSWA) ( void*(*alloc_nofail)(HChar* cc, SizeT), 
+                        HChar* cc,
+                        void(*dealloc)(void*) );
+
+// Delete one, and free all associated storage
+void VG_(deleteSWA) ( SparseWA* swa );
+
+// Add the binding key -> val to this swa.  Any existing binding is
+// overwritten.  Returned Bool is True iff a previous binding existed.
+Bool VG_(addToSWA) ( SparseWA* swa, UWord key, UWord val );
+
+// Delete key from swa, returning associated key and val if found.
+// Note: returning associated key is stupid (it can only be the
+// key you just specified).  This behaviour is retained to make it
+// easier to migrate from WordFM.  Returned Bool is True iff
+// the key was actually bound in the mapping.
+Bool VG_(delFromSWA) ( SparseWA* swa,
+                       /*OUT*/UWord* oldK, /*OUT*/UWord* oldV,
+                       UWord key );
+
+// Indexes swa at 'key' (or, if you like, looks up 'key' in the
+// mapping), and returns the associated value, if any, in *valP.  For
+// compatibility with WordFM, 'key' is also returned in *keyP.  Returned
+// Bool is True iff a binding for 'key' actually existed.
+Bool VG_(lookupSWA) ( SparseWA* swa,
+                      /*OUT*/UWord* keyP, /*OUT*/UWord* valP,
+                      UWord key );
+
+// Set up 'swa' for iteration.
+void VG_(initIterSWA) ( SparseWA* swa );
+
+// Get the next key/val pair.  Behaviour undefined (highly likely 
+// to segfault) if 'swa' has been modified since initIterSWA was
+// called.  Returned Bool is False iff there are no more pairs
+// that can be extracted.
+Bool VG_(nextIterSWA)( SparseWA* swa,
+                       /*OUT*/UWord* keyP, /*OUT*/UWord* valP );
+
+
+#endif   // __PUB_TOOL_SPARSEWA_H
+
+/*--------------------------------------------------------------------*/
+/*--- end                                      pub_tool_sparsewa.h ---*/
+/*--------------------------------------------------------------------*/