📄 lbitset.c
字号:
/* Functions to support link list bitsets. Copyright (C) 2002, 2003, 2004 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 "lbitset.h"#include "obstack.h"#include <stddef.h>#include <stdlib.h>#include <stdio.h>#include <string.h>/* This file implements linked-list bitsets. These bitsets can be of arbitrary length and are more efficient than arrays of bits for large sparse sets. Usually if all the bits in an element are zero we remove the element from the list. However, a side effect of the bit caching is that we do not always notice when an element becomes zero. Hence the lbitset_weed function which removes zero elements. *//* Number of words to use for each element. The larger the value the greater the size of the cache and the shorter the time to find a given bit but the more memory wasted for sparse bitsets and the longer the time to search for set bits. The routines that dominate timing profiles are lbitset_elt_find and lbitset_elt_link, especially when accessing the bits randomly. */#define LBITSET_ELT_WORDS 2typedef bitset_word lbitset_word;#define LBITSET_WORD_BITS BITSET_WORD_BITS/* Number of bits stored in each element. */#define LBITSET_ELT_BITS \ ((unsigned int) (LBITSET_ELT_WORDS * LBITSET_WORD_BITS))/* Lbitset element. We use an array of bits for each element. These are linked together in a doubly-linked list. */typedef struct lbitset_elt_struct{ struct lbitset_elt_struct *next; /* Next element. */ struct lbitset_elt_struct *prev; /* Previous element. */ bitset_windex index; /* bitno / BITSET_WORD_BITS. */ bitset_word words[LBITSET_ELT_WORDS]; /* Bits that are set. */}lbitset_elt;enum lbitset_find_mode { LBITSET_FIND, LBITSET_CREATE, LBITSET_SUBST };static lbitset_elt lbitset_zero_elts[3]; /* Elements of all zero bits. *//* Obstack to allocate bitset elements from. */static struct obstack lbitset_obstack;static bool lbitset_obstack_init = false;static lbitset_elt *lbitset_free_list; /* Free list of bitset elements. */extern void debug_lbitset (bitset);#define LBITSET_CURRENT1(X) \ ((lbitset_elt *) (void *) ((char *) (X) - offsetof (lbitset_elt, words)))#define LBITSET_CURRENT(X) LBITSET_CURRENT1((X)->b.cdata)#define LBITSET_HEAD(X) ((X)->l.head)#define LBITSET_TAIL(X) ((X)->l.tail)/* Allocate a lbitset element. The bits are not cleared. */static inline lbitset_elt *lbitset_elt_alloc (void){ lbitset_elt *elt; if (lbitset_free_list != 0) { elt = lbitset_free_list; lbitset_free_list = elt->next; } else { if (!lbitset_obstack_init) { lbitset_obstack_init = true; /* Let particular systems override the size of a chunk. */#ifndef OBSTACK_CHUNK_SIZE#define OBSTACK_CHUNK_SIZE 0#endif /* Let them override the alloc and free routines too. */#ifndef OBSTACK_CHUNK_ALLOC#define OBSTACK_CHUNK_ALLOC xmalloc#endif#ifndef OBSTACK_CHUNK_FREE#define OBSTACK_CHUNK_FREE free#endif#if !defined(__GNUC__) || (__GNUC__ < 2)#define __alignof__(type) 0#endif obstack_specify_allocation (&lbitset_obstack, OBSTACK_CHUNK_SIZE, __alignof__ (lbitset_elt), OBSTACK_CHUNK_ALLOC, OBSTACK_CHUNK_FREE); } /* Perhaps we should add a number of new elements to the free list. */ elt = (lbitset_elt *) obstack_alloc (&lbitset_obstack, sizeof (lbitset_elt)); } return elt;}/* Allocate a lbitset element. The bits are cleared. */static inline lbitset_elt *lbitset_elt_calloc (void){ lbitset_elt *elt; elt = lbitset_elt_alloc (); memset (elt->words, 0, sizeof (elt->words)); return elt;}static inline voidlbitset_elt_free (lbitset_elt *elt){ elt->next = lbitset_free_list; lbitset_free_list = elt;}/* Unlink element ELT from bitset BSET. */static inline voidlbitset_elt_unlink (bitset bset, lbitset_elt *elt){ lbitset_elt *next = elt->next; lbitset_elt *prev = elt->prev; if (prev) prev->next = next; if (next) next->prev = prev; if (LBITSET_HEAD (bset) == elt) LBITSET_HEAD (bset) = next; if (LBITSET_TAIL (bset) == elt) LBITSET_TAIL (bset) = prev; /* Update cache pointer. Since the first thing we try is to insert before current, make current the next entry in preference to the previous. */ if (LBITSET_CURRENT (bset) == elt) { if (next) { bset->b.cdata = next->words; bset->b.cindex = next->index; } else if (prev) { bset->b.cdata = prev->words; bset->b.cindex = prev->index; } else { bset->b.csize = 0; bset->b.cdata = 0; } } lbitset_elt_free (elt);}/* Cut the chain of bitset BSET before element ELT and free the elements. */static inline voidlbitset_prune (bitset bset, lbitset_elt *elt){ lbitset_elt *next; if (!elt) return; if (elt->prev) { LBITSET_TAIL (bset) = elt->prev; bset->b.cdata = elt->prev->words; bset->b.cindex = elt->prev->index; elt->prev->next = 0; } else { LBITSET_HEAD (bset) = 0; LBITSET_TAIL (bset) = 0; bset->b.cdata = 0; bset->b.csize = 0; } for (; elt; elt = next) { next = elt->next; lbitset_elt_free (elt); }}/* Are all bits in an element zero? */static inline boollbitset_elt_zero_p (lbitset_elt *elt){ int i; for (i = 0; i < LBITSET_ELT_WORDS; i++) if (elt->words[i]) return false; return true;}/* Link the bitset element into the current bitset linked list. */static inline voidlbitset_elt_link (bitset bset, lbitset_elt *elt){ bitset_windex windex = elt->index; lbitset_elt *ptr; lbitset_elt *current; if (bset->b.csize) current = LBITSET_CURRENT (bset); else current = LBITSET_HEAD (bset); /* If this is the first and only element, add it in. */ if (LBITSET_HEAD (bset) == 0) { elt->next = elt->prev = 0; LBITSET_HEAD (bset) = elt; LBITSET_TAIL (bset) = elt; } /* If this index is less than that of the current element, it goes somewhere before the current element. */ else if (windex < bset->b.cindex) { for (ptr = current; ptr->prev && ptr->prev->index > windex; ptr = ptr->prev) continue; if (ptr->prev) ptr->prev->next = elt; else LBITSET_HEAD (bset) = elt; elt->prev = ptr->prev; elt->next = ptr; ptr->prev = elt; } /* Otherwise, it must go somewhere after the current element. */ else { for (ptr = current; ptr->next && ptr->next->index < windex; ptr = ptr->next) continue; if (ptr->next) ptr->next->prev = elt; else LBITSET_TAIL (bset) = elt; elt->next = ptr->next; elt->prev = ptr; ptr->next = elt; } /* Set up so this is the first element searched. */ bset->b.cindex = windex; bset->b.csize = LBITSET_ELT_WORDS; bset->b.cdata = elt->words;}static lbitset_elt *lbitset_elt_find (bitset bset, bitset_windex windex, enum lbitset_find_mode mode){ lbitset_elt *elt; lbitset_elt *current; if (bset->b.csize) { current = LBITSET_CURRENT (bset); /* Check if element is the cached element. */ if ((windex - bset->b.cindex) < bset->b.csize) return current; } else { current = LBITSET_HEAD (bset); } if (current) { if (windex < bset->b.cindex) { for (elt = current; elt->prev && elt->index > windex; elt = elt->prev) continue; } else { for (elt = current; elt->next && (elt->index + LBITSET_ELT_WORDS - 1) < windex; elt = elt->next) continue; } /* ELT is the nearest to the one we want. If it's not the one we want, the one we want does not exist. */ if (elt && (windex - elt->index) < LBITSET_ELT_WORDS) { bset->b.cindex = elt->index; bset->b.csize = LBITSET_ELT_WORDS; bset->b.cdata = elt->words; return elt; } } switch (mode) { case LBITSET_FIND: return 0; case LBITSET_CREATE: windex -= windex % LBITSET_ELT_WORDS; elt = lbitset_elt_calloc (); elt->index = windex; lbitset_elt_link (bset, elt); return elt; case LBITSET_SUBST: return &lbitset_zero_elts[0]; default: abort (); }}/* Weed out the zero elements from the list. */static inline voidlbitset_weed (bitset bset){ lbitset_elt *elt; lbitset_elt *next; for (elt = LBITSET_HEAD (bset); elt; elt = next) { next = elt->next; if (lbitset_elt_zero_p (elt)) lbitset_elt_unlink (bset, elt); }}/* Set all bits in the bitset to zero. */static voidlbitset_zero (bitset bset){ lbitset_elt *head; head = LBITSET_HEAD (bset); if (!head) return; /* Clear a bitset by freeing the linked list at the head element. */ lbitset_prune (bset, head);}/* Is DST == SRC? */static inline boollbitset_equal_p (bitset dst, bitset src){ lbitset_elt *selt; lbitset_elt *delt; int j; if (src == dst) return true; lbitset_weed (src); lbitset_weed (dst); for (selt = LBITSET_HEAD (src), delt = LBITSET_HEAD (dst); selt && delt; selt = selt->next, delt = delt->next) { if (selt->index != delt->index) return false; for (j = 0; j < LBITSET_ELT_WORDS; j++) if (delt->words[j] != selt->words[j]) return false; } return !selt && !delt;}/* Copy bits from bitset SRC to bitset DST. */static inline voidlbitset_copy (bitset dst, bitset src){ lbitset_elt *elt; lbitset_elt *head; lbitset_elt *prev; lbitset_elt *tmp; if (src == dst) return; lbitset_zero (dst); head = LBITSET_HEAD (src); if (!head) return; prev = 0; for (elt = head; elt; elt = elt->next) { tmp = lbitset_elt_alloc (); tmp->index = elt->index; tmp->prev = prev; tmp->next = 0; if (prev) prev->next = tmp; else LBITSET_HEAD (dst) = tmp; prev = tmp; memcpy (tmp->words, elt->words, sizeof (elt->words)); } LBITSET_TAIL (dst) = tmp; dst->b.csize = LBITSET_ELT_WORDS; dst->b.cdata = LBITSET_HEAD (dst)->words; dst->b.cindex = LBITSET_HEAD (dst)->index;}/* Copy bits from bitset SRC to bitset DST. Return true if bitsets different. */static inline boollbitset_copy_cmp (bitset dst, bitset src){ if (src == dst) return false; if (!LBITSET_HEAD (dst)) { lbitset_copy (dst, src); return LBITSET_HEAD (src) != 0; } if (lbitset_equal_p (dst, src)) return false; lbitset_copy (dst, src); return true;}static bitset_bindexlbitset_resize (bitset src, bitset_bindex size){ BITSET_NBITS_ (src) = size; /* Need to prune any excess bits. FIXME. */ return size;}/* Set bit BITNO in bitset DST. */static voidlbitset_set (bitset dst, bitset_bindex bitno){ bitset_windex windex = bitno / BITSET_WORD_BITS; lbitset_elt_find (dst, windex, LBITSET_CREATE); dst->b.cdata[windex - dst->b.cindex] |= (bitset_word) 1 << (bitno % BITSET_WORD_BITS);}/* Reset bit BITNO in bitset DST. */static voidlbitset_reset (bitset dst, bitset_bindex bitno){ bitset_windex windex = bitno / BITSET_WORD_BITS; if (!lbitset_elt_find (dst, windex, LBITSET_FIND)) return; dst->b.cdata[windex - dst->b.cindex] &= ~((bitset_word) 1 << (bitno % BITSET_WORD_BITS)); /* If all the data is zero, perhaps we should unlink it now... */}/* Test bit BITNO in bitset SRC. */static boollbitset_test (bitset src, bitset_bindex bitno){ bitset_windex windex = bitno / BITSET_WORD_BITS; return (lbitset_elt_find (src, windex, LBITSET_FIND) && ((src->b.cdata[windex - src->b.cindex] >> (bitno % BITSET_WORD_BITS)) & 1));}static voidlbitset_free (bitset bset){ lbitset_zero (bset);}/* 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 bitset_bindexlbitset_list_reverse (bitset bset, bitset_bindex *list, bitset_bindex num, bitset_bindex *next){ bitset_bindex rbitno; bitset_bindex bitno; unsigned int bcount; bitset_bindex boffset; bitset_windex windex; bitset_bindex count; lbitset_elt *elt; bitset_word word; bitset_bindex n_bits; elt = LBITSET_TAIL (bset); if (!elt) return 0; n_bits = (elt->index + LBITSET_ELT_WORDS) * BITSET_WORD_BITS; rbitno = *next; if (rbitno >= n_bits) return 0; bitno = n_bits - (rbitno + 1); windex = bitno / BITSET_WORD_BITS; /* Skip back to starting element. */ for (; elt && elt->index > windex; elt = elt->prev) continue; if (!elt) return 0; if (windex >= elt->index + LBITSET_ELT_WORDS) { /* We are trying to start in no-mans land so start at end of current elt. */ bcount = BITSET_WORD_BITS - 1; windex = elt->index + LBITSET_ELT_WORDS - 1; } else { bcount = bitno % BITSET_WORD_BITS; } count = 0; boffset = windex * BITSET_WORD_BITS; /* If num is 1, we could speed things up with a binary search of the word of interest. */ while (elt) { bitset_word *srcp = elt->words; for (; (windex - elt->index) < LBITSET_ELT_WORDS; windex--, boffset -= BITSET_WORD_BITS, bcount = BITSET_WORD_BITS - 1) { word = srcp[windex - elt->index] << (BITSET_WORD_BITS - 1 - bcount); for (; word; bcount--) { if (word & BITSET_MSB) { list[count++] = boffset + bcount; if (count >= num) { *next = n_bits - (boffset + bcount); return count; } } word <<= 1; } } elt = elt->prev; if (elt) { windex = elt->index + LBITSET_ELT_WORDS - 1; boffset = windex * BITSET_WORD_BITS; } } *next = n_bits - (boffset + 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 bitset_bindexlbitset_list (bitset bset, bitset_bindex *list, bitset_bindex num, bitset_bindex *next){ bitset_bindex bitno; bitset_windex windex; bitset_bindex count; lbitset_elt *elt; lbitset_elt *head; bitset_word word; head = LBITSET_HEAD (bset); if (!head) return 0; bitno = *next; count = 0; if (!bitno) { /* This is the most common case. */ /* Start with the first element. */ elt = head; windex = elt->index; bitno = windex * BITSET_WORD_BITS; } else { windex = bitno / BITSET_WORD_BITS; /* Skip to starting element. */ for (elt = head; elt && (elt->index + LBITSET_ELT_WORDS - 1) < windex; elt = elt->next) continue; if (!elt) return 0; if (windex < elt->index) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -