Thursday, May 28, 2015

Tagging pointers in Rust

I've been learning some Rust after hearing about their 1.0 release. I'm very impressed by the compiler, the documentation, and the language itself.

The first thing I wrote in Rust was a routine to tag pointers. (If you haven't heard of pointer tagging, it's just a way of storing information within a pointer. Pointers tend to be at least 32-bit aligned, leaving the two least-significant bits of the pointer free for tags.) It works by using Rust's unsafe-cast mechanism (std::mem::transmute) to do a little bit flipping:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn tag_pointer<T>(p: &T, bit: u8) -> &T {
    unsafe {
        let pod: usize = mem::transmute(p);
        let pod = pod | (1 << bit);
        mem::transmute(pod)
    }
}

fn untag_pointer<T>(p: &T, bit: u8) -> &T {
    unsafe {
        let pod: usize = mem::transmute(p);
        let pod = pod & (!(1 << bit) as usize);
        mem::transmute(pod)
    }
}

fn check_pointer_tag<T>(p: &T, bit: u8) -> u8 {
    unsafe {
        let pod: usize = mem::transmute(p);
        ((pod >> bit) & 1) as u8
    }
}

To check that this works, we can do:

1
2
3
4
5
let p = &0; // Take the address of an integer.
let pn = tag_pointer(p, 0);
assert_eq!(check_pointer_tag(p, 0), 0);
assert_eq!(check_pointer_tag(pn, 0), 1);
assert_eq!(p, untag_pointer(pn, 0));

N.b: it isn't safe to dereference a tagged pointer :-). Here is a safe, alternate version which implements Deref and DerefMut.

This code isn't particularly tricky or interesting, but maybe it'll be useful to someone.

No comments:

Post a Comment