| /*****************************************************************************\ |
| * 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_nset_max_count,slurm_bit_nset_max_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_nffc, slurm_bit_nffc); |
| strong_alias(bit_noc, slurm_bit_noc); |
| strong_alias(bit_nffs, slurm_bit_nffs); |
| 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); |
| |
| 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); |
| } |
| |
| /* |
| * 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 the first n contiguous bits clear in b. |
| * b (IN) bitstring to search |
| * n (IN) number of bits needed |
| * RETURN position of first bit in range (-1 if none found) |
| */ |
| bitoff_t |
| bit_nffc(bitstr_t *b, int32_t n) |
| { |
| bitoff_t value = -1; |
| bitoff_t bit; |
| int32_t cnt = 0; |
| |
| _assert_bitstr_valid(b); |
| xassert(n > 0 && n < _bitstr_bits(b)); |
| |
| for (bit = 0; bit < _bitstr_bits(b); bit++) { |
| if (bit_test(b, bit)) { /* fail */ |
| cnt = 0; |
| } else { |
| cnt++; |
| if (cnt >= n) { |
| value = bit - (cnt - 1); |
| break; |
| } |
| } |
| } |
| |
| return value; |
| } |
| |
| /* Find n contiguous bits clear in b starting at some offset. |
| * b (IN) bitstring to search |
| * n (IN) number of bits needed |
| * seed (IN) position at which to begin search |
| * RETURN position of first bit in range (-1 if none found) |
| */ |
| bitoff_t |
| bit_noc(bitstr_t *b, int32_t n, int32_t seed) |
| { |
| bitoff_t value = -1; |
| bitoff_t bit; |
| int32_t cnt = 0; |
| |
| _assert_bitstr_valid(b); |
| xassert(n > 0 && n <= _bitstr_bits(b)); |
| |
| if ((seed + n) >= _bitstr_bits(b)) |
| seed = _bitstr_bits(b); /* skip offset test, too small */ |
| |
| for (bit = seed; bit < _bitstr_bits(b); bit++) { /* start at offset */ |
| if (bit_test(b, bit)) { /* fail */ |
| cnt = 0; |
| } else { |
| cnt++; |
| if (cnt >= n) { |
| value = bit - (cnt - 1); |
| return value; |
| } |
| } |
| } |
| |
| cnt = 0; /* start at beginning */ |
| for (bit = 0; bit < _bitstr_bits(b); bit++) { |
| if (bit_test(b, bit)) { /* fail */ |
| if (bit >= seed) |
| break; |
| cnt = 0; |
| } else { |
| cnt++; |
| if (cnt >= n) { |
| value = bit - (cnt - 1); |
| return value; |
| } |
| } |
| } |
| |
| return -1; |
| } |
| |
| /* Find the first n contiguous bits set in b. |
| * b (IN) bitstring to search |
| * n (IN) number of bits needed |
| * RETURN position of first bit in range (-1 if none found) |
| */ |
| bitoff_t |
| bit_nffs(bitstr_t *b, int32_t n) |
| { |
| bitoff_t value = -1; |
| bitoff_t bit; |
| int32_t cnt = 0; |
| |
| _assert_bitstr_valid(b); |
| xassert(n > 0 && n <= _bitstr_bits(b)); |
| |
| for (bit = 0; bit <= _bitstr_bits(b) - n; bit++) { |
| if (!bit_test(b, bit)) { /* fail */ |
| cnt = 0; |
| } else { |
| cnt++; |
| if (cnt >= n) { |
| value = bit - (cnt - 1); |
| break; |
| } |
| } |
| } |
| |
| 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)); |
| } |
| |
| /* Return the count of the largest number of contiguous bits set in b. |
| * b (IN) bitstring to search |
| * RETURN the largest number of contiguous bits set in b |
| */ |
| int32_t |
| bit_nset_max_count(bitstr_t *b) |
| { |
| bitoff_t bit; |
| int32_t cnt = 0; |
| int32_t maxcnt = 0; |
| uint32_t bitsize; |
| |
| _assert_bitstr_valid(b); |
| bitsize = _bitstr_bits(b); |
| |
| for (bit = 0; bit < bitsize; bit++) { |
| if (!bit_test(b, bit)) { /* no longer continuous */ |
| cnt = 0; |
| } else { |
| cnt++; |
| if (cnt > maxcnt) { |
| maxcnt = cnt; |
| } |
| } |
| if (cnt == 0 && ((bitsize - bit) < maxcnt)) { |
| break; /* already found max */ |
| } |
| } |
| |
| return maxcnt; |
| } |
| |
| /* |
| * 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); |
| } |
| } |