Lab 2. Bitwise Operations

Due: Sunday, March 2 at 23:59

Your task is to write a Rust program that performs bit operations on integers.

Preliminaries

Click on the assignment link.

Once you have accepted the assignment, you can clone the repository on your computer by following the instruction and begin working.

This lab is in Rust. You’ll either need to have the Rust toolchain installed on your computer (I recommend using rustup) or use the lab machines which have Rust installed.

Be sure to ask any questions on Ed.

Program specification

In the assignment repository you will find a Rust project named bitops. This project contains source code for a library src/lib.rs which you will be editing and a binary src/main.rs which you will not be editing.

Your task is to implement the functions in src/lib.rs. You are only allowed to use the Rust bit operators, & (and), | (or), ^ (xor), >> (right shift), and << (left shift). You will not receive credit for the lab if you use addition, subtraction, division, multiplication, modulo/remainder, or control flow constructs like if and loops.

You must perform bit operations directly on the integer passed to each function, and you are not allowed to convert the integer to another format. In particular, you may not convert the integer to a String. For any method that asks you to change specific bits, you should assume bits are numbered 31 to 0, with 31 being the most significant bit or highest bit, and 0 being the least significant bit or lowest bit. All of these methods can (and should) be implemented with a single line of code.

Example

For example, say I asked you to write a function named set_bit_one that sets bit one of an integer to one while leaving the rest of the bits unchanged. My code for that method would look like this:

#![allow(unused)]
fn main() {
fn set_bit_one(x: u32) -> u32 {
    x | 2
}
}

Make sure you understand why this is ORing x with 2.

It may be helpful to know that you can specify the value of an integer in hexadecimal by prepending it with 0x, e.g., let x = 0xBADBEEF;. You can also specify the value of an integer in octal by prepending it with 0o or in binary by prepending it with 0b.

Info

Fun fact: Many programming languages—such as C and Java—use a leading 0 to indicate that a number is in octal. For example, in C or Java 0755 is how the integer 493 would be written in octal. In Rust, we would write 0o755.

The bitops binary whose source code is in src/main.rs takes input in the form of command line arguments. Each command line argument is parsed as an integer and passed to each of the functions you have to implement and the results are printed to stdout. The inputs can be specified in decimal, octal, hexadecimal, binary, or as an IPv4 “dotted quad” address like 132.162.201.24. See the examples below.

You will need to implement the following functions.

  • fn is_odd(x: i32) -> i32: This method returns 1 if x is odd, and 0 if it is even. It returns 0 on 6, and 1 on 5.

  • fn is_negative(x: i32) -> i32: This method returns 0 if x is positive, and 1 if x is negative. It returns 0 on 4, and 1 on -3.

  • fn div_by_4(x: u32) -> u32: This methods performs unsigned integer division by 4. It returns 1 on 6, 3 on 13, and 0 on 3.

  • fn nearest_odd(x: i32) -> i32: This method rounds up the number to the nearest odd number. It returns 7 on 6, 5 on 5, and -3 on -4.

  • fn flip_parity(x: i32) -> i32: This method adds 1 to even numbers, and subtracts one from odd numbers. It returns 7 on 6, 4 on 5, and -4 on -3.

  • fn routing_prefix(addr: u32) -> u32: IPv4 network addresses are stored as 32 bit integers. Each address is divided into two parts: the routing address and the host address. The routing address specifies the network the computer is on (for example, Oberlin’s network). The host address identifies a specific computer on that network. A subnet mask is applied to an IPv4 address to zero out bits corresponding to the host address, leaving just the routing prefix.

    Your task is to write a function that returns the routing addresses for a network with 256 host addresses: these routing addresses will consist of the 24 highest order bits of the address, with the last 8 bits of the address set to 0. If you code this function correctly, the IP form of your result should look like x.x.x.0.

  • fn make_readable(perms: u32) -> u32: The chmod, or change mode, command in Unix is used to change access permissions. It accepts user permission flags as a 3 digit octal number, where the first digit corresponds to the User permissions, the second digit corresponds to the Group permissions, and the third digit corresponds to the Other permissions. Each octal digit is three bits. For chmod, we set the most significant bit of the digit to 1 to give read permission, the middle bit to 1 to give write permission, and the least significant bit to 1 to give execute permission. For this function, take in a number that represents a set of permissions, and set all the read permission bits to 1. All other permission bits should remain unchanged. Good values for testing are 0, 64 (0o100 in octal, i.e. execute for user and no permissions for anyone else), and 146 (0o222 in octal, or only write permissions set for everyone).

Testing

For each function that you implement, write a unit test containing at least 3 assertions. Your tests should appear in the test module in src/lib.rs. A test function for is_odd is provided for you to use as a template for other tests. You don’t need to add additional tests for is_odd, but you may if you wish.

To run the tests, you can run

$ cargo test --lib

in the terminal.

Examples

Here are some sample outputs when running bitops with different arguments.

$ cargo run --quiet -- 0o640
is_odd(416) = 0
is_negative(416) = 0
div_by_4(416) = 104
nearest_odd(416) = 417
flip_parity(416) = 417
routing_address(416 /* 0.0.1.160 */) = 256 /* 0.0.1.0 */
make_readable(0o640) = 0o644

$ cargo run --quiet -- -87  
is_odd(-87) = 1
is_negative(-87) = 1
div_by_4(4294967209) = 1073741802
nearest_odd(-87) = -87
flip_parity(-87) = -88
routing_address(4294967209 /* 255.255.255.169 */) = 4294967040 /* 255.255.255.0 */
make_readable(0o37777777651) = 0o37777777655

$ cargo run --quiet -- 132.162.201.24
is_odd(-2069706472) = 0
is_negative(-2069706472) = 1
div_by_4(2225260824) = 556315206
nearest_odd(-2069706472) = -2069706471
flip_parity(-2069706472) = -2069706471
routing_address(2225260824 /* 132.162.201.24 */) = 2225260800 /* 132.162.201.0 */
make_readable(0o20450544430) = 0o20450544474

Hint

These examples make good test cases but don’t necessarily cover every case you may wish to test.

Submission

Submit the lab by committing your code and pushing it to your GitHub repository.