• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] BitFlags (boolean bit fields)

Level 12
Joined
Jun 12, 2010
Messages
413
BitFlags

With patch 1.31, Blizzard introduced many new natives that function as flags in a a bit field,
such as the meta keys in the oskeytype events. This library introduces utility functions that make
code featuring boolean flags in bit fields much clearer. It also provides constants for all powers
of 2 available (these can be used as actual flags for the bit field).

Code
JASS:
library BitFlags
/*
*   v1.1.0 - by Guhun
*
*
*   With patch 1.31, Blizzard introduced many new natives that function as flags in a a bit field,
* such as the meta keys in the oskeytype events. This library introduces utility functions that make
* code featuring boolean flags in bit fields much clearer. It also provides constants for all powers
* of 2 available (these can be used as actual flags for the bit field).
*
*
*
************
* Configuration
************
*/
globals

// If this is set to true, then the library will initialize an array for all powers of 2 from 0 to 31.
// Be aware that the 31st power is negative, because 32 bit integers cannot hold this value as a positive number.
public constant boolean CACHE_POWERS = false

endglobals
//! novjass
'                                                                                                  '
'                                              API                                                 '

/*
    Structs
*/

// Holds constants that represent powers of 2. All of these constants will be inlined by JassHelper.
struct Pow2 extends array

    static constant n0 = 1
    static constant n1 = 2
    static constant n2 = 4
    ...
    static constant n30 = 1073741824
    static constant n31 = - 2147483648 $negative$
   
    // Returns a power of 2. If <power> == 31, returns the negative value. Set CACHE_POWERS to true to make this faster.
    method operator [] takes integer power returns integer

endstruct

/*
    Functions
*/
$inlines$ -> "this means a function inlines, so only natives are actually called"

// Returns true if <field> contains all flags in <flags>.
function BitAll takes integer field, integer flags returns boolean

// Returns true if <field> contains at least one flag in <flags>.
$inlines$
function BitAny takes integer field, integer flags returns boolean

// Returns true if <field> contains no flags in <flags>. Equivalent to 'not BitAny(fields, flags)'.
$inlines$
function BitNone takes integer field, integer flags returns boolean

// Returns true if <field> contains at least one flag in <flags> but, not all of them.
function BitNotAll takes integer field, integer flags returns boolean

// Returns true if <field> contains only a single flag in <flags>.
function BitSingle takes integer field, integer flags returns boolean

// Counts the number of ones in the binary representation of <x>.
function BitCountOnes takes integer x returns integer

// Counts the number of zeroes in the binary representation of <x>.
function BitCountZeroes takes integer x returns integer

/*
    Examples
*/
BitAll(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3)           -> true
BitAll(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3+Pow2.n4)   -> false

BitAny(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3 + Pow2.n4) -> true
BitAny(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n0)                     -> false

BitNotAll(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n2 + Pow2.n3)           -> false
BitNotAll(Pow2.n2 + Pow2.n5           ,  Pow2.n2 + Pow2.n3)           -> true

BitSingle(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n2 + Pow2.n3)           -> false
BitSingle(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n5)                     -> true

BitCountOnes(Pow2.n2)                     -> 1
BitCountOnes(Pow2.n7 + Pow2.n10)          -> 2
BitCountOnes(Pow2.n2 + Pow2.n3 + Pow2.n5) -> 3

BitCountZeroes(0)                           -> 32
BitCountZeroes(Pow2.n2)                     -> 31
BitCountZeroes(Pow2.n7 + Pow2.n10)          -> 30
BitCountZeroes(Pow2.n2 + Pow2.n3 + Pow2.n5) -> 20
BitCountZeroes(-1)                          -> 0
'                                                                                                  '
'                                         Source Code                                              '
//! endnovjass

private keyword InitModule
//! textmacro BitFlags_Pow2 takes pow, value
    static constant method operator n$pow$ takes nothing returns integer
        return $value$
    endmethod
//! endtextmacro
struct Pow2 extends array
    //! runtextmacro BitFlags_Pow2("0","1")
    //! runtextmacro BitFlags_Pow2("1","2")
    //! runtextmacro BitFlags_Pow2("2","4")
    //! runtextmacro BitFlags_Pow2("3","8")
    //! runtextmacro BitFlags_Pow2("4","16")
    //! runtextmacro BitFlags_Pow2("5","32")
    //! runtextmacro BitFlags_Pow2("6","64")
    //! runtextmacro BitFlags_Pow2("7","128")
    //! runtextmacro BitFlags_Pow2("8","256")
    //! runtextmacro BitFlags_Pow2("9","512")
    //! runtextmacro BitFlags_Pow2("10","1024")
    //! runtextmacro BitFlags_Pow2("11","2048")
    //! runtextmacro BitFlags_Pow2("12","4096")
    //! runtextmacro BitFlags_Pow2("13","8192")
    //! runtextmacro BitFlags_Pow2("14","16384")
    //! runtextmacro BitFlags_Pow2("15","32768")
    //! runtextmacro BitFlags_Pow2("16","65536")
    //! runtextmacro BitFlags_Pow2("17","131072")
    //! runtextmacro BitFlags_Pow2("18","262144")
    //! runtextmacro BitFlags_Pow2("19","524288")
    //! runtextmacro BitFlags_Pow2("20","1048576")
    //! runtextmacro BitFlags_Pow2("21","2097152")
    //! runtextmacro BitFlags_Pow2("22","4194304")
    //! runtextmacro BitFlags_Pow2("23","8388608")
    //! runtextmacro BitFlags_Pow2("24","16777216")
    //! runtextmacro BitFlags_Pow2("25","33554432")
    //! runtextmacro BitFlags_Pow2("26","67108864")
    //! runtextmacro BitFlags_Pow2("27","134217728")
    //! runtextmacro BitFlags_Pow2("28","268435456")
    //! runtextmacro BitFlags_Pow2("29","536870912")
    //! runtextmacro BitFlags_Pow2("30","1073741824")
    //! runtextmacro BitFlags_Pow2("31","-2147483648")
   
    private integer value
   
    method operator[] takes integer power returns integer
        static if CACHE_POWERS then
            return .value
        else
            if power == 31 then
                return thistype.n31
            else
                return R2I(Pow(2, power))
            endif
        endif
    endmethod
   
    static if CACHE_POWERS then
        implement InitModule
    endif
           
endstruct

function BitAll takes integer field, integer flags returns boolean
    return BlzBitAnd(field, flags) == flags
endfunction

function BitAny takes integer field, integer flags returns boolean
    return BlzBitAnd(field, flags) != 0
endfunction

function BitNone takes integer field, integer flags returns boolean
    return BlzBitAnd(field, flags) == 0
endfunction

function BitNotAll takes integer field, integer flags returns boolean
    local integer x = BlzBitAnd(field, flags)
    return x != flags and x != 0
endfunction

function BitSingle takes integer field, integer flags returns boolean
    local integer x = BlzBitAnd(field, flags)
    return x==1 or (x!=0 and BlzBitAnd(x, x-1) == 0)
endfunction


//! textmacro BitCountStep takes step
    if Pow2.n$step$ > x then
        return count
    endif
    if BitAny(x, Pow2.n$step$) then
        set count = count + 1
    endif
//! endtextmacro
function BitCountOnes takes integer x returns integer
    local integer count = 0
         
    if x < 0 then
        set count = count + 1
        set x = x - Pow2.n31
    endif
   
    //! runtextmacro BitCountStep("0")
    //! runtextmacro BitCountStep("1")
    //! runtextmacro BitCountStep("2")
    //! runtextmacro BitCountStep("3")
    //! runtextmacro BitCountStep("4")
    //! runtextmacro BitCountStep("5")
    //! runtextmacro BitCountStep("6")
    //! runtextmacro BitCountStep("7")
    //! runtextmacro BitCountStep("8")
    //! runtextmacro BitCountStep("9")
    //! runtextmacro BitCountStep("10")
    //! runtextmacro BitCountStep("11")
    //! runtextmacro BitCountStep("12")
    //! runtextmacro BitCountStep("13")
    //! runtextmacro BitCountStep("14")
    //! runtextmacro BitCountStep("15")
    //! runtextmacro BitCountStep("16")
    //! runtextmacro BitCountStep("17")
    //! runtextmacro BitCountStep("18")
    //! runtextmacro BitCountStep("19")
    //! runtextmacro BitCountStep("20")
    //! runtextmacro BitCountStep("21")
    //! runtextmacro BitCountStep("22")
    //! runtextmacro BitCountStep("23")
    //! runtextmacro BitCountStep("24")
    //! runtextmacro BitCountStep("25")
    //! runtextmacro BitCountStep("26")       
    //! runtextmacro BitCountStep("27")
    //! runtextmacro BitCountStep("28")
    //! runtextmacro BitCountStep("29")
    //! runtextmacro BitCountStep("30")
   
    return count
endfunction


function BitCountZeroes takes integer x returns integer
    return 32 - BitCountOnes(x)
endfunction

private module InitModule
    private static method onInit takes nothing returns nothing
        set thistype(0).value = thistype.n0
        set thistype(1).value = thistype.n1
        set thistype(2).value = thistype.n2
        set thistype(3).value = thistype.n3
        set thistype(4).value = thistype.n4
        set thistype(5).value = thistype.n5
        set thistype(6).value = thistype.n6
        set thistype(7).value = thistype.n7
        set thistype(8).value = thistype.n8
        set thistype(9).value = thistype.n9
        set thistype(10).value = thistype.n10
        set thistype(11).value = thistype.n11
        set thistype(12).value = thistype.n12
        set thistype(13).value = thistype.n13
        set thistype(14).value = thistype.n14
        set thistype(15).value = thistype.n15
        set thistype(16).value = thistype.n16
        set thistype(17).value = thistype.n17
        set thistype(18).value = thistype.n18
        set thistype(19).value = thistype.n19
        set thistype(20).value = thistype.n20
        set thistype(21).value = thistype.n21
        set thistype(22).value = thistype.n22
        set thistype(23).value = thistype.n23
        set thistype(24).value = thistype.n24
        set thistype(25).value = thistype.n25
        set thistype(26).value = thistype.n26
        set thistype(27).value = thistype.n27
        set thistype(28).value = thistype.n28
        set thistype(29).value = thistype.n29
        set thistype(30).value = thistype.n30
        set thistype(31).value = thistype.n31
    endmethod
endmodule
   
endlibrary

Unit Tests
Create an untitled trigger in an empty map with the library present, then paste this code into it.
JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
local integer x

call BJDebugMsg("BitAll")

if BitAll(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3) == true then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif
if BitAll(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3+Pow2.n4) == false then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif

call BJDebugMsg("BitAny")

if BitAny(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n2 + Pow2.n3 + Pow2.n4) == true then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif
if BitAny(Pow2.n2 + Pow2.n3 + Pow2.n5    ,  Pow2.n0) == false then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif

call BJDebugMsg("BitNotAll")

if BitNotAll(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n2 + Pow2.n3)== false then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif
if BitNotAll(Pow2.n2 + Pow2.n5           ,  Pow2.n2 + Pow2.n3)== true then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif

call BJDebugMsg("BitSingle")

if BitSingle(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n2 + Pow2.n3) == false then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif
if BitSingle(Pow2.n2 + Pow2.n3 + Pow2.n5 ,  Pow2.n5) == true then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure")
endif

call BJDebugMsg("BitCountOnes")

set x = BitCountOnes(Pow2.n2)
if x == 1 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountOnes(Pow2.n7 + Pow2.n10)
if x == 2 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountOnes(Pow2.n2 + Pow2.n3 + Pow2.n5)
if x == 3 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif

call BJDebugMsg("BitCountZeroes")

set x = BitCountZeroes(0)
if x == 32 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountZeroes(Pow2.n2)
if x == 31 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountZeroes(Pow2.n7 + Pow2.n10)
if x == 30 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountZeroes(Pow2.n2 + Pow2.n3 + Pow2.n5)
if x == 29 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
set x = BitCountZeroes(-1)
if x == 0 then
    call BJDebugMsg("Success")
else
    call BJDebugMsg("Failure: " + I2S(x))
endif
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerRegisterPlayerChatEvent( gg_trg_Untitled_Trigger_001, Player(0), "test", true )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
 
Last edited:
Level 12
Joined
Jun 12, 2010
Messages
413
I'd prefer instead of duplicating code via texmacros to just use an array lookup to get the low-number power values (like what's used in ASCII to get the Char/Ord figures).
I guess that's pretty useful addition for usability, I was mostly going for efficiency and minimal resources with the constants. I'll add caching for powers as an option, since that will require an init function.


EDIT: Updated library with new CACHE_POWERS configuration boolean and a new [] method for the Pow2 struct.
 
Last edited:
Level 12
Joined
Jun 12, 2010
Messages
413
Eh, the textmacros will run anyway and defeat the purpose. If you prefer the slight possibility of faster speed, just split this into two resources (option A w/macro, B with array).

The huge if-chain might add more overhead than the array.

All the constant functions in the first textmacro will be gone when a user optimizes the map, so I don't think it's worth making another resource just to avoid textmacro usage for people who won't optimize their maps. If they don't optimize, they won't care about these very niche performance considerations anyway.

Hmmm, I'm kinda confused now. How would you change the if-chain in order to reduce overhead? Using an array implementation of the powers in the code as is would simply change an inlined number into an array lookup, which would certainly be slower. The if chain is just an inlined loop.

This is the if-chain as a loop (loops add some overhead in JASS, so inlining loops is a good option, since this is a pretty static resource, because not much will ever change about bit fields).
JASS:
function BitCountOnes takes integer x returns integer
    local integer i = 0
    local integer count= 0
    local integer pow
   
    if x < 0 then
        set count=count + 1
        set x=x - (- 2147483648) // INLINED!!
    endif
    loop
        exitwhen i > 30

        set pow = Pow2[i]
        exitwhen pow > x

        if BitAny(x, pow) then
            set count = count + 1
        endif
        set i = i + 1
    endloop

    return count
endfunction

JASS:
function BitCountOnes takes integer x returns integer
    local integer count= 0
   
    if x < 0 then
        set count=count + 1
        set x=x - (- 2147483648) // INLINED!!
    endif
 

    if (1) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (1))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (2) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (2))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (4) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (4))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (8) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (8))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (16) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (16))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (32) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (32))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (64) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (64))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (128) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (128))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (256) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (256))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (512) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (512))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (1024) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (1024))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (2048) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (2048))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (4096) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (4096))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (8192) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (8192))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (16384) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (16384))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (32768) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (32768))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (65536) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (65536))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (131072) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (131072))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (262144) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (262144))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (524288) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (524288))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (1048576) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (1048576))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (2097152) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (2097152))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (4194304) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (4194304))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (8388608) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (8388608))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (16777216) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (16777216))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (33554432) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (33554432))) != 0) then // INLINED!!
        set count=count + 1
    endif

 
    if (67108864) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (67108864))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (134217728) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (134217728))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (268435456) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (268435456))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (536870912) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (536870912))) != 0) then // INLINED!!
        set count=count + 1
    endif

    if (1073741824) > x then // INLINED!!
        return count
    endif
    if (BlzBitAnd((x ), ( (1073741824))) != 0) then // INLINED!!
        set count=count + 1
    endif
 
    return count
endfunction
 
Last edited:
Top