Skip to content

Instantly share code, notes, and snippets.

@nwellnhof
Created June 7, 2015 17:50
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 nwellnhof/299dd7315b8b54ac4e3d to your computer and use it in GitHub Desktop.
Save nwellnhof/299dd7315b8b54ac4e3d to your computer and use it in GitHub Desktop.
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;
}
// Obj
pub struct Obj {
ptr: *mut ObjInternal,
}
#[allow(non_snake_case)]
pub trait ObjTrait {
fn PTR(&self) -> *mut ObjInternal;
}
#[allow(non_snake_case)]
pub trait ObjCore {
fn CLASS() -> Class;
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self;
}
pub trait ObjCopy {
fn copy(&self) -> Self;
}
impl<T:ObjTrait + ObjCore> ObjCopy for T {
fn copy(&self) -> T {
T::NEW_FROM_PTR(self.PTR())
}
}
impl ObjTrait for Obj {
fn PTR(&self) -> *mut ObjInternal { self.ptr }
}
impl ObjCore 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 }
}
}
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 }
}
}
pub fn cfish_class<T: ObjCore>() -> Class { T::CLASS() }
pub fn is_a<T: ObjCore>(obj: &ObjTrait) -> bool {
unsafe { return cfish_Obj_is_a(obj.PTR(), T::CLASS().PTR()) != 0; }
}
pub fn cast<T: ObjCore>(obj: &ObjTrait) -> Option<T> {
if is_a::<T>(obj) {
return Some(T::NEW_FROM_PTR(obj.PTR()));
}
else {
return None;
}
}
// Class (no need for refcount handling)
pub struct Class {
ptr: *mut ObjInternal,
}
impl ObjTrait for Class {
fn PTR(&self) -> *mut ObjInternal { self.ptr }
}
impl ObjCore for Class {
fn CLASS() -> Class { Class { ptr: CFISH_CLASS } }
fn NEW_FROM_PTR(ptr: *mut ObjInternal) -> Self { Class { ptr: ptr } }
}
// String
pub struct String {
ptr: *mut ObjInternal,
}
impl ObjTrait for String {
fn PTR(&self) -> *mut ObjInternal { self.ptr }
}
impl ObjCore 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 }
}
}
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 }
}
fn clone_from(&mut self, source: &String) {
unsafe { cfish_inc_refcount(self.ptr); }
self.ptr = source.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(); }
// Create a new String (Clownfish String, not Rust String).
let string: String = String::new("Hello, world!");
{
// Create String trait object.
let string_trait: &StringTrait = &string;
// Create Obj trait object.
let obj_trait: &ObjTrait = &string;
// Unfortunately, Rust doesn't allow to upcast trait objects.
//let new_obj_trait: &ObjTrait = string_trait;
// Obj trait knows it's a String.
assert!(is_a::<String>(obj_trait));
// String is not a Class.
assert!(!is_a::<Class>(&string));
// Method call works.
assert_eq!(string.get_size(), 13);
}
// We can't add the Copy trait to Clownfish objects, because we must
// call incref when creating a copy. Unfortunately, Rust doesn't allow
// to override the behavior of the Copy trait. This means that
// Clownfish object structs have "move" semantics.
// This invalidates the original string. This only works because the
// references we created earlier are out of scope.
let string_copy = string;
// This would fail now, for example.
//let string_ref: &String = &string;
// Custom "copy" method that creates a new String struct pointing
// to the original string, leaving the original struct intact.
let another_copy: String = string_copy.copy();
// Upcast String to Object. It's easier to cast to an ObjTrait
// implicitly.
let obj: Obj = cast::<Obj>(&string_copy).expect("Invalid cast");
assert_eq!(obj.PTR(), string_copy.PTR());
// Downcast Obj back to String.
let cast_string: String = cast::<String>(&obj).expect("Invalid cast");
// Invalid cast of String to Class.
let option: Option<Class> = cast::<Class>(&string_copy);
assert!(option.is_none());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment