// Copyright (c) 2025 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) internal U32Array bit_array_init32(Arena *arena, U64 word_count) { U32Array result; result.count = CeilIntegerDiv(word_count, 32); result.v = push_array(arena, U32, word_count); return result; } internal U64 bit_array_scan_left_to_right32(U32Array bit_array, U64 lo, U64 hi, B32 state) { Assert(lo < bit_array.count*32); Assert(hi <= bit_array.count*32); Assert(lo <= hi); Assert(state == 0 || state == 1); U64 word_lo = lo / 32; U64 word_hi = CeilIntegerDiv(hi, 32) - 1; U64 word_idx = word_lo; U64 bit_idx = 0; U64 scan_count = hi - lo; if (scan_count < 32) { U64 bit_lo = lo % 32; U64 bit_hi = hi % 32; U64 word = bit_array.v[word_idx]; word ^= state - 1; word &= (1U << bit_hi) - (1U << bit_lo); if (word) { bit_idx = ctz32(word); goto exit; } } else { U32 first_word = bit_array.v[word_idx]; first_word ^= state - 1; first_word &= ~0u << (lo % 32); if (first_word) { bit_idx = ctz32(first_word); goto exit; } for (word_idx += 1; word_idx < word_hi; word_idx += 1) { U32 word = bit_array.v[word_idx]; word ^= state - 1; if (word != 0) { bit_idx = ctz32(word); goto exit; } } U64 bit_hi = hi - (word_idx * 32); U32 last_word = bit_array.v[word_idx]; last_word ^= state - 1; last_word &= (1 << bit_hi) - 1; if (last_word) { bit_idx = ctz32(last_word); goto exit; } } word_idx = 0; bit_idx = max_U32; exit:; U64 result = word_idx * 32 + bit_idx; return result; } internal U64 bit_array_scan_right_to_left32(U32Array bit_array, U64 lo, U64 hi, B32 state) { Assert(lo <= hi); Assert(state == 0 || state == 1); S64 word_lo = lo / 32; S64 word_hi = CeilIntegerDiv(hi, 32) - 1; S64 word_idx = word_hi; S64 bit_idx = -1; U64 scan_count = hi - lo; if (scan_count < 32) { S64 bit_lo = lo % 32; S64 bit_hi = bit_lo + scan_count; U32 word = bit_array.v[word_idx]; for (bit_idx = bit_hi; bit_idx >= bit_lo; bit_idx -= 1) { U32 bit = ExtractBit(word, bit_idx); if (bit == state) { goto exit; } } } else { U32 last_word = bit_array.v[word_idx]; S64 bit_hi = hi % 32; for (bit_idx = bit_hi; bit_idx >= 0; bit_idx -= 1) { U32 bit = ExtractBit(last_word, bit_idx); if (bit == state) { goto exit; } } for (word_idx -= 1; word_idx > word_lo; word_idx -= 1) { U32 word = bit_array.v[word_idx]; for (bit_idx = 32 - 1; bit_idx >= 0; bit_idx -= 1) { U32 bit = ExtractBit(word, bit_idx); if (bit == state) { goto exit; } } } U32 first_word = bit_array.v[word_idx]; S64 bit_lo = lo % 32; for (bit_idx = 32 - 1; bit_idx >= bit_lo; bit_idx -= 1) { U32 bit = ExtractBit(first_word, bit_idx); if (bit == state) { goto exit; } } } word_idx = 0; bit_idx = max_U32; exit:; S64 result_s64 = word_idx * 32 + bit_idx; U64 result_u64 = (U64)result_s64; return result_u64; } internal Rng1U64 bit_array_scan_left_to_right32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count) { Rng1U64 result = rng_1u64(max_U64, max_U64); U64 curr_count = 0, rover = lo; while (curr_count < in_row_count) { rover = bit_array_scan_left_to_right32(bit_array, rover, hi, state); // no more bits in range if (rover >= hi) { break; } // set first match if (result.v[0] == max_U64) { result = rng_1u64(rover, rover); continue; } // reset on non-contiguous range B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover); if (is_bit_index_not_adjoined) { curr_count = 0; result = rng_1u64(max_U64, max_U64); continue; } // advance result.v[1] = rover; curr_count -= 1; } // did we allocate enough bits? if (curr_count != in_row_count) { result = rng_1u64(max_U64, max_U64); } return result; } internal Rng1U64 bit_array_scan_right_to_left32_contiguous(U32Array bit_array, U64 lo, U64 hi, B32 state, U64 in_row_count) { Rng1U64 result = rng_1u64(max_U64, max_U64); U64 curr_count = 0, rover = lo; while (curr_count < in_row_count) { rover = bit_array_scan_right_to_left32(bit_array, lo, rover, state); // no more bits in range if (rover >= hi) { break; } // set first match if (result.v[0] == max_U64) { result = rng_1u64(rover, rover); continue; } // reset on non-contiguous range B32 is_bit_index_not_adjoined = (result.v[0] + 1 < rover); if (is_bit_index_not_adjoined) { curr_count = 0; result = rng_1u64(max_U64, max_U64); continue; } // advance result.v[0] = rover; curr_count -= 1; } // did we allocate enough bits? if (curr_count != in_row_count) { result = rng_1u64(max_U64, max_U64); } return result; } internal U64 bit_array_find_next_unset_bit32(U32Array bit_array) { U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 0); return result; } internal U64 bit_array_find_next_set_bit32(U32Array bit_array) { U64 result = bit_array_scan_left_to_right32(bit_array, 0, bit_array.count*32, 1); return result; } internal void bit_array_set_bit32(U32Array bit_array, U64 idx, B32 state) { Assert(idx < bit_array.count*32); U64 word_idx = idx / 32; U64 bit_idx = idx % 32; if (state) { bit_array.v[word_idx] |= (1 << bit_idx); } else { bit_array.v[word_idx] &= ~(1 << bit_idx); } } internal void bit_array_set_bit_range32(U32Array bit_array, Rng1U64 range, B32 state) { for (U64 idx = range.min ; idx < range.max; idx += 1) { bit_array_set_bit32(bit_array, idx, state); } } internal U32 bit_array_get_bit32(U32Array bit_array, U64 idx) { Assert(idx < bit_array.count*32); U64 word_idx = idx / 32; U64 bit_idx = idx % 32; U32 bit = (bit_array.v[word_idx] & (1 << bit_idx)) >> bit_idx; return bit; } internal B32 bit_array_is_bit_set(U32Array bit_arr, U64 bit_pos) { U64 word_idx = bit_pos / 32; Assert(word_idx < bit_arr.count); U32 word = bit_arr.v[word_idx]; U64 bit_idx = bit_pos % 32; B32 is_set = !!(word & (1 << bit_idx)); return is_set; }