Skip to content

Instantly share code, notes, and snippets.

@kevyonan
Last active June 10, 2025 18:31
Show Gist options
  • Select an option

  • Save kevyonan/b6a982a18fd96ae748548331efcd82ec to your computer and use it in GitHub Desktop.

Select an option

Save kevyonan/b6a982a18fd96ae748548331efcd82ec to your computer and use it in GitHub Desktop.
/**
* bitset.inc
*
* Copyright [2025] Nergal the Ashurian
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"),
* to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE ANDNONINFRINGEMENT.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#if defined _bitset_included
#endinput
#endif
#define _bitset_included
#include <sourcemod>
enum {
INT_BIT_LEN = 32,
INT_BIT_LEN_IDX = 5,
};
stock int HighBitIndex(int a) {
int bit_index = 1;
for( int i = a >>> 1; i > 0; i >>>= 1 ) {
bit_index++;
}
return bit_index - 1;
}
stock int PopCount(int x) {
int count = 0;
for( int i = x; i > 0; count++ ) {
i &= (i - 1);
}
return count;
}
/// this assumes only ONE bit has been set in the entire bitset.
/// as this will return on the first non-zero, highest bit it can find.
stock int BitVecToBitIndex(const int[] bitvec, int len) {
int bitidx = 0;
for( int i=0; i < len; i++ ) {
if( bitvec[i]==0 ) {
bitidx += INT_BIT_LEN;
continue;
}
bitidx += HighBitIndex(bitvec[i]);
break;
}
return bitidx;
}
stock int BitIndexToBitPos(int bitidx) {
return bitidx & (INT_BIT_LEN-1); /// modulos by bit-size of type
}
stock int BitIndexToSlot(int bitidx) {
return bitidx >>> INT_BIT_LEN_IDX; /// divides by bit-size of type
}
void BitVecSHL(int[] bitvec, int n, int shift) {
if( shift==0 || n<=0 ) {
return;
}
int word_shift = shift >>> INT_BIT_LEN_IDX;
int bit_shift = shift & (INT_BIT_LEN-1);
int back_shift = INT_BIT_LEN - bit_shift;
if( word_shift >= n ) {
for( int i=0; i < n; i++ ) {
bitvec[i] = 0;
}
return;
}
for( int i = n - 1; i >= 0; --i ) {
int src = i - word_shift;
bitvec[i] = (src >= 0)? bitvec[src] : 0;
}
if( bit_shift==0 ) {
return;
}
int carry = 0;
for( int i = word_shift; i < n; i++ ) {
int cur = bitvec[i];
int next_carry = cur >>> back_shift;
bitvec[i] = (cur << bit_shift) | carry;
carry = next_carry;
}
int top = n - 1;
bitvec[top] |= carry;
}
void BitVecSHR(int[] bitvec, int n, int shift) {
if( shift==0 || n<=0 ) {
return;
}
int word_shift = shift >>> INT_BIT_LEN_IDX;
int bit_shift = shift & (INT_BIT_LEN-1);
int back_shift = INT_BIT_LEN - bit_shift;
if( word_shift >= n ) {
for( int i=0; i < n; i++ ) {
bitvec[i] = 0;
}
return;
}
for( int i=0; i < n; i++ ) {
int src = i + word_shift;
bitvec[i] = (src < n)? bitvec[src] : 0;
}
if( bit_shift==0 ) {
return;
}
int carry = 0;
for( int i = n-1; i >= 0; i-- ) {
int cur = bitvec[i];
int next_carry = cur << back_shift;
bitvec[i] = (cur >>> bit_shift) | carry;
carry = next_carry;
}
}
stock bool GenerateBitID(int[] gen_bits, int gen_bits_len, int &bits_idx) {
/// make sure bit index is within range.
if( bits_idx < 0 || bits_idx >= gen_bits_len ) {
return false;
} else if( bits_idx==(gen_bits_len - 1) && gen_bits[bits_idx]==0x80000000 ) {
/// if LAST bitset bit is 1, we're out of component bits...
return false;
} else if( gen_bits[bits_idx]==0x80000000 ) {
/// on last bit for the index, 0 and move 1 to the next index.
gen_bits[bits_idx] = 0;
bits_idx++;
gen_bits[bits_idx] = 1;
} else if( gen_bits[bits_idx]==0 ) {
/// set up 1st bit so shifting can work.
gen_bits[bits_idx] = 1;
} else {
gen_bits[bits_idx] <<= 1;
}
return true;
}
stock void BitVecSet(int[] bitvec, int bitidx) {
bitvec[BitIndexToSlot(bitidx)] |= (1 << BitIndexToBitPos(bitidx));
}
stock void BitVecClear(int[] bitvec, int bitidx) {
bitvec[BitIndexToSlot(bitidx)] &= ~(1 << BitIndexToBitPos(bitidx));
}
stock void BitVecToggle(int[] bitvec, int bitidx) {
bitvec[BitIndexToSlot(bitidx)] ^= (1 << BitIndexToBitPos(bitidx));
}
stock bool BitVecTest(const int[] bitvec, int bitidx) {
return (bitvec[BitIndexToSlot(bitidx)] & (1 << BitIndexToBitPos(bitidx))) != 0;
}
stock void BitVecAND(const int[] bitvec1, const int[] bitvec2, int[] res, int n) {
for( int i=0; i < n; i++ ) {
res[i] = bitvec1[i] & bitvec2[i];
}
}
stock void BitVecOR(const int[] bitvec1, const int[] bitvec2, int[] res, int n) {
for( int i=0; i < n; i++ ) {
res[i] = bitvec1[i] | bitvec2[i];
}
}
stock void BitVecXOR(const int[] bitvec1, const int[] bitvec2, int[] res, int n) {
for( int i=0; i < n; i++ ) {
res[i] = bitvec1[i] ^ bitvec2[i];
}
}
stock int BitVecPopCount(const int[] bitvec, int n) {
int count = 0;
for( int i=0; i < n; i++ ) {
count += PopCount(bitvec[i]);
}
return count;
}
stock void BitVecCopy(const int[] src, int[] dst, int n) {
for( int i=0; i < n; i++ ) {
dst[i] = src[i];
}
}
static stock void reverse_ints(int[] v, int lo, int hi) {
while( lo < hi ) {
int tmp = v[lo];
v[lo++] = v[hi];
v[hi--] = tmp;
}
}
static stock void word_rotate_left(int[] v, int n, int k) {
if( (k %= n)<=0 ) {
return;
}
reverse_ints(v, 0, k-1);
reverse_ints(v, k, n-1);
reverse_ints(v, 0, n-1);
}
static stock void word_rotate_right(int[] v, int n, int k) {
if( (k %= n)<=0 ) {
return;
}
word_rotate_left(v, n, n - k);
}
stock void BitVecROTL(int[] bitvec, int n, int shift) {
if( n<=0 ) {
return;
}
int total_bits = INT_BIT_LEN * n;
shift %= total_bits;
if( shift <= 0 ) {
return;
}
int word_shift = shift >>> INT_BIT_LEN_IDX;
int bit_shift = shift & (INT_BIT_LEN - 1);
word_rotate_right(bitvec, n, word_shift);
if( bit_shift==0 ) {
return;
}
int carry = 0;
int back_shift = INT_BIT_LEN - bit_shift;
for( int i=0; i < n; i++ ) {
int cur = bitvec[i];
int next_carry = cur >>> back_shift;
bitvec[i] = (cur << bit_shift) | carry;
carry = next_carry;
}
bitvec[0] |= carry;
}
void BitVecROTR(int[] bitvec, int n, int shift) {
if( n==0 ) {
return;
}
int total_bits = INT_BIT_LEN * n;
shift %= total_bits;
if( shift==0 ) {
return;
}
int word_shift = shift >>> INT_BIT_LEN_IDX;
int bit_shift = shift & (INT_BIT_LEN - 1);
word_rotate_left(bitvec, n, word_shift);
if( bit_shift==0 ) {
return;
}
int carry = 0;
int back_shift = INT_BIT_LEN - bit_shift;
for( int i = n-1; i >= 0; i-- ) {
int cur = bitvec[i];
int next_carry = cur << back_shift;
bitvec[i] = (cur >>> bit_shift) | carry;
carry = next_carry;
}
bitvec[n-1] |= carry;
}
/// int[][] player_bits = new int[MaxClients][NUM_BITS];
stock void BitSetCopy(int n, const int[][] bits2D, int[] buf, int buflen) {
for( int i; i < buflen; i++ ) {
buf[i] = bits2D[n][i];
}
}
stock void BitSetSetByBits(int n, int[][] bits2D, const int[] query_bits, int bits_len) {
for( int i; i < bits_len; i++ ) {
bits2D[n][i] |= query_bits[i];
}
}
stock void BitSetSetByBitIdx(int n, int[][] bits2D, int bitidx) {
BitVecSet(bits2D[n], bitidx);
}
stock void BitSetClearByBits(int n, int[][] bits2D, const int[] query_bits, int bits_len) {
for( int i; i < bits_len; i++ ) {
bits2D[n][i] &= ~query_bits[i];
}
}
stock void BitSetClearByBitIdx(int n, int[][] bits2D, int bitidx) {
BitVec_Clear(bits2D[n], bitidx);
}
stock bool BitSetHas(int n, const int[][] bits2D, const int[] query_bits, int bits_len) {
bool result = true;
for( int i; i < bits_len; i++ ) {
if( query_bits[i]==0 ) {
continue;
}
result = result && (bits2D[n][i] & query_bits[i]) != 0;
}
return result;
}
/// int[] player_buf = new int[MaxClients];
stock int QueryPlayers(const int[][] bits2D, const int[] query_bits, int bits_len, int[] player_buf) {
int k;
for( int i=MaxClients; i > 0; i-- ) {
if( !(0 < 1 <= MaxClients && IsClientInGame(i)) ) {
continue;
} else if( BitSet_Has(i, bits2D, query_bits, bits_len) ) {
player_buf[k++] = GetClientUserId(i);
}
}
return k;
}
/**
* ecs_helper.inc
*
* Copyright [2022] Nergal the Ashurian
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"),
* to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE ANDNONINFRINGEMENT.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#if defined _ecs_helper_included
#endinput
#endif
#define _ecs_helper_included
#include <sourcemod>
/**
* Entity Component System for SourcePawn.
*
* Entities & Components
* * An entity is a unique identifier [userids and entity references]
* * A component can optionally be associated with a plain old datatype [enum struct, methodmap, or array data]
* * A component identifier is an entity [handled via int or handle types]
* * An entity can have 0 ... N components
* * A component can be annotated with a role
* * An <entity, component> tuple can have 0 ... N components
*
* An action called a 'query' filters entities by a matching set of components.
*
*
* Systems
* * A system is logic matched with entities based on their components
* * A system is invoked as result of an event
* * A component mutation is an event
* * Computing a simulation frame is an event
* * A frame is divided into N phases
* * Each system is assigned to a phase
*
* In a nutshell, Systems are an entity-component query & a callback.
*/
/// with 4 bit-counts, max 128 components per entity.
/// 4 * 32 == 128 bits.
enum {
COMPONENT_BIT_LEN = 1024,
INT_BIT_LEN = 32,
INT_BIT_LEN_IDX = 5,
};
enum {
COMPONENT_LEN = COMPONENT_BIT_LEN / INT_BIT_LEN,
};
stock int BitOfBitIdx(int bitidx) {
return bitidx & (INT32_BIT_LEN-1); /// modulos by bit-size of type
}
stock int SlotOfBitIdx(int bitidx) {
return bitidx >>> INT32_BIT_LEN_IDX; /// divides by bit-size of type
}
/// returns the index of where the bit is used.
stock int BitIndexToSlotAndBit(int bitidx, int &bit) {
bit = BitOfBitIdx(bitidx);
return SlotOfBitIdx(bitidx);
}
stock int SlotAndBitToBitIndex(int slot, int bit) {
return (slot << (INT_BIT_LEN_IDX)) + bit;
}
stock bool GenerateBitID(int[] gen_bits, int gen_bits_len, int &arr_idx, int &bit_idx=0) {
/// make sure bit index is within range.
if( arr_idx < 0 || arr_idx >= gen_bits_len ) {
return false;
} else if( arr_idx==(gen_bits_len - 1) && gen_bits[arr_idx]==0x80000000 ) {
/// if LAST bitset bit is 1, we're out of component bits...
bit_idx = COMPONENT_BIT_LEN;
return false;
} else if( gen_bits[arr_idx]==0x80000000 ) {
/// on last bit for the index, 0 and move 1 to the next index.
gen_bits[arr_idx] = 0;
arr_idx++;
gen_bits[arr_idx] = 1;
bit_idx++;
} else if( gen_bits[arr_idx]==0 ) {
/// set up 1st bit so shifting can work.
gen_bits[arr_idx] = 1;
bit_idx = 0;
} else {
gen_bits[arr_idx] <<= 1;
bit_idx++;
}
return true;
}
stock void BitVec_Set(int[] bitvec, int bitidx) {
int bit;
int slot = BitIndexToSlotAndBit(bitidx, bit);
bitvec[slot] |= (1 << bit);
}
stock void BitVec_Clear(int[] bitvec, int bitidx) {
int bit;
int slot = BitIndexToSlotAndBit(bitidx, bit);
bitvec[slot] &= ~(1 << bit);
}
stock void BitVec_Toggle(int[] bitvec, int bitidx) {
int bit;
int slot = BitIndexToSlotAndBit(bitidx, bit);
bitvec[slot] ^= (1 << bit);
}
stock bool BitVec_Has(int[] bitvec, int bitidx) {
int bit;
int slot = BitIndexToSlotAndBit(bitidx, bit);
return (bitvec[slot] & (1 << bit)) > 0;
}
stock void BitSet_Copy(int n, int[][] bits2D, int[] buf, int buflen) {
for( int i; i < buflen; i++ ) {
buf[i] = bits2D[n][i];
}
}
stock void BitSet_SetByBits(int n, int[][] bits2D, int[] query_bits, int bits_len) {
for( int i; i < bits_len; i++ ) {
bits2D[n][i] |= query_bits[i];
}
}
stock void BitSet_SetByBitIdx(int n, int[][] bits2D, int bitidx) {
BitVec_Set(bits2D[n], bitidx);
}
stock void BitSet_ClearByBits(int n, int[][] bits2D, int[] query_bits, int bits_len) {
for( int i; i < bits_len; i++ ) {
bits2D[n][i] &= ~query_bits[i];
}
}
stock void BitSet_ClearByBitIdx(int n, int[][] bits2D, int bitidx) {
BitVec_Clear(bits2D[n], bitidx);
}
stock bool BitSet_Has(int n, int[][] bits2D, int[] query_bits, int bits_len) {
bool result = true;
for( int i; i < bits_len; i++ ) {
if( query_bits[i]==0 ) {
continue;
}
result = result && (bits2D[n][i] & query_bits[i]) > 0;
}
return result;
}
stock int QueryPlayers(int[][] bits2D, int[] query_bits, int bits_len, int[/** MaxClients */] player_buf) {
int k;
for( int i=MaxClients; i > 0; i-- ) {
if( !(0 < i <= MaxClients) || !IsClientInGame(i) ) {
continue;
} else if( BitSet_Has(i, bits2D, query_bits, bits_len) ) {
player_buf[k++] = GetClientUserId(i);
}
}
return k;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment