33. Bitwise Operators

I have to admit that I haven’t seen many instances of bitwise manipulation since my university assignments. That’s not to say they’re not used or not important - I’ve just not done a lot of programming that’s called on bitwise operators.

The bitwise operators
Operator Name
& Bitwise AND
| Bitwise OR
^ Bitwise XOR1
~ Bitwise negation (Not)
>> Right shift
>>> Right shift unsigned
<< Left shift

Truth Tables

Truth tables describe the results of various logical operations. The truth tables below illustrate the NOT, AND, OR and XOR logic.

Not (~) Result
Not 0 0
Not 1 1
And (&) Result
0 AND 0 0
0 AND 1 0
1 AND 0 0
1 AND 1 1
Or (|) Result
0 OR 0 0
0 OR 1 1
1 OR 0 1
1 OR 1 1
Xor (^) Result
0 XOR 0 0
0 XOR 1 1
1 XOR 0 1
1 XOR 1 0

Flag example

The unix file permission scheme uses binary flags for read, write and execute permissions on files. You see them when you run ls -l as something like -rwxr-xr-x. Essentially, this is a set of flags where binary 1 turns on that permission and binary 0 turns it off. The three elements I’ll look at here are read (r), write (w) and execute (x):

  • READ has the binary value 001 (decimal 1)
  • WRITE has the binary value 010 (decimal 2)
  • EXECUTE has the binary value 100 (decimal 4)

Let’s look at the example code first and then I’ll discuss it:

A bitwise example
//Create global variables for the permissions
READ = 0b100
WRITE = 0b010
EXECUTE = 0b001

println 'Checking for READ:'
checkFile(READ)

println 'Checking for WRITE:'
checkFile(WRITE)

println 'Checking for READ or EXECUTE:'
checkFile(READ | EXECUTE)

def checkFile (check) {
    def fileList = [:]
    for (i in 0..7) {
        fileList["File $i"] = i
    }

    for (file in fileList) {
        if (file.value & check) {
            println "$file.key (${displayFilePermission(file.value)}) meets crit\
eria"
        }
    }
}

def displayFilePermission(val) {
    def retval = ""
    retval <<= (READ & val)? 'r': '-'
    retval <<= (WRITE & val)? 'w': '-'
    retval <<= (EXECUTE & val)? 'x': '-'
    return retval
}

First up I set the flags for each of the three elements using the 0b prefix to indicate binary numbers:

READ = 0b100
WRITE = 0b010
EXECUTE = 0b001

I then call my checkFile method to see which permissions match what I’m seeking. The third call to checkFile is the more interesting as I OR two flags: READ | EXECUTE. If I OR the READ flag (100) with the EXECUTE flag (001) I get 101 (decimal 5):

Value Binary     Operator
READ 1 0 0 OR
EXECUTE 0 0 1  
Result 1 0 1 =

The checkFile method does the checking for me. The first part of the method just creates a set of possible files - enough to cover the various variations of the rwx elements:

def fileList = [:]
for (i in 0..7) {
    fileList["File $i"] = i
}

It’s the second half of checkFile that does the important stuff:

for (file in fileList) {
    if (file.value & check) {
        println "$file.key (${displayFilePermission(file.value)}) meets criteria"
    }
}

The if (file.value & check) performs an AND on the check requested (e.g. READ) and the file’s permissions. If the AND returns a result greater than 0 then the file’s permission match the check. For example, a file with execute permission (--x) meets the READ | EXECUTE criteria:

Item Value Binary     Operator
check READ | EXECUTE 1 0 1 AND
file --x 0 0 1  
  Result 0 0 1 =

A file with read and write permission (rw-) also matches:

Item Value Binary     Operator
check READ | EXECUTE 1 0 1 AND
file rw- 1 1 0  
  Result 1 0 0 =

However, a file with only the write permission (-w-) will not successfully match:

Item Value Binary     Operator
check READ | EXECUTE 1 0 1 AND
file -w- 0 1 0  
  Result 0 0 0 =

Lastly, the displayFilePermission method just helps me display the permissions in the rwx format.

Some other quick points follow:

I can negate (~) a value to indicate that I want the inverse of a value, rather than ORing the other options individually:

println 'Checking for WRITE or EXECUTE:'
checkFile(~READ)

I can XOR (^) to aggregate the permissions but ignore intersections (where both variables contain the same flag):

def file1 = READ | EXECUTE
def file2 = WRITE | EXECUTE
println 'File 1: ' << displayFilePermission(file1)
println 'File 2: ' << displayFilePermission(file2)
println 'Result: ' << displayFilePermission(file1 ^ file2)

Shift Example

Shifting just moves the bits in a binary to the left (<<) or to the right (>>), depending on the left-hand operand. If we take the following list as a starting point we can see how progressive shifts left change a value:

  • 0001 (binary) = 1 (decimal)
  • 0010 (binary) = 2 (decimal)
  • 0100 (binary) = 4 (decimal)
  • 1000 (binary) = 8 (decimal)

With this is mind the following code demonstrates the left- and right-shift operators:

assert 2 << 1 == 4	//Left-shift once
assert 2 << 2 == 8	//Left-shift twice
assert 2 >> 1 == 1	//Right-shift once

The code below displays a table in which each row represents a value that’s be left-shifted by one position more than the prior row:

A bit shifting example
def value = 0b0000_0000_0000_0000_1111_1111

println '| Shift   | Hex      | Decimal  | Octal    | Binary                   |'
println '|---------|----------|----------|----------|--------------------------|'
(0..16).each {
    def shifted = value << it
    def hexDisplay = '0x' << Integer.toHexString(shifted).padLeft(6, '0')
    def binDisplay = Integer.toBinaryString(shifted).padLeft(24, '0')
    def decDisplay = "$shifted".padLeft(8, ' ')
    def octDisplay = Integer.toOctalString(shifted).padLeft(8, ' ')
    def shiftDisplay = "$it".padLeft(7, ' ')
    println "| $shiftDisplay | $hexDisplay | $decDisplay | $octDisplay | $binDis\
play |"
}
println '|---------|----------|----------|----------|--------------------------|'
  1. Known as an Exclusive OR