rexglue-skate3/include/rex/bit.h
2026-05-18 22:41:13 +01:00

145 lines
4.9 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015-2019 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*
* @modified 2026 Tom Clay <tomc@tctechstuff.com>
* Modified for rexglue - Xbox 360 recompilation framework
*
* @changes - Consolidated bit_map.h and bit_range.h into single header
*/
#pragma once
#include <climits>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <utility>
#include <vector>
#include <rex/math.h>
namespace rex::bit {
//=============================================================================
// BitMap - Efficient lookup of free/used entries
//=============================================================================
class BitMap {
public:
BitMap();
// Size is the number of entries, must be a multiple of 64.
BitMap(size_t size_bits);
// Data does not have to be aligned to a 4-byte boundary, but it is
// preferable.
// Size is the number of entries, must be a multiple of 64.
BitMap(uint64_t* data, size_t size_bits);
// (threadsafe) Acquires an entry and returns its index. Returns -1 if there
// are no more free entries.
size_t Acquire();
// (threadsafe) Releases an entry by an index.
void Release(size_t index);
// Resize the bitmap. Size is the number of entries, must be a multiple of 64.
void Resize(size_t new_size_bits);
// Sets all entries to free.
void Reset();
const std::vector<uint64_t> data() const { return data_; }
std::vector<uint64_t>& data() { return data_; }
private:
const static size_t kDataSize = 8;
const static size_t kDataSizeBits = kDataSize * 8;
std::vector<uint64_t> data_;
};
//=============================================================================
// Bit Range Utilities
//=============================================================================
// Provided length is in bits since the first. Returns <first, length> of the
// range in bits, with length == 0 if not found.
template <typename Block>
std::pair<size_t, size_t> GetNextRangeUnset(const Block* bits, size_t first, size_t length) {
if (!length) {
return std::make_pair(size_t(first), size_t(0));
}
size_t last = first + length - 1;
const size_t block_bits = sizeof(Block) * CHAR_BIT;
size_t block_first = first / block_bits;
size_t block_last = last / block_bits;
size_t range_start = SIZE_MAX;
for (size_t i = block_first; i <= block_last; ++i) {
Block block = bits[i];
// Ignore bits in the block outside the specified range by considering them
// set.
if (i == block_first) {
block |= (Block(1) << (first & (block_bits - 1))) - 1;
}
if (i == block_last && (last & (block_bits - 1)) != block_bits - 1) {
block |= ~((Block(1) << ((last & (block_bits - 1)) + 1)) - 1);
}
while (true) {
uint32_t block_bit;
if (range_start == SIZE_MAX) {
// Check if need to open a new range.
if (!rex::bit_scan_forward(~block, &block_bit)) {
break;
}
range_start = i * block_bits + block_bit;
} else {
// Check if need to close the range.
// Ignore the set bits before the beginning of the range.
Block block_bits_set_from_start = block;
if (i == range_start / block_bits) {
block_bits_set_from_start &= ~((Block(1) << (range_start & (block_bits - 1))) - 1);
}
if (!rex::bit_scan_forward(block_bits_set_from_start, &block_bit)) {
break;
}
return std::make_pair(range_start, (i * block_bits) + block_bit - range_start);
}
}
}
if (range_start != SIZE_MAX) {
return std::make_pair(range_start, last + size_t(1) - range_start);
}
return std::make_pair(first + length, size_t(0));
}
template <typename Block>
void SetRange(Block* bits, size_t first, size_t length) {
if (!length) {
return;
}
size_t last = first + length - 1;
const size_t block_bits = sizeof(Block) * CHAR_BIT;
size_t block_first = first / block_bits;
size_t block_last = last / block_bits;
Block set_first = ~((Block(1) << (first & (block_bits - 1))) - 1);
Block set_last = ~Block(0);
if ((last & (block_bits - 1)) != (block_bits - 1)) {
set_last &= (Block(1) << ((last & (block_bits - 1)) + 1)) - 1;
}
if (block_first == block_last) {
bits[block_first] |= set_first & set_last;
return;
}
bits[block_first] |= set_first;
if (block_first + 1 < block_last) {
std::memset(bits + block_first + 1, CHAR_MAX, (block_last - (block_first + 1)) * sizeof(Block));
}
bits[block_last] |= set_last;
}
} // namespace rex::bit