/src/mask.c

https://github.com/nickg/nvc · C · 204 lines · 145 code · 37 blank · 22 comment · 45 complexity · 762184996181105a68a07840fa487384 MD5 · raw file

  1. //
  2. // Copyright (C) 2022 Nick Gasson
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. //
  17. #include "util.h"
  18. #include "mask.h"
  19. #include <assert.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. void mask_init(bit_mask_t *m, size_t size)
  23. {
  24. m->size = size;
  25. if (size > 64)
  26. m->ptr = xcalloc_array((size + 63) / 64, sizeof(uint64_t));
  27. else
  28. m->bits = 0;
  29. }
  30. void mask_free(bit_mask_t *m)
  31. {
  32. if (m->size > 64)
  33. free(m->ptr);
  34. m->bits = 0;
  35. m->size = 0;
  36. }
  37. static inline uint64_t mask_for_range(int low, int high)
  38. {
  39. assert(low >= 0 && low < 64);
  40. assert(high >= 0 && high < 64);
  41. assert(low <= high);
  42. uint64_t mask = ~UINT64_C(0);
  43. if (high < 63)
  44. mask &= (UINT64_C(1) << (high + 1)) - 1;
  45. if (low > 0)
  46. mask &= ~((UINT64_C(1) << low) - 1);
  47. assert(__builtin_popcountll(mask) == high - low + 1);
  48. return mask;
  49. }
  50. void mask_clear_range(bit_mask_t *m, int start, int count)
  51. {
  52. if (m->size <= 64) {
  53. m->bits &= ~mask_for_range(start, start + count - 1);
  54. return;
  55. }
  56. if (count > 0 && start % 64 != 0) {
  57. // Pre-loop: clear range of bits in first 64-bit word
  58. const int low = start % 64;
  59. const int high = MIN(low + count - 1, 63);
  60. const int nbits = high - low + 1;
  61. m->ptr[start / 64] &= ~mask_for_range(low, high);
  62. start += nbits;
  63. count -= nbits;
  64. }
  65. if (count > 0) {
  66. // Main loop: clear complete 64-bit words
  67. for (; count >= 64; count -= 64, start += 64)
  68. m->ptr[start / 64] = 0;
  69. }
  70. if (count > 0) {
  71. // Post-loop: clear range of bits in last 64-bit word
  72. assert(start % 64 == 0);
  73. const int high = MIN(count - 1, 63);
  74. m->ptr[start / 64] &= ~mask_for_range(0, high);
  75. assert(count == high + 1);
  76. }
  77. }
  78. void mask_set_range(bit_mask_t *m, int start, int count)
  79. {
  80. if (m->size <= 64) {
  81. m->bits |= mask_for_range(start, start + count - 1);
  82. return;
  83. }
  84. if (count > 0 && start % 64 != 0) {
  85. // Pre-loop: set range of bits in first 64-bit word
  86. const int low = start % 64;
  87. const int high = MIN(low + count - 1, 63);
  88. const int nbits = high - low + 1;
  89. m->ptr[start / 64] |= mask_for_range(low, high);
  90. start += nbits;
  91. count -= nbits;
  92. }
  93. if (count > 0) {
  94. // Main loop: set complete 64-bit words
  95. for (; count >= 64; count -= 64, start += 64)
  96. m->ptr[start / 64] = ~UINT64_C(0);
  97. }
  98. if (count > 0) {
  99. // Post-loop: set range of bits in last 64-bit word
  100. assert(start % 64 == 0);
  101. const int high = MIN(count - 1, 63);
  102. m->ptr[start / 64] |= mask_for_range(0, high);
  103. assert(count == high + 1);
  104. }
  105. }
  106. int mask_popcount(bit_mask_t *m)
  107. {
  108. if (m->size > 64) {
  109. int sum = 0;
  110. for (int i = 0; i < (m->size + 63) / 64; i++)
  111. sum += __builtin_popcountll(m->ptr[i]);
  112. return sum;
  113. }
  114. else
  115. return __builtin_popcountll(m->bits);
  116. }
  117. void mask_setall(bit_mask_t *m)
  118. {
  119. mask_set_range(m, 0, m->size);
  120. }
  121. void mask_clearall(bit_mask_t *m)
  122. {
  123. mask_clear_range(m, 0, m->size);
  124. }
  125. int mask_scan_backwards(bit_mask_t *m, int bit)
  126. {
  127. if (m->size <= 64) {
  128. uint64_t word0 = m->bits & mask_for_range(0, bit);
  129. return word0 == 0 ? -1 : 63 - __builtin_clzll(word0);
  130. }
  131. uint64_t word0 = m->ptr[bit / 64];
  132. word0 &= mask_for_range(0, bit % 64);
  133. if (word0 != 0)
  134. return (bit | 63) - __builtin_clzll(word0);
  135. bit -= bit % 64 + 1;
  136. for (; bit > 0 && m->ptr[bit / 64] == 0; bit -= 64);
  137. if (bit > 0)
  138. return (bit | 63) - __builtin_clzll(m->ptr[bit / 64]);
  139. return -1;
  140. }
  141. int mask_count_clear(bit_mask_t *m, int bit)
  142. {
  143. assert(bit < m->size);
  144. if (m->size <= 64) {
  145. const int fs = __builtin_ffsll(m->bits & mask_for_range(bit, 63));
  146. return fs > 0 ? fs - 1 - bit : m->size - bit;
  147. }
  148. int count = 0;
  149. const int modbits = bit % 64, maxbits = MIN(64, m->size - (bit & ~63));
  150. if (modbits > 0) {
  151. uint64_t word0 = m->ptr[bit / 64];
  152. word0 &= mask_for_range(modbits, maxbits - 1);
  153. const int fs = __builtin_ffsll(word0);
  154. if (fs > 0)
  155. return fs - 1 - modbits;
  156. count = maxbits - modbits;
  157. bit = bit + maxbits - modbits;
  158. }
  159. if (bit == m->size)
  160. return count;
  161. assert(bit % 64 == 0);
  162. for (; bit + 64 < m->size && m->ptr[bit / 64] == 0; bit += 64, count += 64);
  163. const int fs = __builtin_ffsll(m->ptr[bit / 64]);
  164. if (fs > 0)
  165. return count + fs - 1;
  166. return count + m->size - bit;
  167. }