| /*****************************************************************************\ | |
| * bitstring.c - bitmap manipulation functions | |
| ***************************************************************************** | |
| * See comments about origin, limitations, and internal structure in | |
| * bitstring.h. | |
| * | |
| * Copyright (C) 2002 The Regents of the University of California. | |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). | |
| * Written by Jim Garlick <garlick@llnl.gov>, Morris Jette <jette1@llnl.gov> | |
| * CODE-OCEC-09-009. All rights reserved. | |
| * | |
| * This file is part of Slurm, a resource management program. | |
| * For details, see <https://slurm.schedmd.com/>. | |
| * Please also read the included file: DISCLAIMER. | |
| * | |
| * Slurm 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. | |
| * | |
| * In addition, as a special exception, the copyright holders give permission | |
| * to link the code of portions of this program with the OpenSSL library under | |
| * certain conditions as described in each individual source file, and | |
| * distribute linked combinations including the two. You must obey the GNU | |
| * General Public License in all respects for all of the code used other than | |
| * OpenSSL. If you modify file(s) with this exception, you may extend this | |
| * exception to your version of the file(s), but you are not obligated to do | |
| * so. If you do not wish to do so, delete this exception statement from your | |
| * version. If you delete this exception statement from all source files in | |
| * the program, then also delete it here. | |
| * | |
| * Slurm 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 Slurm; if not, write to the Free Software Foundation, Inc., | |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| \*****************************************************************************/ | |
| #include "config.h" | |
| #include <ctype.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "src/common/bitstring.h" | |
| #include "src/common/log.h" | |
| #include "src/common/macros.h" | |
| #include "src/common/xassert.h" | |
| #include "src/common/xmalloc.h" | |
| #include "src/common/xstring.h" | |
| #define BITSTR_MAGIC 0x42434445 | |
| #define BITSTR_MAXPOS (BITSTR_WORD_SIZE - 1) | |
| #define BITSTR_OVERHEAD 2 | |
| #define BITSTR_SHIFT 6 | |
| #define BITSTR_SHIFT_WORD8 3 | |
| #define BITSTR_SHIFT_WORD64 6 | |
| #define BITSTR_MAXVAL 0xffffffffffffffff | |
| #define BITSTR_WORD_SIZE (sizeof(bitstr_t) * 8) | |
| /* word of the bitstring bit is in */ | |
| #define _bit_word(bit) (((bit) >> BITSTR_SHIFT) + BITSTR_OVERHEAD) | |
| /* address of the byte containing bit */ | |
| #define _bit_byteaddr(name, bit) \ | |
| ((char *)((name) + BITSTR_OVERHEAD) + ((bit) >> BITSTR_SHIFT_WORD8)) | |
| /* mask for the bit within its word */ | |
| #ifdef SLURM_BIGENDIAN | |
| #define _bit_mask(bit) ((bitstr_t)1 << (BITSTR_MAXPOS - ((bit)&BITSTR_MAXPOS))) | |
| #else | |
| #define _bit_mask(bit) ((bitstr_t)1 << ((bit)&BITSTR_MAXPOS)) | |
| #endif | |
| /* mask for less significant bits within their word*/ | |
| #ifdef SLURM_BIGENDIAN | |
| #define _bit_nmask(n) \ | |
| ((~((bitstr_t) 0)) << ((BITSTR_MAXPOS + 1) - ((n) & BITSTR_MAXPOS))) | |
| #else | |
| #define _bit_nmask(n) \ | |
| (((bitstr_t) 1 << ((n) & BITSTR_MAXPOS)) - 1) | |
| #endif | |
| /* number of bits actually allocated to a bitstr */ | |
| #define _bitstr_bits(name) ((name)[1]) | |
| /* magic cookie stored here */ | |
| #define _bitstr_magic(name) ((name)[0]) | |
| /* words in a bitstring of nbits bits */ | |
| #define _bitstr_words(nbits) \ | |
| ((((nbits) + BITSTR_MAXPOS) >> BITSTR_SHIFT) + BITSTR_OVERHEAD) | |
| #undef bit_test | |
| #define bit_test(name, bit) \ | |
| ((((name)[_bit_word(bit)]) & _bit_mask(bit)) ? 1 : 0) | |
| /* check signature */ | |
| #define _assert_bitstr_valid(name) do { \ | |
| xassert((name) != NULL); \ | |
| xassert(_bitstr_magic(name) == BITSTR_MAGIC); \ | |
| } while (0) | |
| /* check bit position */ | |
| #define _assert_bit_valid(name,bit) do { \ | |
| xassert((bit) >= 0); \ | |
| xassert((bit) < _bitstr_bits(name)); \ | |
| } while (0) | |
| /* Ensure valid bitmap size, prevent overflow in buffer size calculation */ | |
| #define _assert_valid_size(bit) do { \ | |
| xassert((bit) > 0); \ | |
| xassert((bit) <= 0x40000000); \ | |
| } while (0) | |
| /* | |
| * Define slurm-specific aliases for use by plugins, see slurm_xlator.h | |
| * for details. | |
| */ | |
| strong_alias(bit_alloc, slurm_bit_alloc); | |
| strong_alias(bit_set, slurm_bit_set); | |
| strong_alias(bit_clear, slurm_bit_clear); | |
| strong_alias(bit_nclear, slurm_bit_nclear); | |
| strong_alias(bit_nset, slurm_bit_nset); | |
| strong_alias(bit_set_all, slurm_bit_set_all); | |
| strong_alias(bit_clear_all, slurm_bit_clear_all); | |
| strong_alias(bit_ffc, slurm_bit_ffc); | |
| strong_alias(bit_ffs, slurm_bit_ffs); | |
| strong_alias(bit_size, slurm_bit_size); | |
| strong_alias(bit_and, slurm_bit_and); | |
| strong_alias(bit_not, slurm_bit_not); | |
| strong_alias(bit_or, slurm_bit_or); | |
| strong_alias(bit_set_count, slurm_bit_set_count); | |
| strong_alias(bit_set_count_range, slurm_bit_set_count_range); | |
| strong_alias(bit_clear_count, slurm_bit_clear_count); | |
| strong_alias(bit_rotate_copy, slurm_bit_rotate_copy); | |
| strong_alias(bit_rotate, slurm_bit_rotate); | |
| strong_alias(bit_fmt, slurm_bit_fmt); | |
| strong_alias(bit_fmt_full, slurm_bit_fmt_full); | |
| strong_alias(bit_unfmt, slurm_bit_unfmt); | |
| strong_alias(bitfmt2int, slurm_bitfmt2int); | |
| strong_alias(bit_fmt_hexmask, slurm_bit_fmt_hexmask); | |
| strong_alias(bit_fmt_hexmask_trim, slurm_bit_fmt_hexmask_trim); | |
| strong_alias(bit_unfmt_hexmask, slurm_bit_unfmt_hexmask); | |
| strong_alias(bit_fls, slurm_bit_fls); | |
| strong_alias(bit_fls_from_bit, slurm_bit_fls_from_bit); | |
| strong_alias(bit_fill_gaps, slurm_bit_fill_gaps); | |
| strong_alias(bit_super_set, slurm_bit_super_set); | |
| strong_alias(bit_overlap, slurm_bit_overlap); | |
| strong_alias(bit_overlap_any, slurm_bit_overlap_any); | |
| strong_alias(bit_equal, slurm_bit_equal); | |
| strong_alias(bit_copy, slurm_bit_copy); | |
| strong_alias(bit_pick_cnt, slurm_bit_pick_cnt); | |
| strong_alias(bit_copybits, slurm_bit_copybits); | |
| strong_alias(bit_get_bit_num, slurm_bit_get_bit_num); | |
| #ifdef SLURM_BIGENDIAN | |
| static const char* hexmask_lookup[256] = { | |
| "00", "80", "40", "C0", "20", "A0", "60", "E0", | |
| "10", "90", "50", "D0", "30", "B0", "70", "F0", | |
| "08", "88", "48", "C8", "28", "A8", "68", "E8", | |
| "18", "98", "58", "D8", "38", "B8", "78", "F8", | |
| "04", "84", "44", "C4", "24", "A4", "64", "E4", | |
| "14", "94", "54", "D4", "34", "B4", "74", "F4", | |
| "0C", "8C", "4C", "CC", "2C", "AC", "6C", "EC", | |
| "1C", "9C", "5C", "DC", "3C", "BC", "7C", "FC", | |
| "02", "82", "42", "C2", "22", "A2", "62", "E2", | |
| "12", "92", "52", "D2", "32", "B2", "72", "F2", | |
| "0A", "8A", "4A", "CA", "2A", "AA", "6A", "EA", | |
| "1A", "9A", "5A", "DA", "3A", "BA", "7A", "FA", | |
| "06", "86", "46", "C6", "26", "A6", "66", "E6", | |
| "16", "96", "56", "D6", "36", "B6", "76", "F6", | |
| "0E", "8E", "4E", "CE", "2E", "AE", "6E", "EE", | |
| "1E", "9E", "5E", "DE", "3E", "BE", "7E", "FE", | |
| "01", "81", "41", "C1", "21", "A1", "61", "E1", | |
| "11", "91", "51", "D1", "31", "B1", "71", "F1", | |
| "09", "89", "49", "C9", "29", "A9", "69", "E9", | |
| "19", "99", "59", "D9", "39", "B9", "79", "F9", | |
| "05", "85", "45", "C5", "25", "A5", "65", "E5", | |
| "15", "95", "55", "D5", "35", "B5", "75", "F5", | |
| "0D", "8D", "4D", "CD", "2D", "AD", "6D", "ED", | |
| "1D", "9D", "5D", "DD", "3D", "BD", "7D", "FD", | |
| "03", "83", "43", "C3", "23", "A3", "63", "E3", | |
| "13", "93", "53", "D3", "33", "B3", "73", "F3", | |
| "0B", "8B", "4B", "CB", "2B", "AB", "6B", "EB", | |
| "1B", "9B", "5B", "DB", "3B", "BB", "7B", "FB", | |
| "07", "87", "47", "C7", "27", "A7", "67", "E7", | |
| "17", "97", "57", "D7", "37", "B7", "77", "F7", | |
| "0F", "8F", "4F", "CF", "2F", "AF", "6F", "EF", | |
| "1F", "9F", "5F", "DF", "3F", "BF", "7F", "FF", | |
| }; | |
| #else | |
| static const char* hexmask_lookup[256] = { | |
| "00", "01", "02", "03", "04", "05", "06", "07", | |
| "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", | |
| "10", "11", "12", "13", "14", "15", "16", "17", | |
| "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", | |
| "20", "21", "22", "23", "24", "25", "26", "27", | |
| "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", | |
| "30", "31", "32", "33", "34", "35", "36", "37", | |
| "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", | |
| "40", "41", "42", "43", "44", "45", "46", "47", | |
| "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", | |
| "50", "51", "52", "53", "54", "55", "56", "57", | |
| "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", | |
| "60", "61", "62", "63", "64", "65", "66", "67", | |
| "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", | |
| "70", "71", "72", "73", "74", "75", "76", "77", | |
| "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", | |
| "80", "81", "82", "83", "84", "85", "86", "87", | |
| "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", | |
| "90", "91", "92", "93", "94", "95", "96", "97", | |
| "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", | |
| "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", | |
| "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", | |
| "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", | |
| "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", | |
| "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", | |
| "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", | |
| "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", | |
| "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", | |
| "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", | |
| "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", | |
| "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", | |
| "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF", | |
| }; | |
| #endif | |
| /* | |
| * Implement a simple cache for a specific size of bitstring rather | |
| * than churning through xfree() + xmalloc() constantly. | |
| * This is intended for use with node_record_count in slurmctld, which | |
| * is constantly cycling through bitstrings of that size in the scheduler. | |
| */ | |
| static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER; | |
| static void *cached_bitstr = NULL; | |
| static bitoff_t cached_bitstr_len = 0; | |
| static void *_cache_pop(void) | |
| { | |
| void *b = NULL; | |
| slurm_mutex_lock(&cache_mutex); | |
| if (cached_bitstr) { | |
| b = cached_bitstr; | |
| cached_bitstr = *(void **) b; | |
| } | |
| slurm_mutex_unlock(&cache_mutex); | |
| return b; | |
| } | |
| static void _cache_push(void *b) | |
| { | |
| slurm_mutex_lock(&cache_mutex); | |
| *(void **) b = cached_bitstr; | |
| cached_bitstr = b; | |
| slurm_mutex_unlock(&cache_mutex); | |
| } | |
| extern void bit_cache_init(bitoff_t nbits) | |
| { | |
| /* | |
| * Disable cache in MEMORY_LEAK_DEBUG as otherwise valgrind may attribute | |
| * any bitstring leaks that cycled through the cache to the original | |
| * allocation location, which is potentially completely unrelated to where | |
| * the leak is. The only downside is that a leak of the cache itself will | |
| * not be caught. | |
| */ | |
| #ifndef MEMORY_LEAK_DEBUG | |
| slurm_mutex_lock(&cache_mutex); | |
| if (cached_bitstr_len && (cached_bitstr_len != nbits)) | |
| fatal_abort("%s: cannot change size once set", __func__); | |
| cached_bitstr_len = nbits; | |
| slurm_mutex_unlock(&cache_mutex); | |
| #endif | |
| } | |
| extern void bit_cache_fini(void) | |
| { | |
| void *b = NULL; | |
| while ((b = _cache_pop())) | |
| xfree(b); | |
| } | |
| /* | |
| * Allocate a bitstring. | |
| * nbits (IN) valid bits in new bitstring, initialized to all clear | |
| * RETURN new bitstring | |
| */ | |
| bitstr_t *bit_alloc(bitoff_t nbits) | |
| { | |
| bitstr_t *new = NULL; | |
| _assert_valid_size(nbits); | |
| if (nbits == cached_bitstr_len) | |
| new = _cache_pop(); | |
| if (new) | |
| memset(new, 0, _bitstr_words(nbits) * sizeof(bitstr_t)); | |
| else | |
| new = xcalloc(_bitstr_words(nbits), sizeof(bitstr_t)); | |
| _bitstr_magic(new) = BITSTR_MAGIC; | |
| _bitstr_bits(new) = nbits; | |
| return new; | |
| } | |
| static bitstr_t *bit_alloc_nz(bitoff_t nbits) | |
| { | |
| bitstr_t *new = NULL; | |
| _assert_valid_size(nbits); | |
| if (nbits == cached_bitstr_len) | |
| new = _cache_pop(); | |
| if (!new) | |
| new = xcalloc_nz(_bitstr_words(nbits), sizeof(bitstr_t)); | |
| _bitstr_magic(new) = BITSTR_MAGIC; | |
| _bitstr_bits(new) = nbits; | |
| return new; | |
| } | |
| /* | |
| * Reallocate a bitstring (expand or contract size). | |
| * b (IN) pointer to old bitstring | |
| * nbits (IN) valid bits in new bitstr | |
| * RETURN new bitstring | |
| */ | |
| bitstr_t *slurm_bit_realloc(bitstr_t **b, bitoff_t nbits) | |
| { | |
| _assert_bitstr_valid(*b); | |
| _assert_valid_size(nbits); | |
| /* | |
| * Clear the bits a shrink cuts off, or a later grow brings them back. | |
| * Only the last word kept holds any; xrecalloc() frees the rest. | |
| */ | |
| if ((nbits < _bitstr_bits(*b)) && (nbits & BITSTR_MAXPOS)) | |
| (*b)[_bit_word(nbits)] &= _bit_nmask(nbits); | |
| xrecalloc(*b, _bitstr_words(nbits), sizeof(bitstr_t)); | |
| _assert_bitstr_valid(*b); | |
| _bitstr_bits(*b) = nbits; | |
| return *b; | |
| } | |
| /* | |
| * Free a bitstr. | |
| * b (IN/OUT) bitstr to be freed | |
| */ | |
| void slurm_bit_free(bitstr_t **b) | |
| { | |
| xassert(*b); | |
| xassert(_bitstr_magic(*b) == BITSTR_MAGIC); | |
| _bitstr_magic(*b) = 0; | |
| if (_bitstr_bits(*b) == cached_bitstr_len) { | |
| _cache_push(*b); | |
| *b = NULL; | |
| } else | |
| xfree(*b); | |
| } | |
| void bit_free_ptr(void *p) | |
| { | |
| bitstr_t *b = p; | |
| slurm_bit_free(&b); | |
| } | |
| /* | |
| * Return the number of possible bits in a bitstring. | |
| * b (IN) bitstring to check | |
| * RETURN number of bits allocated | |
| */ | |
| bitoff_t | |
| bit_size(bitstr_t *b) | |
| { | |
| _assert_bitstr_valid(b); | |
| return _bitstr_bits(b); | |
| } | |
| /* | |
| * Is bit N of bitstring b set? | |
| * b (IN) bitstring to test | |
| * bit (IN) bit position to test | |
| * RETURN 1 if bit set, 0 if clear | |
| */ | |
| int | |
| slurm_bit_test(bitstr_t *b, bitoff_t bit) | |
| { | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, bit); | |
| return bit_test(b, bit); | |
| } | |
| /* | |
| * Set bit N of bitstring. | |
| * b (IN) target bitstring | |
| * bit (IN) bit position to set | |
| */ | |
| void | |
| bit_set(bitstr_t *b, bitoff_t bit) | |
| { | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, bit); | |
| b[_bit_word(bit)] |= _bit_mask(bit); | |
| } | |
| /* | |
| * Clear bit N of bitstring | |
| * b (IN) target bitstring | |
| * bit (IN) bit position to clear | |
| */ | |
| void | |
| bit_clear(bitstr_t *b, bitoff_t bit) | |
| { | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, bit); | |
| b[_bit_word(bit)] &= ~_bit_mask(bit); | |
| } | |
| /* | |
| * Set bits start ... stop in bitstring | |
| * b (IN) target bitstring | |
| * start (IN) starting (low numbered) bit position | |
| * stop (IN) ending (higher numbered) bit position | |
| */ | |
| void | |
| bit_nset(bitstr_t *b, bitoff_t start, bitoff_t stop) | |
| { | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, start); | |
| _assert_bit_valid(b, stop); | |
| while (start <= stop && start % 8 > 0) /* partial first byte? */ | |
| bit_set(b, start++); | |
| while (stop >= start && (stop+1) % 8 > 0) /* partial last byte? */ | |
| bit_set(b, stop--); | |
| if (stop > start) { /* now do whole bytes */ | |
| xassert((stop-start+1) % 8 == 0); | |
| memset(_bit_byteaddr(b, start), 0xff, (stop-start+1) / 8); | |
| } | |
| } | |
| /* | |
| * Clear bits start ... stop in bitstring | |
| * b (IN) target bitstring | |
| * start (IN) starting (low numbered) bit position | |
| * stop (IN) ending (higher numbered) bit position | |
| */ | |
| void | |
| bit_nclear(bitstr_t *b, bitoff_t start, bitoff_t stop) | |
| { | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, start); | |
| _assert_bit_valid(b, stop); | |
| while (start <= stop && start % 8 > 0) /* partial first byte? */ | |
| bit_clear(b, start++); | |
| while (stop >= start && (stop+1) % 8 > 0)/* partial last byte? */ | |
| bit_clear(b, stop--); | |
| if (stop > start) { /* now do whole bytes */ | |
| xassert((stop-start+1) % 8 == 0); | |
| memset(_bit_byteaddr(b, start), 0, (stop-start+1) / 8); | |
| } | |
| } | |
| /* | |
| * Set all bits in bitstring | |
| * b (IN) target bitstring | |
| */ | |
| void | |
| bit_set_all(bitstr_t *b) | |
| { | |
| bit_nset(b, 0, bit_size(b)-1); | |
| } | |
| /* | |
| * Clear all bits in bitstring | |
| * b (IN) target bitstring | |
| */ | |
| void | |
| bit_clear_all(bitstr_t *b) | |
| { | |
| bit_nclear(b, 0, bit_size(b)-1); | |
| } | |
| /* | |
| * Find first bit clear in bitstring. | |
| * b (IN) bitstring to search | |
| * nbits (IN) number of bits to search | |
| * RETURN resulting bit position (-1 if none found) | |
| */ | |
| bitoff_t | |
| bit_ffc(bitstr_t *b) | |
| { | |
| bitoff_t bit = 0, value = -1; | |
| _assert_bitstr_valid(b); | |
| while (bit < _bitstr_bits(b) && value == -1) { | |
| int32_t word = _bit_word(bit); | |
| if (b[word] == BITSTR_MAXVAL) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| while (bit < _bitstr_bits(b) && _bit_word(bit) == word) { | |
| if (!bit_test(b, bit)) { | |
| value = bit; | |
| break; | |
| } | |
| bit++; | |
| } | |
| } | |
| return value; | |
| } | |
| /* | |
| * Find first bit set in b from an offset. | |
| * b (IN) bitstring to search | |
| * bit (IN) bit to start the search at | |
| * RETURN resulting bit position (-1 if none found) | |
| */ | |
| bitoff_t bit_ffs_from_bit(bitstr_t *b, bitoff_t bit) | |
| { | |
| bitoff_t value = -1; | |
| int32_t word; | |
| #if (HAVE___BUILTIN_CLZLL && (defined SLURM_BIGENDIAN)) || \ | |
| (HAVE___BUILTIN_CTZLL && (!defined SLURM_BIGENDIAN)) | |
| bitstr_t bitstr_word; | |
| _assert_bitstr_valid(b); | |
| if ((bit % BITSTR_WORD_SIZE) && (bit < _bitstr_bits(b))) { | |
| bitstr_t mask = ~_bit_nmask(bit); | |
| bit -= (bit % BITSTR_WORD_SIZE); | |
| word = _bit_word(bit); | |
| bitstr_word = b[word] & mask; | |
| goto test_word; | |
| } | |
| while ((bit < _bitstr_bits(b)) && (value == -1)) { | |
| word = _bit_word(bit); | |
| bitstr_word = b[word]; | |
| test_word: | |
| if (bitstr_word == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| #if HAVE___BUILTIN_CLZLL && (defined SLURM_BIGENDIAN) | |
| value = bit + __builtin_clzll(bitstr_word); | |
| #elif HAVE___BUILTIN_CTZLL && (!defined SLURM_BIGENDIAN) | |
| value = bit + __builtin_ctzll(bitstr_word); | |
| #endif | |
| } | |
| if (value < _bitstr_bits(b)) | |
| return value; | |
| else | |
| return -1; | |
| #else | |
| _assert_bitstr_valid(b); | |
| if ((bit % BITSTR_WORD_SIZE) && (bit < _bitstr_bits(b)) && | |
| (b[_bit_word(bit)] == 0)) { | |
| bit += BITSTR_WORD_SIZE; | |
| bit -= (bit % BITSTR_WORD_SIZE); | |
| } | |
| while ((bit < _bitstr_bits(b)) && (value == -1)) { | |
| word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| while (bit < _bitstr_bits(b) && _bit_word(bit) == word) { | |
| if (bit_test(b, bit)) { | |
| value = bit; | |
| break; | |
| } | |
| bit++; | |
| } | |
| } | |
| return value; | |
| #endif | |
| } | |
| /* | |
| * Find first bit set in b. | |
| * b (IN) bitstring to search | |
| * RETURN resulting bit position (-1 if none found) | |
| */ | |
| bitoff_t bit_ffs(bitstr_t *b) | |
| { | |
| return bit_ffs_from_bit(b, 0); | |
| } | |
| /* | |
| * Find last bit set in b. | |
| * b (IN) bitstring to search | |
| * RETURN resulting bit position (-1 if none found) | |
| */ | |
| bitoff_t bit_fls(bitstr_t *b) | |
| { | |
| /* zero origin */ | |
| bitoff_t bit = _bitstr_bits(b) - 1; | |
| return bit_fls_from_bit(b, bit); | |
| } | |
| /* | |
| * Find last bit set in b from an offset. | |
| * b (IN) bitstring to search | |
| * bit (IN) bit to start the search at | |
| * RETURN resulting bit position (-1 if none found) | |
| */ | |
| bitoff_t bit_fls_from_bit(bitstr_t *b, bitoff_t bit) | |
| { | |
| bitoff_t value = -1; | |
| int32_t word; | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b, bit); | |
| if (_bitstr_bits(b) == 0) /* empty bitstring */ | |
| return -1; | |
| while (bit >= 0 && /* test partial words */ | |
| (_bit_word(bit) == _bit_word(bit + 1))) { | |
| if (bit_test(b, bit)) { | |
| value = bit; | |
| break; | |
| } | |
| bit--; | |
| } | |
| while (bit >= 0 && value == -1) { /* test whole words */ | |
| word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit -= BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| #if HAVE___BUILTIN_CTZLL && (defined SLURM_BIGENDIAN) | |
| value = bit - __builtin_ctzll(b[word]); | |
| #elif HAVE___BUILTIN_CLZLL && (!defined SLURM_BIGENDIAN) | |
| value = bit - __builtin_clzll(b[word]); | |
| #else | |
| while (bit >= 0) { | |
| if (bit_test(b, bit)) { | |
| value = bit; | |
| break; | |
| } | |
| bit--; | |
| } | |
| #endif | |
| } | |
| return value; | |
| } | |
| /* | |
| * set all bits between the first and last bits set (i.e. fill in the gaps | |
| * to make set bits contiguous) | |
| */ | |
| void | |
| bit_fill_gaps(bitstr_t *b) | |
| { | |
| bitoff_t first, last; | |
| _assert_bitstr_valid(b); | |
| first = bit_ffs(b); | |
| if (first == -1) | |
| return; | |
| last = bit_fls(b); | |
| bit_nset(b, first, last); | |
| return; | |
| } | |
| /* | |
| * return 1 if all bits set in b1 are also set in b2, 0 otherwise | |
| */ | |
| int | |
| bit_super_set(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| xassert(_bitstr_bits(b1) == _bitstr_bits(b2)); | |
| for (bit = 0; bit < _bitstr_bits(b1); bit += BITSTR_WORD_SIZE) { | |
| if (b1[_bit_word(bit)] != (b1[_bit_word(bit)] & | |
| b2[_bit_word(bit)])) { | |
| bitstr_t mask; | |
| if ((bit + BITSTR_WORD_SIZE) <= _bitstr_bits(b1)) | |
| return 0; | |
| mask = _bit_nmask(_bitstr_bits(b1)); | |
| if ((b1[_bit_word(bit)] & mask) != (b1[_bit_word(bit)] & | |
| b2[_bit_word(bit)] & | |
| mask)) | |
| return 0; | |
| } | |
| } | |
| return 1; | |
| } | |
| /* | |
| * return 1 if b1 and b2 are identical, 0 otherwise | |
| */ | |
| extern int | |
| bit_equal(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| if (_bitstr_bits(b1) != _bitstr_bits(b2)) | |
| return 0; | |
| bit_cnt = _bitstr_bits(b1); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) { | |
| if (b1[_bit_word(bit)] != b2[_bit_word(bit)]) | |
| return 0; | |
| } | |
| if (bit < bit_cnt) { | |
| uint64_t mask = _bit_nmask(bit_cnt); | |
| if ((b1[_bit_word(bit)] ^ b2[_bit_word(bit)]) & mask) | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /* | |
| * b1 &= b2 as many bits as both bitstr_t have | |
| * b1 (IN/OUT) first string | |
| * b2 (IN) second bitstring | |
| */ | |
| void | |
| bit_and(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2)); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) | |
| b1[_bit_word(bit)] &= b2[_bit_word(bit)]; | |
| if (bit < bit_cnt) { | |
| uint64_t mask = ~(_bit_nmask(bit_cnt)); | |
| b1[_bit_word(bit)] &= (b2[_bit_word(bit)] | mask); | |
| } | |
| } | |
| /* | |
| * b1 &= ~b2 as many bits as both bitstr_t have | |
| * b1 (IN/OUT) | |
| * b2 (IN) | |
| */ | |
| void bit_and_not(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2)); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) | |
| b1[_bit_word(bit)] &= ~b2[_bit_word(bit)]; | |
| if (bit < bit_cnt) { | |
| uint64_t mask = _bit_nmask(bit_cnt); | |
| b1[_bit_word(bit)] &= ~(b2[_bit_word(bit)] & mask); | |
| } | |
| } | |
| /* | |
| * b1 = ~b1 one's complement | |
| * b1 (IN/OUT) first bitmap | |
| */ | |
| void | |
| bit_not(bitstr_t *b) | |
| { | |
| bitoff_t bit; | |
| _assert_bitstr_valid(b); | |
| for (bit = 0; bit < _bitstr_bits(b); bit += BITSTR_WORD_SIZE) | |
| b[_bit_word(bit)] = ~b[_bit_word(bit)]; | |
| } | |
| /* | |
| * b1 |= b2 as many bits as both bitstr_t have | |
| * b1 (IN/OUT) first bitmap | |
| * b2 (IN) second bitmap | |
| */ | |
| void | |
| bit_or(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2)); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) | |
| b1[_bit_word(bit)] |= b2[_bit_word(bit)]; | |
| if (bit < bit_cnt) { | |
| uint64_t mask = _bit_nmask(bit_cnt); | |
| b1[_bit_word(bit)] |= (b2[_bit_word(bit)] & mask); | |
| } | |
| } | |
| /* | |
| * b1 |= ~b2 as many bits as both bitstr_t have | |
| * b1 (IN/OUT) | |
| * b2 (IN) | |
| */ | |
| void bit_or_not(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| bit_cnt = MIN(_bitstr_bits(b1), _bitstr_bits(b2)); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) | |
| b1[_bit_word(bit)] |= ~b2[_bit_word(bit)]; | |
| if (bit < bit_cnt) { | |
| uint64_t mask = ~(_bit_nmask(bit_cnt)); | |
| b1[_bit_word(bit)] |= ~(b2[_bit_word(bit)] | mask); | |
| } | |
| } | |
| /* | |
| * return a copy of the supplied bitmap | |
| */ | |
| bitstr_t * | |
| bit_copy(bitstr_t *b) | |
| { | |
| bitstr_t *new; | |
| int32_t newsize_bits; | |
| size_t len = 0; /* Number of bytes to memcpy() */ | |
| _assert_bitstr_valid(b); | |
| newsize_bits = bit_size(b); | |
| len = (_bitstr_words(newsize_bits) - BITSTR_OVERHEAD)*sizeof(bitstr_t); | |
| new = bit_alloc_nz(newsize_bits); | |
| memcpy(&new[BITSTR_OVERHEAD], &b[BITSTR_OVERHEAD], len); | |
| return new; | |
| } | |
| void | |
| bit_copybits(bitstr_t *dest, bitstr_t *src) | |
| { | |
| int32_t len; | |
| _assert_bitstr_valid(dest); | |
| _assert_bitstr_valid(src); | |
| xassert(bit_size(src) == bit_size(dest)); | |
| len = (_bitstr_words(bit_size(src)) - BITSTR_OVERHEAD)*sizeof(bitstr_t); | |
| memcpy(&dest[BITSTR_OVERHEAD], &src[BITSTR_OVERHEAD], len); | |
| } | |
| #ifdef HAVE___BUILTIN_POPCOUNTLL | |
| #define hweight __builtin_popcountll | |
| #else | |
| /* | |
| * Returns the hamming weight (i.e. the number of bits set) in a word. | |
| * NOTE: This routine borrowed from Linux 4.9 <tools/lib/hweight.c>. | |
| */ | |
| static uint64_t | |
| hweight(uint64_t w) | |
| { | |
| w -= (w >> 1) & 0x5555555555555555ul; | |
| w = (w & 0x3333333333333333ul) + ((w >> 2) & 0x3333333333333333ul); | |
| w = (w + (w >> 4)) & 0x0f0f0f0f0f0f0f0ful; | |
| return (w * 0x0101010101010101ul) >> 56; | |
| } | |
| #endif | |
| /* | |
| * Count the number of bits set in bitstring. | |
| * b (IN) bitstring to check | |
| * RETURN count of set bits | |
| */ | |
| int32_t | |
| bit_set_count(bitstr_t *b) | |
| { | |
| int32_t count = 0; | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b); | |
| bit_cnt = _bitstr_bits(b); | |
| for (bit = 0; (bit + BITSTR_WORD_SIZE) <= bit_cnt; | |
| bit += BITSTR_WORD_SIZE) { | |
| count += hweight(b[_bit_word(bit)]); | |
| } | |
| if (bit < bit_cnt) { | |
| uint64_t mask = _bit_nmask(bit_cnt); | |
| count += hweight(b[_bit_word(bit)] & mask); | |
| } | |
| return count; | |
| } | |
| /* | |
| * Count the number of bits set in a range of bitstring. | |
| * b (IN) bitstring to check | |
| * start (IN) first bit to check | |
| * end (IN) last bit to check+1 | |
| * RETURN count of set bits | |
| */ | |
| int32_t | |
| bit_set_count_range(bitstr_t *b, int32_t start, int32_t end) | |
| { | |
| int32_t count = 0, eow; | |
| bitoff_t bit; | |
| _assert_bitstr_valid(b); | |
| _assert_bit_valid(b,start); | |
| end = MIN(end, _bitstr_bits(b)); | |
| /* end of word */ | |
| eow = (((start + BITSTR_MAXPOS) >> BITSTR_SHIFT) << BITSTR_SHIFT); | |
| bit = start; | |
| if ((start < eow) && (eow <= end)) { | |
| uint64_t mask = ~_bit_nmask(start); | |
| count += hweight(b[_bit_word(bit)] & mask); | |
| bit = eow; | |
| } else if (eow > start) { | |
| uint64_t mask = ~_bit_nmask(start); | |
| mask &= _bit_nmask(end); | |
| count += hweight(b[_bit_word(bit)] & mask); | |
| bit = eow; | |
| } | |
| for (; (bit + BITSTR_WORD_SIZE) <= end ; bit += BITSTR_WORD_SIZE) { | |
| count += hweight(b[_bit_word(bit)]); | |
| } | |
| if (bit < end) { | |
| uint64_t mask = _bit_nmask(end); | |
| count += hweight(b[_bit_word(bit)] & mask); | |
| } | |
| return count; | |
| } | |
| static int32_t _bit_overlap_internal(bitstr_t *b1, bitstr_t *b2, bool count_it) | |
| { | |
| int32_t count = 0; | |
| int64_t anded; | |
| bitoff_t bit, bit_cnt; | |
| _assert_bitstr_valid(b1); | |
| _assert_bitstr_valid(b2); | |
| xassert(_bitstr_bits(b1) == _bitstr_bits(b2)); | |
| bit_cnt = _bitstr_bits(b1); | |
| for (bit = 0; bit < bit_cnt; bit += BITSTR_WORD_SIZE) { | |
| if ((bit + BITSTR_WORD_SIZE - 1) >= bit_cnt) | |
| break; | |
| anded = b1[_bit_word(bit)] & b2[_bit_word(bit)]; | |
| if (count_it) | |
| count += hweight(anded); | |
| else if (anded) | |
| return 1; | |
| } | |
| if (bit < bit_cnt) { | |
| uint64_t mask = _bit_nmask(bit_cnt); | |
| anded = b1[_bit_word(bit)] & b2[_bit_word(bit)] & mask; | |
| if (count_it) | |
| count += hweight(anded); | |
| else if (anded) | |
| return 1; | |
| } | |
| return count; | |
| } | |
| /* | |
| * return number of bits set in b1 that are also set in b2, 0 if no overlap | |
| */ | |
| extern int32_t bit_overlap(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| return _bit_overlap_internal(b1, b2, 1); | |
| } | |
| /* | |
| * return 1 if there is at least one bit set in b1 that is also set in b2, 0 if | |
| * no overlap | |
| */ | |
| extern int32_t bit_overlap_any(bitstr_t *b1, bitstr_t *b2) | |
| { | |
| return _bit_overlap_internal(b1, b2, 0); | |
| } | |
| /* | |
| * Count the number of bits clear in bitstring. | |
| * b (IN) bitstring to check | |
| * RETURN count of clear bits | |
| */ | |
| int32_t | |
| bit_clear_count(bitstr_t *b) | |
| { | |
| _assert_bitstr_valid(b); | |
| return (_bitstr_bits(b) - bit_set_count(b)); | |
| } | |
| /* | |
| * rotate b1 by n bits returning a rotated copy | |
| * b1 (IN) bitmap to rotate | |
| * n (IN) rotation distance (+ = rotate left, - = rotate right) | |
| * nbits (IN) size of the new copy (in which the rotation occurs) | |
| * note: nbits must be >= bit_size(b1) | |
| * RETURN new rotated bitmap | |
| */ | |
| bitstr_t * | |
| bit_rotate_copy(bitstr_t *b1, int32_t n, bitoff_t nbits) | |
| { | |
| bitoff_t bit, dst; | |
| bitstr_t *new; | |
| bitoff_t bitsize; | |
| bitoff_t deltasize, wrapbits; | |
| _assert_bitstr_valid(b1); | |
| bitsize = bit_size(b1); | |
| xassert(nbits >= bitsize); | |
| deltasize = nbits - bitsize; | |
| /* normalize n to a single positive rotation */ | |
| n = n % nbits; | |
| if (n < 0) { | |
| n += nbits; | |
| } | |
| wrapbits = 0; /* number of bits that will wrap around */ | |
| if (n > deltasize) { | |
| wrapbits = n - deltasize; | |
| } | |
| new = bit_alloc(nbits); | |
| /* bits shifting up */ | |
| for (bit = 0; bit < (bitsize-wrapbits); bit++) { | |
| if (bit_test(b1, bit)) | |
| bit_set(new, bit+n); | |
| } | |
| /* continue bit into wrap-around bits, if any */ | |
| for (dst = 0; bit < bitsize; bit++, dst++) { | |
| if (bit_test(b1, bit)) | |
| bit_set(new, dst); | |
| } | |
| return(new); | |
| } | |
| /* | |
| * rotate b1 by n bits | |
| * b1 (IN/OUT) bitmap to rotate | |
| * n (IN) rotation distance (+ = rotate left, - = rotate right) | |
| */ | |
| void | |
| bit_rotate(bitstr_t *b1, int32_t n) | |
| { | |
| uint32_t bitsize; | |
| bitstr_t *new; | |
| if (n == 0) | |
| return; | |
| _assert_bitstr_valid(b1); | |
| bitsize = bit_size(b1); | |
| new = bit_rotate_copy(b1, n, bitsize); | |
| bit_copybits(b1, new); | |
| FREE_NULL_BITMAP(new); | |
| } | |
| /* | |
| * find first set n | |
| * return position of nth set bit in bitstr word | |
| * minimal validity checking: if n > set_count, return 63 | |
| * if n == 0, return 0 | |
| */ | |
| static bitoff_t _ffsn(bitstr_t b, bitoff_t n) | |
| { | |
| uint64_t mask = 0xFFFFFFFFu; | |
| uint32_t size = 32u; | |
| bitstr_t base = 0u; | |
| while (size > 0) { | |
| if (n > hweight(b & mask)) { | |
| base += size; | |
| size >>= 1; | |
| mask |= mask << size; | |
| } else { | |
| size >>= 1; | |
| mask >>= size; | |
| } | |
| } | |
| return base; | |
| } | |
| /* | |
| * return the bit position of the nth set bit | |
| * if n > bit_set_count(b), return the position of the last set bit | |
| * return -1 if b is empty or n == 0 | |
| */ | |
| bitoff_t bit_nth_set(bitstr_t *b, bitoff_t n) | |
| { | |
| bitoff_t bit, bit_cnt, last_bit; | |
| bitstr_t mask = -1; | |
| bitstr_t last_mask = -1; | |
| uint32_t count, last_count, last_word; | |
| _assert_bitstr_valid(b); | |
| if (n <= 0) | |
| return -1; | |
| bit_cnt = _bitstr_bits(b); | |
| last_word = _bit_word(bit_cnt); | |
| last_bit = -1; | |
| for (bit = 0; bit < bit_cnt; bit += BITSTR_WORD_SIZE){ | |
| if (_bit_word(bit) == last_word) | |
| mask = _bit_nmask(bit_cnt); | |
| count = hweight(b[_bit_word(bit)] & mask); | |
| if (count > 0){ | |
| last_bit = bit; | |
| last_count = count; | |
| } | |
| if (n <= count) | |
| break; | |
| n -= count; | |
| } | |
| /* b has no set bits */ | |
| if (last_bit < 0) | |
| return -1; | |
| if (_bit_word(last_bit) == last_word) | |
| last_mask = _bit_nmask(bit_cnt); | |
| /* | |
| * if last_bit != bit, n > set_count. | |
| * find position of last bit in last non-empty word | |
| */ | |
| return last_bit + _ffsn(b[_bit_word(last_bit)] & last_mask, | |
| (last_bit == bit) ? n : last_count); | |
| } | |
| /* | |
| * Clear all bits after the first n set bits | |
| */ | |
| void bit_pick_firstn(bitstr_t *b, bitoff_t n) | |
| { | |
| _assert_bitstr_valid(b); | |
| bitoff_t nth_bit = bit_nth_set(b, n); | |
| /* | |
| * b is empty or nth bit is at the last position. | |
| * Nothing to do in either case | |
| */ | |
| if (((nth_bit + 1) == _bitstr_bits(b)) || (nth_bit < 0)) | |
| return; | |
| bit_nclear(b, nth_bit + 1, _bitstr_bits(b) - 1); | |
| } | |
| /* | |
| * build a bitmap containing the first nbits of b which are set | |
| */ | |
| bitstr_t * | |
| bit_pick_cnt(bitstr_t *b, bitoff_t nbits) | |
| { | |
| bitoff_t bit = 0, new_bits, count = 0; | |
| bitstr_t *new; | |
| _assert_bitstr_valid(b); | |
| if (_bitstr_bits(b) < nbits) | |
| return NULL; | |
| new = bit_alloc(bit_size(b)); | |
| while ((bit < _bitstr_bits(b)) && (count < nbits)) { | |
| int32_t word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| new_bits = hweight(b[word]); | |
| if (((count + new_bits) <= nbits) && | |
| ((bit + BITSTR_WORD_SIZE - 1) < _bitstr_bits(b))) { | |
| new[word] = b[word]; | |
| count += new_bits; | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| while ((bit < _bitstr_bits(b)) && (count < nbits)) { | |
| if (bit_test(b, bit)) { | |
| bit_set(new, bit); | |
| count++; | |
| } | |
| bit++; | |
| } | |
| } | |
| if (count < nbits) { | |
| FREE_NULL_BITMAP(new); | |
| } | |
| return new; | |
| } | |
| /* | |
| * Convert to range string format, e.g. 0-5,42 | |
| */ | |
| char *bit_fmt(char *str, int32_t len, bitstr_t *b) | |
| { | |
| int32_t word; | |
| char *comma = ""; | |
| bitoff_t bit; | |
| _assert_bitstr_valid(b); | |
| xassert(len > 0); | |
| *str = '\0'; | |
| for (bit = 0; bit < _bitstr_bits(b); ) { | |
| word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| if (bit_test(b, bit)) { | |
| int32_t ret, size; | |
| bitoff_t start = bit; | |
| while (bit+1 < _bitstr_bits(b) && bit_test(b, bit+1)) { | |
| bit++; | |
| } | |
| size = strlen(str); | |
| if (bit == start) { /* add single bit position */ | |
| ret = snprintf(str + size, len - size, | |
| "%s%"BITSTR_FMT"", comma, start); | |
| } else { /* add bit position range */ | |
| ret = snprintf(str + size, len - size, | |
| "%s%"BITSTR_FMT"-%"BITSTR_FMT"", | |
| comma, start, bit); | |
| } | |
| comma = ","; | |
| xassert(ret != -1); | |
| if (ret == -1) | |
| error("failed to write to string -- this should never happen"); | |
| } | |
| bit++; | |
| } | |
| return str; | |
| } | |
| /* | |
| * Convert to range string format, e.g. 0-5,42 with no length restriction | |
| * Call xfree() on return value to avoid memory leak | |
| */ | |
| char *bit_fmt_full(bitstr_t *b) | |
| { | |
| int32_t word; | |
| bitoff_t start, bit; | |
| char *str = NULL, *pos = NULL, *comma = ""; | |
| _assert_bitstr_valid(b); | |
| for (bit = 0; bit < _bitstr_bits(b); ) { | |
| word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| if (bit_test(b, bit)) { | |
| start = bit; | |
| while (bit+1 < _bitstr_bits(b) && bit_test(b, bit+1)) { | |
| bit++; | |
| } | |
| if (bit == start) /* add single bit position */ | |
| xstrfmtcatat(str, &pos, "%s%"BITSTR_FMT"", | |
| comma, start); | |
| else /* add bit position range */ | |
| xstrfmtcatat(str, &pos, | |
| "%s%"BITSTR_FMT"-%"BITSTR_FMT, | |
| comma, start, bit); | |
| comma = ","; | |
| } | |
| bit++; | |
| } | |
| return str; | |
| } | |
| /* | |
| * Convert to range string format, e.g. 0-5,42 with no length restriction | |
| * offset IN - location of bit zero | |
| * len IN - number of bits to test | |
| * Call xfree() on return value to avoid memory leak | |
| */ | |
| char *bit_fmt_range(bitstr_t *b, int offset, int len) | |
| { | |
| int32_t word; | |
| bitoff_t start, fini_bit, bit; | |
| char *str = NULL, *pos = NULL, *comma = ""; | |
| _assert_bitstr_valid(b); | |
| fini_bit = MIN(_bitstr_bits(b), offset + len); | |
| for (bit = offset; bit < fini_bit; ) { | |
| word = _bit_word(bit); | |
| if (b[word] == 0) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| if (bit_test(b, bit)) { | |
| start = bit; | |
| while ((bit + 1 < fini_bit) && bit_test(b, bit + 1)) { | |
| bit++; | |
| } | |
| if (bit == start) { /* add single bit position */ | |
| xstrfmtcatat(str, &pos, "%s%"BITSTR_FMT"", | |
| comma, (start - offset)); | |
| } else { /* add bit position range */ | |
| xstrfmtcatat(str, &pos, | |
| "%s%"BITSTR_FMT"-%"BITSTR_FMT, | |
| comma, (start - offset), | |
| (bit - offset)); | |
| } | |
| comma = ","; | |
| } | |
| bit++; | |
| } | |
| return str; | |
| } | |
| /* | |
| * Convert range string format, e.g. "0-5,42" to bitmap | |
| * Ret 0 on success, -1 on error | |
| */ | |
| extern int bit_unfmt(bitstr_t *b, const char *str) | |
| { | |
| int32_t *intvec; | |
| int rc = 0; | |
| _assert_bitstr_valid(b); | |
| if (!str || str[0] == '\0') /* no bits set */ | |
| return rc; | |
| intvec = bitfmt2int(str); | |
| if (intvec == NULL) | |
| return -1; | |
| rc = inx2bitstr(b, intvec); | |
| xfree(intvec); | |
| return rc; | |
| } | |
| /* | |
| * bitfmt2int - convert a string describing bitmap (output from bit_fmt, | |
| * e.g. "0-30,45,50-60") into an array of integer (start/end) pairs | |
| * terminated by -1 (e.g. "0, 30, 45, 45, 50, 60, -1") | |
| * Also supports the "1-17:4" step format ("1, 5, 9, 13, 17, -1"). | |
| * input: bitmap string as produced by bitstring.c : bitfmt | |
| * output: an array of integers | |
| * NOTE: the caller must xfree the returned memory | |
| */ | |
| extern int32_t *bitfmt2int(const char *bit_str_ptr) | |
| { | |
| int32_t *bit_int_ptr, i, bit_inx, size, sum, start_val; | |
| char *tmp = NULL; | |
| int32_t start_task_id = -1; | |
| int32_t end_task_id = -1; | |
| int32_t step = -1; | |
| if (bit_str_ptr == NULL) | |
| return NULL; | |
| if (!(xstrchr(bit_str_ptr, ':'))) { | |
| size = strlen(bit_str_ptr) + 1; | |
| /* more than enough space */ | |
| bit_int_ptr = xmalloc(sizeof(int32_t) * (size * 2 + 1)); | |
| bit_inx = sum = 0; | |
| start_val = -1; | |
| for (i = 0; i < size; i++) { | |
| if (bit_str_ptr[i] >= '0' && | |
| bit_str_ptr[i] <= '9') { | |
| sum = (sum * 10) + (bit_str_ptr[i] - '0'); | |
| } else if (bit_str_ptr[i] == '-') { | |
| start_val = sum; | |
| sum = 0; | |
| } else if (bit_str_ptr[i] == ',' || | |
| bit_str_ptr[i] == '\0') { | |
| if (i == 0) | |
| break; | |
| if (start_val == -1) | |
| start_val = sum; | |
| bit_int_ptr[bit_inx++] = start_val; | |
| bit_int_ptr[bit_inx++] = sum; | |
| start_val = -1; | |
| sum = 0; | |
| } | |
| } | |
| xassert(bit_inx < (size * 2 + 1)); | |
| } else { /* handle step format */ | |
| start_task_id = strtol(bit_str_ptr, &tmp, 10); | |
| if (*tmp != '-') | |
| return NULL; | |
| end_task_id = strtol(tmp + 1, &tmp, 10); | |
| if (*tmp != ':') | |
| return NULL; | |
| step = strtol(tmp + 1, &tmp, 10); | |
| if (*tmp != '\0') | |
| return NULL; | |
| if (end_task_id < start_task_id || step <= 0) | |
| return NULL; | |
| size = ((end_task_id - start_task_id) / step) + 1; | |
| bit_int_ptr = xmalloc(sizeof(int32_t) * (size * 2 + 1)); | |
| bit_inx = 0; | |
| for(i = start_task_id; i < end_task_id; i += step) { | |
| bit_int_ptr[bit_inx++] = i; /* start of pair */ | |
| bit_int_ptr[bit_inx++] = i; /* end of pair */ | |
| } | |
| } | |
| bit_int_ptr[bit_inx] = -1; | |
| return bit_int_ptr; | |
| } | |
| int inx2bitstr(bitstr_t *b, int32_t *inx) | |
| { | |
| int32_t *p, bit_cnt; | |
| int rc = 0; | |
| xassert(b); | |
| xassert(inx); | |
| bit_cnt = _bitstr_bits(b); | |
| if (bit_cnt > 0) | |
| bit_nclear(b, 0, bit_cnt - 1); | |
| for (p = inx; *p != -1; p += 2) { | |
| if ((*p < 0) || (*p >= bit_cnt) || | |
| (*(p + 1) < 0) || (*(p + 1) >= bit_cnt)) { | |
| rc = -1; | |
| break; | |
| } | |
| bit_nset(b, *p, *(p + 1)); | |
| } | |
| return rc; | |
| } | |
| /* | |
| * convert a bitstring to inx format | |
| * returns an xmalloc()'d array of int32_t that must be xfree()'d | |
| */ | |
| int32_t *bitstr2inx(bitstr_t *b) | |
| { | |
| bitoff_t start, bit, pos = 0; | |
| int32_t *bit_inx; | |
| if (!b) { | |
| bit_inx = xmalloc(sizeof(int32_t)); | |
| bit_inx[0] = -1; | |
| return bit_inx; | |
| } | |
| /* worst case: every other bit set, resulting in an array of length | |
| * bitstr_bits(b) + 1 (if an odd number of elements) | |
| * + 1 (for trailing -1) */ | |
| bit_inx = xmalloc_nz(sizeof(int32_t) * (_bitstr_bits(b) + 2)); | |
| for (bit = 0; bit < _bitstr_bits(b); ) { | |
| /* skip past empty words */ | |
| if (!b[_bit_word(bit)]) { | |
| bit += BITSTR_WORD_SIZE; | |
| continue; | |
| } | |
| if (bit_test(b, bit)) { | |
| start = bit; | |
| while (bit + 1 < _bitstr_bits(b) | |
| && bit_test(b, bit + 1)) | |
| bit++; | |
| bit_inx[pos++] = start; | |
| bit_inx[pos++] = bit; | |
| } | |
| bit++; | |
| } | |
| /* terminate array with -1 */ | |
| bit_inx[pos] = -1; | |
| return bit_inx; | |
| } | |
| /* If trim_output is true, strip off leading zeros from result. */ | |
| static char *_bit_fmt_hexmask(bitstr_t *bitmap, bool trim_output) | |
| { | |
| char *retstr, *ptr; | |
| char current; | |
| bitoff_t i, j, word; | |
| bitoff_t bitsize; | |
| if (trim_output) | |
| bitsize = bit_fls(bitmap) + 1; | |
| else | |
| bitsize = bit_size(bitmap); | |
| if (!bitsize) | |
| return xstrdup("0x0"); | |
| /* 4 bits per ASCII '0'-'F' */ | |
| bitoff_t charsize = (bitsize + 3) / 4; | |
| retstr = xmalloc(charsize + 3); | |
| retstr[0] = '0'; | |
| retstr[1] = 'x'; | |
| retstr[charsize + 2] = '\0'; | |
| ptr = &retstr[charsize + 1]; | |
| for (i = 0; i < bitsize;) { | |
| if (i + BITSTR_WORD_SIZE <= bitsize) { | |
| word = _bit_word(i); | |
| for (j = 0; j < sizeof(bitstr_t); j++) { | |
| uint8_t idx = ((uint8_t *)(&(bitmap[word])))[j]; | |
| *ptr = hexmask_lookup[idx][1]; | |
| ptr--; | |
| *ptr = hexmask_lookup[idx][0]; | |
| ptr--; | |
| } | |
| i += BITSTR_WORD_SIZE; | |
| } else { | |
| current = 0; | |
| if (bit_test(bitmap, i)) | |
| current |= 0x1; | |
| i++; | |
| if ((i < bitsize) && bit_test(bitmap, i)) | |
| current |= 0x2; | |
| i++; | |
| if ((i < bitsize) && bit_test(bitmap, i)) | |
| current |= 0x4; | |
| i++; | |
| if ((i < bitsize) && bit_test(bitmap, i)) | |
| current |= 0x8; | |
| i++; | |
| if (current <= 9) { | |
| current += '0'; | |
| } else { | |
| current += 'A' - 10; | |
| } | |
| *ptr-- = current; | |
| } | |
| } | |
| return retstr; | |
| } | |
| /* bit_fmt_hexmask | |
| * | |
| * Given a bitstr_t, allocate and return a string in the form of: | |
| * "0x0123ABC\0" | |
| * ^ ^ | |
| * | | | |
| * MSB LSB | |
| * bitmap (IN) bitmap to format | |
| * RETURN formatted string | |
| */ | |
| char *bit_fmt_hexmask(bitstr_t *bitmap) | |
| { | |
| return _bit_fmt_hexmask(bitmap, false); | |
| } | |
| /* bit_fmt_hexmask_trim | |
| * | |
| * Same as bit_fmt_hexmask() except leading zeros are stripped from the output. | |
| */ | |
| char *bit_fmt_hexmask_trim(bitstr_t *bitmap) | |
| { | |
| return _bit_fmt_hexmask(bitmap, true); | |
| } | |
| /* bit_unfmt_hexmask | |
| * | |
| * Given a hex mask string "0x0123ABC\0", convert to a bitstr_t * | |
| * ^ ^ | |
| * | | | |
| * MSB LSB | |
| * bitmap (OUT) bitmap to update | |
| * str (IN) hex mask string to unformat | |
| * RET: 0 on success, -1 on error | |
| */ | |
| int bit_unfmt_hexmask(bitstr_t * bitmap, const char* str) | |
| { | |
| int32_t bit_index = 0, len; | |
| int rc = 0; | |
| const char *curpos; | |
| bitstr_t current; | |
| bitoff_t bitsize; | |
| if (!bitmap || !str) | |
| return -1; | |
| len = strlen(str); | |
| bitsize = bit_size(bitmap); | |
| curpos = str + len - 1; | |
| bit_nclear(bitmap, 0, bitsize - 1); | |
| if (xstrncmp(str, "0x", 2) == 0) { /* Bypass 0x */ | |
| str += 2; | |
| } | |
| while (curpos >= str) { | |
| current = (bitoff_t) *curpos; | |
| if (isxdigit(current)) { /* valid hex digit */ | |
| if (isdigit(current)) { | |
| current -= '0'; | |
| } else { | |
| current = toupper(current); | |
| current -= 'A' - 10; | |
| } | |
| } else { /* not a valid hex digit */ | |
| current = 0; | |
| rc = -1; | |
| break; | |
| } | |
| if (bit_index + 3 < bitsize && !(bit_index % 4)) { | |
| #ifdef SLURM_BIGENDIAN | |
| current = (((current & 1) << 3) | | |
| ((current & 2) << 1) | | |
| ((current & 4) >> 1) | | |
| ((current & 8) >> 3)); | |
| current = (current << ((BITSTR_MAXPOS - | |
| (bit_index & BITSTR_MAXPOS)) - | |
| 3)); | |
| bitmap[_bit_word(bit_index)] |= current; | |
| #else | |
| bitmap[_bit_word(bit_index)] |= | |
| (current & 0xf) << (bit_index & BITSTR_MAXPOS); | |
| #endif | |
| curpos--; | |
| bit_index += 4; | |
| continue; | |
| } | |
| if (current & 1) { | |
| if (bit_index < bitsize) | |
| bit_set(bitmap, bit_index); | |
| else { | |
| rc = -1; | |
| break; | |
| } | |
| } | |
| if (current & 2) { | |
| if ((bit_index + 1) < bitsize) | |
| bit_set(bitmap, bit_index + 1); | |
| else { | |
| rc = -1; | |
| break; | |
| } | |
| } | |
| if (current & 4) { | |
| if ((bit_index + 2) < bitsize) | |
| bit_set(bitmap, bit_index + 2); | |
| else { | |
| rc = -1; | |
| break; | |
| } | |
| } | |
| if (current & 8) { | |
| if ((bit_index + 3) < bitsize) | |
| bit_set(bitmap, bit_index + 3); | |
| else { | |
| rc = -1; | |
| break; | |
| } | |
| } | |
| curpos--; | |
| bit_index += 4; | |
| } | |
| return rc; | |
| } | |
| /* Find the bit set at pos (0 - bitstr_bits) in bitstr b. | |
| * b (IN) bitstring to search | |
| * pos (IN) bit to search for | |
| * RETURN number bit is set in bitstring (-1 on error) | |
| */ | |
| bitoff_t | |
| bit_get_bit_num(bitstr_t *b, int32_t pos) | |
| { | |
| bitoff_t bit; | |
| int32_t cnt = 0; | |
| bitoff_t bit_cnt; | |
| _assert_bitstr_valid(b); | |
| bit_cnt = _bitstr_bits(b); | |
| xassert(pos <= bit_cnt); | |
| for (bit = 0; bit < bit_cnt; bit++) { | |
| if (bit_test(b, bit)) { /* we got one */ | |
| if (cnt == pos) | |
| break; | |
| cnt++; | |
| } | |
| } | |
| if (bit >= bit_cnt) | |
| bit = -1; | |
| return bit; | |
| } | |
| void bit_consolidate(bitstr_t *b) | |
| { | |
| int set_count = bit_set_count(b); | |
| if (set_count && (set_count < bit_size(b))) { | |
| bit_nclear(b, set_count, bit_size(b) - 1); | |
| bit_nset(b, 0, set_count - 1); | |
| } | |
| } |