[ANN] New package: BitFlags.jl — An enum-like type for bit flags

I’d like to announce BitFlags.jl, a package that provides an @enum-like macro that constructs types tailored for use with bit flags. It’s largely a borrowing of the Enum code with minor modifications, so almost all the credit goes to everyone who worked on the Base implementation. What it provides on top of a regular Enum is:

  • Automatic power-of-two numbering, and enforcement that members are a power of two
  • Binary AND (&) and OR (|) operations are defined
  • Pretty-printing of values as combinations of members

For example, my motivation was to make data structures dumped from memory easier to introspect — e.g.

julia> # Setup dummy data — could be file instead

julia> buf = IOBuffer(); write(buf, Int32(1), UInt32(11), Int64(100)); seekstart(buf);

julia> using BitFlags

julia> @bitflag DummyFeatures::UInt32 begin
           FEAT_ACTIVE
           FEAT_READ
           FEAT_WRITE
           FEAT_BACKUP
           FEAT_SHARED
       end

julia> struct DummyHeader
           version::Int32
           features::DummyFeatures
           payload_size::Int64
       end

julia> reinterpret(DummyHeader, read(buf, sizeof(DummyHeader)))[1]
DummyHeader(1, FEAT_ACTIVE | FEAT_READ | FEAT_BACKUP::DummyFeatures = 0x0000000b, 100)

Hopefully others can derive some utility from this simple package.

7 Likes

Cool. Should be coming to Base sometime too: RFC: EnumSet type by simonbyrne · Pull Request #19470 · JuliaLang/julia · GitHub

4 Likes

Ah, interesting! I’d tried searching for such a feature last weekend and apparently never came up with the right search query to bring up that PR.

Hi. Thanks! This is really helpful.

I found one behavior of the code which I don’t know if is expected: the bitwise and operation between two constants cuases an error. For example:

@bitflag flag begin
f1
f2
f3
end

[ sorry, I pressed ‘Reply’ too early by error]

f1 & f2 # causes an error

also

g = f1 | f2
g & f1 #causes error

And this is important to check if the flag is present or not. I believe the zero flag should be permitted :slight_smile:

If you explicitly permit a zero-valued item, then it works without type conversions:

julia> using BitFlags

julia> @bitflag flag begin
       f1
       f2
       f3
       fnone = 0
       end

julia> f1 & f2
fnone::flag = 0x00000000

(I don’t see an error in the second case you gave — did you mean to do g & f3?

This behavior was specifically chosen because you may want to enforce at least one flag bit being set for any given realization/instantiation of the bit falgs. You can also cast to regular integers to compare to zero:

julia> Int(g) & Int(f3)
0

I debated with myself on exactly how to handle this case, and I figured allowing explicitly-zero flags in the definition was a reasonable solution.

Great! That indeed solves the problem. Thank you very much.