]> git.saurik.com Git - bison.git/blobdiff - lib/sbitset.c
* lib/bitset-int.h, lib/bitset.c, lib/bitset.h, lib/bitsetv.c,
[bison.git] / lib / sbitset.c
diff --git a/lib/sbitset.c b/lib/sbitset.c
new file mode 100644 (file)
index 0000000..807b71d
--- /dev/null
@@ -0,0 +1,672 @@
+/* Simple bitsets.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+   Contributed by Michael Hayes (m.hayes@elec.canterbury.ac.nz).
+
+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.  */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "bitset.h"
+#include <stdlib.h>
+
+# if WITH_DMALLOC
+#  define DMALLOC_FUNC_CHECK
+#  include <dmalloc.h>
+# endif /* WITH_DMALLOC */
+
+/* This file implements fixed size bitsets stored as an array
+   of words.  Any unused bits in the last word must be zero.  */
+
+static void sbitset_unused_clear PARAMS((bitset));
+
+static int sbitset_small_list PARAMS((bitset, bitset_bindex *, bitset_bindex,
+                                     bitset_bindex *));
+
+static void sbitset_set PARAMS((bitset, bitset_bindex));
+static void sbitset_reset PARAMS((bitset, bitset_bindex));
+static int sbitset_test PARAMS((bitset, bitset_bindex));
+static int sbitset_size PARAMS((bitset));
+static int sbitset_op1 PARAMS((bitset, enum bitset_ops));
+static int sbitset_op2 PARAMS((bitset, bitset, enum bitset_ops));
+static int sbitset_op3 PARAMS((bitset, bitset, bitset, enum bitset_ops));
+static int sbitset_op4 PARAMS((bitset, bitset, bitset, bitset,
+                              enum bitset_ops));
+static int sbitset_list PARAMS((bitset, bitset_bindex *, bitset_bindex,
+                               bitset_bindex *));
+static int sbitset_reverse_list PARAMS((bitset, bitset_bindex *, bitset_bindex,
+                                       bitset_bindex *));
+
+#define SBITSET_N_WORDS(N) (((N) + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS)
+#define SBITSET_WORDS(X) ((X)->u.s.words)
+#define SBITSET_N_BITS(X) ((X)->u.s.n_bits)
+
+
+/* Return size in bits of bitset SRC.  */
+static int
+sbitset_size (src)
+     bitset src;
+{
+    return SBITSET_N_BITS (src);
+}
+
+
+/* Find list of up to NUM bits set in BSET starting from and including
+   *NEXT and store in array LIST.  Return with actual number of bits
+   found and with *NEXT indicating where search stopped.  */
+static int
+sbitset_small_list (src, list, num, next)
+     bitset src;
+     bitset_bindex *list;
+     bitset_bindex num;
+     bitset_bindex *next;
+{
+  bitset_bindex bitno;
+  bitset_bindex count;
+  bitset_windex size;
+  bitset_word word;
+
+  word = SBITSET_WORDS (src)[0];
+
+  /* Short circuit common case.  */
+  if (!word)
+    return 0;
+
+  size = SBITSET_N_BITS (src);
+  bitno = *next;
+  if (bitno >= size)
+    return 0;
+
+  word >>= bitno;
+
+  /* If num is 1, we could speed things up with a binary search
+     of the word of interest.  */
+
+  if (num >= BITSET_WORD_BITS)
+  {
+      for (count = 0; word; bitno++)
+      {
+         if (word & 1)
+             list[count++] = bitno;
+         word >>= 1;
+      }
+  }
+  else
+  {
+      for (count = 0; word; bitno++)
+      {
+         if (word & 1)
+         {
+             list[count++] = bitno;
+             if (count >= num)
+             {
+                 bitno++;
+                 break;
+             }
+         }
+         word >>= 1;
+      }
+  }
+
+  *next = bitno;
+  return count;
+}
+
+
+/* Set bit BITNO in bitset DST.  */
+static void
+sbitset_set (dst, bitno)
+     bitset dst ATTRIBUTE_UNUSED;
+     bitset_bindex bitno ATTRIBUTE_UNUSED;
+{
+    /* This should never occur for sbitsets.  */
+    abort ();
+}
+
+
+/* Reset bit BITNO in bitset DST.  */
+static void
+sbitset_reset (dst, bitno)
+     bitset dst ATTRIBUTE_UNUSED;
+     bitset_bindex bitno ATTRIBUTE_UNUSED;
+{
+    /* This should never occur for sbitsets.  */
+    abort ();
+}
+
+
+/* Test bit BITNO in bitset SRC.  */
+static int
+sbitset_test (src, bitno)
+     bitset src ATTRIBUTE_UNUSED;
+     bitset_bindex bitno ATTRIBUTE_UNUSED;
+{
+    /* This should never occur for sbitsets.  */
+    abort ();
+    return 0;
+}
+
+
+/* Find list of up to NUM bits set in BSET in reverse order, starting
+   from and including NEXT and store in array LIST.  Return with
+   actual number of bits found and with *NEXT indicating where search
+   stopped.  */
+static int
+sbitset_reverse_list (src, list, num, next)
+     bitset src;
+     bitset_bindex *list;
+     bitset_bindex num;
+     bitset_bindex *next;
+{
+  bitset_bindex bitno;
+  bitset_bindex rbitno;
+  bitset_bindex count;
+  bitset_windex index;
+  unsigned int bitcnt;
+  bitset_bindex bitoff;
+  bitset_word word;
+  bitset_word *srcp = SBITSET_WORDS (src);
+  bitset_bindex n_bits = SBITSET_N_BITS (src);
+
+  rbitno = *next;
+
+  /* If num is 1, we could speed things up with a binary search
+     of the word of interest.  */
+
+  if (rbitno >= n_bits)
+      return 0;
+
+  count = 0;
+
+  bitno = n_bits - (rbitno + 1);
+
+  index = bitno / BITSET_WORD_BITS;
+  bitcnt = bitno % BITSET_WORD_BITS;
+  bitoff = index * BITSET_WORD_BITS;
+
+  for (; index != ~0U; index--, bitoff -= BITSET_WORD_BITS,
+          bitcnt = BITSET_WORD_BITS - 1)
+    {
+      word = srcp[index] << (BITSET_WORD_BITS - 1 - bitcnt);
+      for (; word; bitcnt--)
+       {
+         if (word & BITSET_MSB)
+           {
+             list[count++] = bitoff + bitcnt;
+             if (count >= num)
+               {
+                 *next = n_bits - (bitoff + bitcnt);
+                 return count;
+               }
+           }
+         word <<= 1;
+       }
+    }
+
+  *next = n_bits - (bitoff + 1);
+  return count;
+}
+
+
+/* Find list of up to NUM bits set in BSET starting from and including
+   *NEXT and store in array LIST.  Return with actual number of bits
+   found and with *NEXT indicating where search stopped.  */
+static int
+sbitset_list (src, list, num, next)
+     bitset src;
+     bitset_bindex *list;
+     bitset_bindex num;
+     bitset_bindex *next;
+{
+  bitset_bindex bitno;
+  bitset_bindex count;
+  bitset_windex index;
+  bitset_bindex bitoff;
+  bitset_windex size = src->csize;
+  bitset_word *srcp = SBITSET_WORDS (src);
+  bitset_word word;
+
+  bitno = *next;
+
+  count = 0;
+  if (! bitno)
+    {
+      /* Many bitsets are zero, so make this common case fast.  */
+      for (index = 0; index < size && ! srcp[index]; index++)
+       continue;
+      if (index >= size)
+       return 0;
+
+      /* If num is 1, we could speed things up with a binary search
+        of the current word.  */
+
+      bitoff = index * BITSET_WORD_BITS;
+    }
+  else
+    {
+      if (bitno >= SBITSET_N_BITS (src))
+       return 0;
+
+      index = bitno / BITSET_WORD_BITS;
+      bitno = bitno % BITSET_WORD_BITS;
+
+      if (bitno)
+       {
+         /* Handle the case where we start within a word.
+            Most often, this is executed with large bitsets
+            with many set bits where we filled the array
+            on the previous call to this function.  */
+
+         bitoff = index * BITSET_WORD_BITS;
+         word = srcp[index] >> bitno;
+         for (bitno = bitoff + bitno; word; bitno++)
+           {
+             if (word & 1)
+               {
+                 list[count++] = bitno;
+                 if (count >= num)
+                   {
+                     *next = bitno + 1;
+                     return count;
+                   }
+               }
+             word >>= 1;
+           }
+         index++;
+       }
+      bitoff = index * BITSET_WORD_BITS;
+    }
+
+  for (; index < size; index++, bitoff += BITSET_WORD_BITS)
+    {
+      if (! (word = srcp[index]))
+       continue;
+
+      if ((count + BITSET_WORD_BITS) < num)
+      {
+       for (bitno = bitoff; word; bitno++)
+         {
+           if (word & 1)
+             list[count++] = bitno;
+           word >>= 1;
+         }
+      }
+      else
+       {
+         for (bitno = bitoff; word; bitno++)
+           {
+             if (word & 1)
+               {
+                 list[count++] = bitno;
+                 if (count >= num)
+                   {
+                     *next = bitno + 1;
+                     return count;
+                   }
+               }
+             word >>= 1;
+           }
+       }
+    }
+
+  *next = bitoff;
+  return count;
+}
+
+
+/* Ensure that any unused bits within the last word are clear.  */
+static inline void
+sbitset_unused_clear (dst)
+     bitset dst;
+{
+  unsigned int last_bit;
+
+  last_bit = SBITSET_N_BITS (dst) % BITSET_WORD_BITS;
+  if (last_bit)
+    SBITSET_WORDS (dst)[dst->csize - 1] &= (bitset_word)((1 << last_bit) - 1);
+}
+
+
+static int
+sbitset_op1 (dst, op)
+     bitset dst;
+     enum bitset_ops op;
+{
+  unsigned int i;
+  bitset_word *dstp = SBITSET_WORDS (dst);
+  unsigned int bytes;
+
+  bytes = sizeof (bitset_word) * dst->csize;
+
+  switch (op)
+    {
+    case BITSET_ZERO:
+      memset (dstp, 0, bytes);
+      break;
+
+    case BITSET_ONES:
+      memset (dstp, ~0, bytes);
+      sbitset_unused_clear (dst);
+      break;
+
+    case BITSET_EMPTY_P:
+      for (i = 0; i < dst->csize; i++)
+       if (dstp[i])
+         return 0;
+      break;
+
+    default:
+      abort ();
+    }
+
+  return 1;
+}
+
+
+static int
+sbitset_op2 (dst, src, op)
+     bitset dst;
+     bitset src;
+     enum bitset_ops op;
+{
+  unsigned int i;
+  bitset_word *srcp = SBITSET_WORDS (src);
+  bitset_word *dstp = SBITSET_WORDS (dst);
+  bitset_windex size = dst->csize;
+
+#ifdef ENABLE_CHECKING
+  /* Check for compatiblity.  */
+  if (src->ops != dst->ops || SBITSET_N_BITS (src) != SBITSET_N_BITS (dst))
+    abort ();
+#endif
+
+  switch (op)
+    {
+    case BITSET_COPY:
+      if (srcp == dstp)
+       break;
+      memcpy (dstp, srcp, sizeof (bitset_word) * size);
+      break;
+
+    case BITSET_NOT:
+      for (i = 0; i < size; i++)
+       *dstp++ = ~(*srcp++);
+      sbitset_unused_clear (dst);
+      break;
+
+    case BITSET_EQUAL_P:
+      for (i = 0; i < size; i++)
+       if (*dstp++ != *srcp++)
+         return 0;
+      break;
+
+    case BITSET_SUBSET_P:
+      for (i = 0; i < size; i++, dstp++, srcp++)
+       {
+         if (*dstp != (*srcp | *dstp))
+           return 0;
+       }
+      break;
+
+    default:
+      abort ();
+    }
+
+  return 1;
+}
+
+
+static int
+sbitset_op3 (dst, src1, src2, op)
+     bitset dst;
+     bitset src1;
+     bitset src2;
+     enum bitset_ops op;
+{
+  unsigned int i;
+  int changed = 0;
+  bitset_word *src1p = SBITSET_WORDS (src1);
+  bitset_word *src2p = SBITSET_WORDS (src2);
+  bitset_word *dstp = SBITSET_WORDS (dst);
+  bitset_windex size = dst->csize;
+
+#ifdef ENABLE_CHECKING
+  /* Check for compatiblity.  */
+  if (src1->ops != dst->ops || SBITSET_N_BITS (src1) != SBITSET_N_BITS (dst)
+      || src2->ops != dst->ops || SBITSET_N_BITS (src2) != SBITSET_N_BITS (dst))
+    abort ();
+#endif
+
+  switch (op)
+    {
+    case BITSET_OR:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = *src1p++ | *src2p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_AND:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = *src1p++ & *src2p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_XOR:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = *src1p++ ^ *src2p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_ANDN:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = *src1p++ & ~(*src2p++);
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_ORN:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = *src1p++ | ~(*src2p++);
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      sbitset_unused_clear (dst);
+      break;
+
+    default:
+      abort ();
+    }
+
+  return changed;
+}
+
+
+static int
+sbitset_op4 (dst, src1, src2, src3, op)
+     bitset dst;
+     bitset src1;
+     bitset src2;
+     bitset src3;
+     enum bitset_ops op;
+{
+  unsigned int i;
+  int changed = 0;
+  bitset_word *src1p = SBITSET_WORDS (src1);
+  bitset_word *src2p = SBITSET_WORDS (src2);
+  bitset_word *src3p = SBITSET_WORDS (src3);
+  bitset_word *dstp = SBITSET_WORDS (dst);
+  bitset_windex size = dst->csize;
+
+#ifdef ENABLE_CHECKING
+  /* Check for compatiblity.  */
+  if (src1->ops != dst->ops || SBITSET_N_BITS (src1) != SBITSET_N_BITS (dst)
+      || src2->ops != dst->ops || SBITSET_N_BITS (src2) != SBITSET_N_BITS (dst)
+      || src3->ops != dst->ops || SBITSET_N_BITS (src3) != SBITSET_N_BITS (dst))
+    abort ();
+#endif
+
+  switch (op)
+    {
+    case BITSET_OR_AND:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = (*src1p++ | *src2p++) & *src3p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_AND_OR:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = (*src1p++ & *src2p++) | *src3p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    case BITSET_ANDN_OR:
+      for (i = 0; i < size; i++, dstp++)
+       {
+         bitset_word tmp = (*src1p++ & ~(*src2p++)) | *src3p++;
+
+         if (*dstp != tmp)
+           {
+             changed = 1;
+             *dstp = tmp;
+           }
+       }
+      break;
+
+    default:
+      abort ();
+    }
+
+  return changed;
+}
+
+
+/* Vector of operations for single word bitsets.  */
+struct bitset_ops_struct sbitset_small_ops =
+  {
+    sbitset_set,
+    sbitset_reset,
+    sbitset_test,
+    sbitset_size,
+    sbitset_op1,
+    sbitset_op2,
+    sbitset_op3,
+    sbitset_op4,
+    sbitset_small_list,
+    sbitset_reverse_list,
+    NULL,
+    BITSET_ARRAY
+  };
+
+
+/* Vector of operations for multiple word bitsets.  */
+struct bitset_ops_struct sbitset_ops =
+  {
+    sbitset_set,
+    sbitset_reset,
+    sbitset_test,
+    sbitset_size,
+    sbitset_op1,
+    sbitset_op2,
+    sbitset_op3,
+    sbitset_op4,
+    sbitset_list,
+    sbitset_reverse_list,
+    NULL,
+    BITSET_ARRAY
+};
+
+
+int
+sbitset_bytes (n_bits)
+     bitset_bindex n_bits;
+{
+  unsigned int bytes, size;
+
+  size = SBITSET_N_WORDS (n_bits);
+  bytes = size * sizeof (bitset_word);
+  return sizeof (struct bitset_struct) + bytes - sizeof (bitset_word);
+}
+
+
+bitset
+sbitset_init (bset, n_bits)
+     bitset bset;
+     bitset_bindex n_bits;
+{
+  bitset_windex size;
+
+  size = SBITSET_N_WORDS (n_bits);
+  SBITSET_N_BITS (bset) = n_bits;
+
+  /* Use optimized routines if bitset fits within a single word.
+     There is probably little merit if using caching since
+     the small bitset will always fit in the cache.  */
+  if (size == 1)
+    bset->ops = &sbitset_small_ops;
+  else
+    bset->ops = &sbitset_ops;
+
+  bset->cindex = 0;
+  bset->csize = size;
+  bset->cdata = SBITSET_WORDS (bset);
+  return bset;
+}