/* * Copyright (c) 2005, 2006, 2007 Tama Communications Corporation * * This file is part of GNU GLOBAL. * * 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 3 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, see . */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef HAVE_LIMITS_H #include #endif #include "checkalloc.h" #include "die.h" #include "idset.h" #ifndef CHAR_BIT #define CHAR_BIT 8 #endif #undef LONG_BIT /** maybe 32 or 64 */ #define LONG_BIT (sizeof(long) * CHAR_BIT) /* * idset->min is initialized to END_OF_ID. * (You may use idset->max instead of idset->min.) */ #define IS_EMPTY(idset) ((idset)->min == END_OF_ID ? 1 : 0) /* Idset: usage and memory status idset->set [] idset = idset_open(21) 000000000000000000000___________ v idset_add(idset, 1) 010000000000000000000___________ v idset_add(idset, 2) 011000000000000000000___________ v idset_add(idset, 20) 011000000000000000001___________ idset_contains(idset, 2) == true idset_contains(idset, 3) == false idset_close(idset) [] Idset's index always start from 0 according to the custom of C language. I you want to treat 1-20 then you must invoke idset_open() with a argument 21. idset = idset_open(21); idset_add(idset, 0); => OK idset_add(idset, 1); => OK ... idset_add(idset, 20); => OK idset_add(idset, 21); => ERROR (idset_add: id is out of range.) The range of value is from 0 to the maximum value expressible by unsigned integer - 1. You should define index as an unsigned integer, and use END_OF_ID like follows: unsigned int id; for (id = idset_first(set); id != END_OF_ID; id = idset_next(set)) -- processing about an id -- */ /** * bit mask table * Prepare all bit mask for performance. */ static unsigned long *bit; /** * Allocate memory for new idset. */ IDSET * idset_open(unsigned int size) { IDSET *idset = (IDSET *)check_malloc(sizeof(IDSET)); int i; if (bit == NULL) { bit = (unsigned long *)check_calloc(sizeof(unsigned long), LONG_BIT); for (i = 0; i < LONG_BIT; i++) bit[i] = 1UL << i; } idset->set = (unsigned long *)check_calloc(sizeof(unsigned long), (size + LONG_BIT - 1) / LONG_BIT); idset->size = size; /* * Initialize all id expressions using invalid value. * END_OF_ID means 'no value' or 'out of range'. */ idset->min = idset->max = idset->lastid = END_OF_ID; return idset; } /** * Return true if idset is empty. * * @param[in] idset idset structure * @return 1: empty, 0: not empty */ int idset_empty(IDSET *idset) { return IS_EMPTY(idset); } /** * Add id to the idset. * * @param[in] idset idset structure * @param[in] id id number */ void idset_add(IDSET *idset, unsigned int id) { int empty = IS_EMPTY(idset); if (id >= idset->size) die("idset_add: id is out of range."); idset->set[id / LONG_BIT] |= bit[id % LONG_BIT]; if (empty) idset->max = idset->min = id; else if (id > idset->max) idset->max = id; else if (id < idset->min) idset->min = id; } /** * Whether or not idset includes specified id. * * @param[in] idset idset structure * @param[in] id id number * @return true: contains, false: doesn't contain */ int idset_contains(IDSET *idset, unsigned int id) { if (IS_EMPTY(idset)) return 0; if (id < idset->min || id > idset->max) return 0; return (idset->set[id / LONG_BIT] & bit[id % LONG_BIT]) != 0; } /** * Get first id. * * @param[in] idset idset structure * @return id (END_OF_ID: end of id) * */ unsigned int idset_first(IDSET *idset) { /* There is no need to check whether idset is empty because idset->min is initialized with END_OF_ID. */ return idset->lastid = idset->min; } /** * Get next id. * * @param[in] idset idset structure * @return id (END_OF_ID: end of id) * */ unsigned int idset_next(IDSET *idset) { unsigned int i, limit; int index0, index1; if (IS_EMPTY(idset)) return END_OF_ID; if (idset->lastid >= idset->max) return END_OF_ID; limit = idset->max / LONG_BIT + 1; index0 = idset->lastid / LONG_BIT; index1 = idset->lastid % LONG_BIT; for (i = ++index1; i < LONG_BIT; i++) if (bit[i] & idset->set[index0]) return idset->lastid = index0 * LONG_BIT + i; index0++; for (i = index0; i < limit && idset->set[i] == 0; i++) ; if (i >= limit) die("idset_next: internal error."); index0 = i; for (i = 0; i < LONG_BIT; i++) if (bit[i] & idset->set[index0]) return idset->lastid = index0 * LONG_BIT + i; die("idset_next: internal error."); } /** * Return the number of bits. * * @param[in] idset idset structure * @return number of bits */ unsigned int idset_count(IDSET *idset) { unsigned int id, count = 0; for (id = idset_first(idset); id != END_OF_ID; id = idset_next(idset)) count++; return count; } /** * Free memory for the idset. */ void idset_close(IDSET *idset) { free(idset->set); free(idset); }