Search code examples
gobitwise-operatorsboolean-logicbitmask

Bitmasking and Bitwise Operations in Golang


I'm a beginner to programming in general so i'm sorry if i make some mistakes while putting this question up.

The tutorial I'm following goes over this code:

package main

import (
    "fmt"
)

const (
    isAdmin = 1 << iota
    isHeadquarters
    canSeeFinancials
    
    canSeeAfrica
    canSeeAsia
    canSeeEurope
    canSeeNorthAmerica
    canSeeSouthAmerica
)

func main() {
    var roles byte = isAdmin | canSeeFinancials | canSeeEurope
    fmt.Printf ("%b\n", roles)
    fmt.Printf ("Is Admin? %v\n", isAdmin & roles == isAdmin)
}

The guy in tutorial quickly mentions how this part is called Bitmasking.

fmt.Printf ("Is Admin? %v\n", isAdmin & roles == isAdmin)

Now, as far as i understand, the process that takes place here goes something like this: The computer is being asked if both isAdmin and roles are equal to isAdmin and replies true.

But, when i'm trying to do this:

fmt.Printf ("Is Admin? %v\n", roles == isAdmin)

It results in false.

Can somebody go in greater detail on the whole logic behind this process? This bit leaves me a bit confused and i want to know why that happens. Thank you.


Solution

  • All your role constants are special numbers where the binary (2's complement) representation contains exactly a single 1 bit, all other bits are zeros, and they are all different (the 1 bit is in a different position in each). This is achieved by shifting the 1 number to the left with increasing values (iota).

    The role variable's value is constructed by using bitwise OR:

    var roles byte = isAdmin | canSeeFinancials | canSeeEurope
    

    Bitwise OR keeps 1 bits, and in the result there will only be 0 bits where each operand contains 0 in that position. Since all values being OR'ed contain a single 1 bit in different positions, role will contain as many bits as many different roles OR'ed, and at their special positions.

    To easily understand the bits, let's print the binary representations:

    fmt.Printf("isAdmin          %08b\n", isAdmin)
    fmt.Printf("canSeeFinancials %08b\n", canSeeFinancials)
    fmt.Printf("canSeeEurope     %08b\n", canSeeEurope)
    fmt.Printf("-------------------------\n")
    fmt.Printf("roles            %08b\n", roles)
    

    This will output:

    isAdmin          00000001
    canSeeFinancials 00000100
    canSeeEurope     00100000
    -------------------------
    roles            00100101
    

    As you can see, roles contains 1s where any of the above bit patterns has 1.

    When you use bitwise AND (masking), the result bit will be 0 if any of the input bit is 0 at a given position, and will be 1 only if both bits are 1s.

    The expression:

    isAdmin & roles
    

    Since isAdmin contains a single 1 bit, the above masking will be a number that may also contain a single 1 bit at most, only if roles has a 1 bit at that position. Effectively this masking tells if roles contains the isAdmin bit. If it contains it, the result will be a value equal to isAdmin. If not, the result will be a number consisting all 0 bits, that is: decimal 0.

    Visualizing the bits again:

    fmt.Printf("roles            %08b\n", roles)
    fmt.Printf("isAdmin          %08b\n", isAdmin)
    fmt.Printf("-------------------------\n")
    fmt.Printf("isAdmin & roles  %08b\n", isAdmin&roles)
    

    Output:

    roles            00100101
    isAdmin          00000001
    -------------------------
    isAdmin & roles  00000001
    

    Try the examples on the Go Playground.

    So the expression:

    isAdmin & roles == isAdmin
    

    Will be true if roles contains (includes) the isAdmin role, false otherwise.

    Without masking:

    roles == isAdmin
    

    This will be true if roles equals to isAdmin, that is, if it only contains the isAdmin role and nothing else. If it contains other roles, it obviously won't be equal to isAdmin.