Created
June 6, 2015 11:42
-
-
Save nwellnhof/0c83c64a8f15c253e73e to your computer and use it in GitHub Desktop.
Proof of concept for Clownfish Rust bindings
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
extern crate libc; | |
use libc::types::common::c99::*; | |
use libc::types::os::arch::c95::*; | |
#[repr(C)] | |
pub struct ObjInternal { | |
refcount: size_t, | |
class: *mut ObjInternal, | |
} | |
#[link(name = "cfish")] | |
extern { | |
static CFISH_OBJ: *mut ObjInternal; | |
static CFISH_CLASS: *mut ObjInternal; | |
static CFISH_STRING: *mut ObjInternal; | |
fn cfish_bootstrap_parcel(); | |
fn cfish_inc_refcount(obj: *mut ObjInternal); | |
fn cfish_dec_refcount(obj: *mut ObjInternal) -> uint32_t; | |
// The libc crate doesn't support the C99 _Bool type. | |
// See https://github.com/rust-lang/rust/issues/14608 | |
fn cfish_Obj_is_a(obj: *mut ObjInternal, class: *mut ObjInternal) | |
-> c_char; | |
fn cfish_Str_new_from_trusted_utf8(utf8: *const u8, size: size_t) | |
-> *mut ObjInternal; | |
static CFISH_Str_Get_Size_OFFSET: size_t; | |
} | |
//fn cfish_class<T: ObjTrait>() -> Class { T::CLASS() } | |
// Obj | |
pub struct Obj { | |
ptr: *mut ObjInternal, | |
} | |
#[allow(non_snake_case)] | |
pub trait ObjTrait { | |
fn CLASS() -> Class; | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self; | |
fn PTR(&self) -> *mut ObjInternal; | |
fn is_a<T: ObjTrait>(&self) -> bool { | |
unsafe { return cfish_Obj_is_a(self.PTR(), T::CLASS().ptr) != 0; } | |
} | |
fn CAST<T: ObjTrait>(&self) -> Option<T> { | |
if self.is_a::<T>() { | |
return Some(T::NEW_FROM_PTR(self.PTR())); | |
} | |
else { | |
return None; | |
} | |
} | |
} | |
impl ObjTrait for Obj { | |
fn CLASS() -> Class { Class { ptr: CFISH_OBJ } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { | |
unsafe { cfish_inc_refcount(ptr); } | |
Obj { ptr: ptr } | |
} | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
impl Drop for Obj { | |
fn drop(&mut self) { | |
unsafe { cfish_dec_refcount(self.ptr); } | |
} | |
} | |
impl Clone for Obj { | |
fn clone(&self) -> Obj { | |
// Should invoke Obj_Clone. | |
unsafe { cfish_inc_refcount(self.ptr); } | |
Obj { ptr: self.ptr } | |
} | |
} | |
// Class (no need for refcount handling) | |
pub struct Class { | |
ptr: *mut ObjInternal, | |
} | |
impl ObjTrait for Class { | |
fn CLASS() -> Class { Class { ptr: CFISH_CLASS } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { Class { ptr: ptr } } | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
// String | |
pub struct String { | |
ptr: *mut ObjInternal, | |
} | |
impl ObjTrait for String { | |
fn CLASS() -> Class { Class { ptr: CFISH_STRING } } | |
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { | |
unsafe { cfish_inc_refcount(ptr); } | |
String { ptr: ptr } | |
} | |
fn PTR(&self) -> *mut ObjInternal { self.ptr } | |
} | |
pub trait StringTrait : ObjTrait { | |
fn get_size(&self) -> usize { | |
let ptr = self.PTR(); | |
unsafe { | |
let class = (*ptr).class as *const c_char; | |
let method_ptr = class.offset(CFISH_Str_Get_Size_OFFSET as isize) | |
as *const extern fn (*mut ObjInternal) -> size_t; | |
return (*method_ptr)(ptr) as usize; | |
} | |
} | |
} | |
impl StringTrait for String {} | |
impl Drop for String { | |
fn drop(&mut self) { | |
unsafe { cfish_dec_refcount(self.ptr); } | |
} | |
} | |
impl Clone for String { | |
fn clone(&self) -> String { | |
unsafe { cfish_inc_refcount(self.ptr); } | |
String { ptr: self.ptr } | |
} | |
} | |
impl String { | |
fn new(s: &str) -> String { | |
unsafe { | |
return String { | |
ptr: cfish_Str_new_from_trusted_utf8(s.as_ptr(), | |
s.len() as size_t), | |
} | |
} | |
} | |
} | |
// main | |
fn main() { | |
unsafe { cfish_bootstrap_parcel(); } | |
let s1 = String::new("Hello, world!"); | |
let s2 = &s1; | |
assert!(s1.is_a::<String>()); | |
assert!(!s2.is_a::<Class>()); | |
assert_eq!(s1.get_size(), 13); | |
let obj = s1.CAST::<Obj>().expect("Invalid cast"); | |
assert_eq!(s1.ptr, obj.ptr); | |
let class = s1.CAST::<Class>(); | |
assert!(class.is_none()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment