Skip to content

Instantly share code, notes, and snippets.

@jacquesf
Last active August 29, 2015 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jacquesf/d448721e3d7f485ffbf1 to your computer and use it in GitHub Desktop.
Save jacquesf/d448721e3d7f485ffbf1 to your computer and use it in GitHub Desktop.
use std::mem;
// Example of a module that loads a tagged union from a raw byte slice.
// The tagged union consists of a 32-bit type field, followed by fields whose types and count
// depend on the tag
//
// The goal is to take a byte slice and return a ref to the appropriate struct depending on
// the tag, or an error in case of overflow of the buffer or unknown tag
//
// I know this doesn't handle endianness. My full implementation does, but I've removed it to
// simplify this example.
//
// Alignment is not a concern. The file format defines all struct members to be naturally
// aligned (any members that aren't naturally aligned in this example are errors in the example).
#[derive(Debug)]
enum TaggedUnion<'a> {
Thing1(&'a TaggedUnionThing1),
Thing2(&'a TaggedUnionThing2),
Thing3(&'a TaggedUnionThing3),
}
#[derive(Debug)]
enum MyLibError {
RangeError,
InvalidTag
}
type MyLibResult<T> = Result<T, MyLibError>;
impl<'a> TaggedUnion<'a> {
pub fn from_byte_slice_offset(buf: &'a [u8], offset: usize) -> MyLibResult<TaggedUnion<'a>> {
let rec_type: &u32 = try!(convert_byte_slice_offset(buf, offset).ok_or(MyLibError::RangeError));
match *rec_type {
TAGGED_UNION_THING1 => {
Ok(TaggedUnion::Thing1(
try!(convert_byte_slice_offset(buf, offset).ok_or(MyLibError::RangeError))))
},
TAGGED_UNION_THING2 => {
Ok(TaggedUnion::Thing2(
try!(convert_byte_slice_offset(buf, offset).ok_or(MyLibError::RangeError))))
},
TAGGED_UNION_THING3 => {
Ok(TaggedUnion::Thing3(
try!(convert_byte_slice_offset(buf, offset).ok_or(MyLibError::RangeError))))
},
_ => Err(MyLibError::InvalidTag)
}
}
}
fn convert_byte_slice_offset<'a, T>(buf: &'a [u8], offset: usize) -> Option<&'a T> {
let thing_size = mem::size_of::<T>();
let buf_len = buf.len();
if offset + thing_size < offset {
None
} else if offset + thing_size > buf_len {
None
} else {
Some(unsafe {
let addr = (&buf[offset] as *const u8) as *const T;
mem::transmute(addr)
})
}
}
fn main() {
let test = [];
let result = TaggedUnion::from_byte_slice_offset(test.as_slice(), 0);
println!("{:?}", result);
let test2 = [1u8, 0, 0, 0, 55, 0, 0, 0];
let result2 = TaggedUnion::from_byte_slice_offset(test2.as_slice(), 0);
println!("{:?}", result2);
}
// This stuff is just to motivate the example... the actual fields aren't terribly important.
const TAGGED_UNION_THING1: u32 = 1;
const TAGGED_UNION_THING2: u32 = 2;
const TAGGED_UNION_THING3: u32 = 3;
#[derive(Debug)]
#[repr(C)]
struct TaggedUnionThing1 {
tag: u32,
field1: u32,
}
#[derive(Debug)]
#[repr(C)]
struct TaggedUnionThing2 {
tag: u32,
other_field1: u32,
other_field2: u32,
other_field3: u32,
other_field4: u32,
other_field5: u32,
other_field6: u32,
}
#[derive(Debug)]
#[repr(C)]
struct TaggedUnionThing3 {
tag: u32,
differernt_field1: u32,
differernt_field2: u32,
differernt_field3: u32,
differernt_field4: u16,
differernt_field5: u16,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment