Last active
August 29, 2015 14:15
-
-
Save jacquesf/d448721e3d7f485ffbf1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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