diff --git a/crates/lune-std-ffi/src/c/c_arr.rs b/crates/lune-std-ffi/src/c/c_arr.rs
index 6fbd964..2b2b94b 100644
--- a/crates/lune-std-ffi/src/c/c_arr.rs
+++ b/crates/lune-std-ffi/src/c/c_arr.rs
@@ -21,7 +21,7 @@ use crate::ffi::ffi_association::{get_association, set_association};
 // There is no problem even if you create a struct with n fields of a single type within the struct. Array adheres to the condition that there is no additional padding between each element. Padding to a struct is padding inside the struct. Simply think of the padding byte as a trailing unnamed field.
 
 pub struct CArr {
-    libffi_type: Type,
+    element_type: Type,
     struct_type: Type,
     length: usize,
     field_size: usize,
@@ -29,12 +29,12 @@ pub struct CArr {
 }
 
 impl CArr {
-    pub fn new(libffi_type: Type, length: usize) -> LuaResult<Self> {
-        let struct_type = Type::structure(vec![libffi_type.clone(); length]);
-        let field_size = get_ensured_size(libffi_type.as_raw_ptr())?;
+    pub fn new(element_type: Type, length: usize) -> LuaResult<Self> {
+        let struct_type = Type::structure(vec![element_type.clone(); length]);
+        let field_size = get_ensured_size(element_type.as_raw_ptr())?;
 
         Ok(Self {
-            libffi_type,
+            element_type,
             struct_type,
             length,
             field_size,
@@ -50,14 +50,18 @@ impl CArr {
         let fields = type_from_userdata(luatype)?;
         let carr = lua.create_userdata(Self::new(fields, length)?)?;
 
-        set_association(lua, CARR_INNER, carr.clone(), luatype)?;
+        set_association(lua, CARR_INNER, &carr, luatype)?;
         Ok(carr)
     }
 
     pub fn get_type(&self) -> Type {
-        self.libffi_type.clone()
+        self.struct_type.clone()
     }
 
+    // pub fn get_element_type(&self) -> Type {
+    //     self.element_type.clone()
+    // }
+
     // Stringify cstruct for pretty printing something like:
     // <CStruct( u8, i32, size = 8 )>
     pub fn stringify(userdata: &LuaAnyUserData) -> LuaResult<String> {
diff --git a/crates/lune-std-ffi/src/c/c_ptr.rs b/crates/lune-std-ffi/src/c/c_ptr.rs
index 5fb8845..00a730d 100644
--- a/crates/lune-std-ffi/src/c/c_ptr.rs
+++ b/crates/lune-std-ffi/src/c/c_ptr.rs
@@ -1,7 +1,5 @@
 #![allow(clippy::cargo_common_metadata)]
 
-use std::borrow::Borrow;
-
 use libffi::middle::Type;
 use mlua::prelude::*;
 
@@ -21,7 +19,7 @@ impl CPtr {
     ) -> LuaResult<LuaValue<'lua>> {
         let value = Self().into_lua(lua)?;
 
-        set_association(lua, CPTR_INNER, value.borrow(), inner)?;
+        set_association(lua, CPTR_INNER, &value, inner)?;
 
         Ok(value)
     }
diff --git a/crates/lune-std-ffi/src/c/c_struct.rs b/crates/lune-std-ffi/src/c/c_struct.rs
index 9e945ec..58628d5 100644
--- a/crates/lune-std-ffi/src/c/c_struct.rs
+++ b/crates/lune-std-ffi/src/c/c_struct.rs
@@ -1,6 +1,6 @@
 #![allow(clippy::cargo_common_metadata)]
 
-use std::vec::Vec;
+use std::{borrow::Borrow, vec::Vec};
 
 use libffi::{
     low,
@@ -18,17 +18,17 @@ use crate::ffi::ffi_association::{get_association, set_association};
 use crate::ffi::ffi_helper::FFI_STATUS_NAMES;
 
 pub struct CStruct {
-    libffi_cif: Cif,
-    libffi_type: Type,
+    // libffi_cif: Cif,
     fields: Vec<Type>,
+    libffi_type: Type,
     offsets: Vec<usize>,
     size: usize,
 }
 
 impl CStruct {
     pub fn new(fields: Vec<Type>) -> LuaResult<Self> {
-        let libffi_type = Type::structure(fields.clone());
-        let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
+        let libffi_type = Type::structure(fields.iter().cloned());
+        // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
 
         // Get field offsets with ffi_get_struct_offsets
         let mut offsets = Vec::<usize>::with_capacity(fields.len());
@@ -52,9 +52,9 @@ impl CStruct {
         let size = unsafe { (*libffi_type.as_raw_ptr()).size };
 
         Ok(Self {
-            libffi_cif: libffi_cfi,
-            libffi_type,
+            // libffi_cif: libffi_cfi,
             fields,
+            libffi_type,
             offsets,
             size,
         })
@@ -69,7 +69,7 @@ impl CStruct {
         let fields = type_list_from_table(&table)?;
         let cstruct = lua.create_userdata(Self::new(fields)?)?;
         table.set_readonly(true);
-        set_association(lua, CSTRUCT_INNER, cstruct.clone(), table)?;
+        set_association(lua, CSTRUCT_INNER, &cstruct, table)?;
         Ok(cstruct)
     }
 
diff --git a/crates/lune-std-ffi/src/c/c_type.rs b/crates/lune-std-ffi/src/c/c_type.rs
index 58d3364..7ed7bca 100644
--- a/crates/lune-std-ffi/src/c/c_type.rs
+++ b/crates/lune-std-ffi/src/c/c_type.rs
@@ -1,44 +1,38 @@
 #![allow(clippy::cargo_common_metadata)]
 
-use core::ffi::{
-    c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
-    c_ulong, c_ulonglong, c_ushort, c_void,
-};
-
-use libffi::middle::{Cif, Type};
+use libffi::middle::Type;
 use mlua::prelude::*;
 
 use super::c_arr::CArr;
 use super::c_helper::get_ensured_size;
 use super::c_ptr::CPtr;
 use crate::ffi::ffi_helper::get_ptr_from_userdata;
-use crate::ffi::ffi_platform::CHAR_IS_SIGNED;
-// use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
 
 pub struct CType {
-    libffi_cif: Cif,
+    // for ffi_ptrarray_to_raw?
+    // libffi_cif: Cif,
     libffi_type: Type,
     size: usize,
     name: Option<String>,
 
     // Write converted data from luavalue into some ptr
-    pub luavalue_into_ptr: fn(value: LuaValue, ptr: *mut c_void) -> LuaResult<()>,
+    pub luavalue_into_ptr: fn(value: LuaValue, ptr: *mut ()) -> LuaResult<()>,
 
     // Read luavalue from some ptr
-    pub ptr_into_luavalue: fn(lua: &Lua, ptr: *mut c_void) -> LuaResult<LuaValue>,
+    pub ptr_into_luavalue: fn(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue>,
 }
 
 impl CType {
     pub fn new(
         libffi_type: Type,
         name: Option<String>,
-        luavalue_into_ptr: fn(value: LuaValue, ptr: *mut c_void) -> LuaResult<()>,
-        ptr_into_luavalue: fn(lua: &Lua, ptr: *mut c_void) -> LuaResult<LuaValue>,
+        luavalue_into_ptr: fn(value: LuaValue, ptr: *mut ()) -> LuaResult<()>,
+        ptr_into_luavalue: fn(lua: &Lua, ptr: *mut ()) -> LuaResult<LuaValue>,
     ) -> LuaResult<Self> {
-        let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
+        // let libffi_cfi = Cif::new(vec![libffi_type.clone()], Type::void());
         let size = get_ensured_size(libffi_type.as_raw_ptr())?;
         Ok(Self {
-            libffi_cif: libffi_cfi,
+            // libffi_cif: libffi_cfi,
             libffi_type,
             size,
             name,
@@ -117,122 +111,3 @@ impl LuaUserData for CType {
         });
     }
 }
-
-// export all default c-types
-#[allow(clippy::too_many_lines)]
-pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
-    Ok(vec![
-        (
-            "int",
-            CType::new(
-                Type::c_int(),
-                Some(String::from("int")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_int;
-                    unsafe {
-                        *(ptr.cast::<c_int>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut c_void| {
-                    let value = unsafe { (*ptr.cast::<c_int>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "long",
-            CType::new(
-                Type::c_long(),
-                Some(String::from("long")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_long;
-                    unsafe {
-                        *(ptr.cast::<c_long>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut c_void| {
-                    let value = unsafe { (*ptr.cast::<c_long>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "longlong",
-            CType::new(
-                Type::c_longlong(),
-                Some(String::from("longlong")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_longlong;
-                    unsafe {
-                        *(ptr.cast::<c_longlong>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut c_void| {
-                    let value = unsafe { (*ptr.cast::<c_longlong>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-        (
-            "char",
-            CType::new(
-                if CHAR_IS_SIGNED {
-                    Type::c_schar()
-                } else {
-                    Type::c_uchar()
-                },
-                Some(String::from("char")),
-                |data, ptr| {
-                    let value = match data {
-                        LuaValue::Integer(t) => t,
-                        _ => {
-                            return Err(LuaError::external(format!(
-                                "Integer expected, got {}",
-                                data.type_name()
-                            )))
-                        }
-                    } as c_char;
-                    unsafe {
-                        *(ptr.cast::<c_char>()) = value;
-                    }
-                    Ok(())
-                },
-                |lua: &Lua, ptr: *mut c_void| {
-                    let value = unsafe { (*ptr.cast::<c_char>()).into_lua(lua)? };
-                    Ok(value)
-                },
-            )?
-            .into_lua(lua)?,
-        ),
-    ])
-}
diff --git a/crates/lune-std-ffi/src/c/mod.rs b/crates/lune-std-ffi/src/c/mod.rs
index 803fa62..25db176 100644
--- a/crates/lune-std-ffi/src/c/mod.rs
+++ b/crates/lune-std-ffi/src/c/mod.rs
@@ -12,3 +12,133 @@ mod association_names {
     pub const CARR_INNER: &str = "__carr_inner";
     pub const CSTRUCT_INNER: &str = "__cstruct_inner";
 }
+
+use core::ffi::{
+    c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
+    c_ulong, c_ulonglong, c_ushort, c_void,
+};
+
+use libffi::middle::Type;
+use mlua::prelude::*;
+
+use self::c_type::CType;
+use crate::ffi::ffi_platform::CHAR_IS_SIGNED;
+
+// export all default c-types
+#[allow(clippy::too_many_lines)]
+pub fn create_all_types(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
+    Ok(vec![
+        (
+            "int",
+            CType::new(
+                Type::c_int(),
+                Some(String::from("int")),
+                |data, ptr| {
+                    let value = match data {
+                        LuaValue::Integer(t) => t,
+                        _ => {
+                            return Err(LuaError::external(format!(
+                                "Integer expected, got {}",
+                                data.type_name()
+                            )))
+                        }
+                    } as c_int;
+                    unsafe {
+                        *(ptr.cast::<c_int>()) = value;
+                    }
+                    Ok(())
+                },
+                |lua: &Lua, ptr: *mut ()| {
+                    let value = unsafe { (*ptr.cast::<c_int>()).into_lua(lua)? };
+                    Ok(value)
+                },
+            )?
+            .into_lua(lua)?,
+        ),
+        (
+            "long",
+            CType::new(
+                Type::c_long(),
+                Some(String::from("long")),
+                |data, ptr| {
+                    let value = match data {
+                        LuaValue::Integer(t) => t,
+                        _ => {
+                            return Err(LuaError::external(format!(
+                                "Integer expected, got {}",
+                                data.type_name()
+                            )))
+                        }
+                    } as c_long;
+                    unsafe {
+                        *(ptr.cast::<c_long>()) = value;
+                    }
+                    Ok(())
+                },
+                |lua: &Lua, ptr: *mut ()| {
+                    let value = unsafe { (*ptr.cast::<c_long>()).into_lua(lua)? };
+                    Ok(value)
+                },
+            )?
+            .into_lua(lua)?,
+        ),
+        (
+            "longlong",
+            CType::new(
+                Type::c_longlong(),
+                Some(String::from("longlong")),
+                |data, ptr| {
+                    let value = match data {
+                        LuaValue::Integer(t) => t,
+                        _ => {
+                            return Err(LuaError::external(format!(
+                                "Integer expected, got {}",
+                                data.type_name()
+                            )))
+                        }
+                    } as c_longlong;
+                    unsafe {
+                        *(ptr.cast::<c_longlong>()) = value;
+                    }
+                    Ok(())
+                },
+                |lua: &Lua, ptr: *mut ()| {
+                    let value = unsafe { (*ptr.cast::<c_longlong>()).into_lua(lua)? };
+                    Ok(value)
+                },
+            )?
+            .into_lua(lua)?,
+        ),
+        (
+            "char",
+            CType::new(
+                if CHAR_IS_SIGNED {
+                    Type::c_schar()
+                } else {
+                    Type::c_uchar()
+                },
+                Some(String::from("char")),
+                |data, ptr| {
+                    let value = match data {
+                        LuaValue::Integer(t) => t,
+                        _ => {
+                            return Err(LuaError::external(format!(
+                                "Integer expected, got {}",
+                                data.type_name()
+                            )))
+                        }
+                    } as c_char;
+                    unsafe {
+                        *(ptr.cast::<c_char>()) = value;
+                    }
+                    Ok(())
+                },
+                |lua: &Lua, ptr: *mut ()| {
+                    let value = unsafe { (*ptr.cast::<c_char>()).into_lua(lua)? };
+                    Ok(value)
+                },
+            )?
+            .into_lua(lua)?,
+        ),
+    ])
+}
diff --git a/crates/lune-std-ffi/src/ffi/ffi_bounds.rs b/crates/lune-std-ffi/src/ffi/ffi_bounds.rs
new file mode 100644
index 0000000..5c73bb3
--- /dev/null
+++ b/crates/lune-std-ffi/src/ffi/ffi_bounds.rs
@@ -0,0 +1,67 @@
+// Memory range for ref or box data. For boundary checking
+pub struct FfiRefBounds {
+    // Indicates how much data is above the pointer
+    pub(crate) high: usize,
+    // Indicates how much data is below the pointer
+    pub(crate) low: usize,
+}
+
+impl FfiRefBounds {
+    pub fn new(high: usize, low: usize) -> Self {
+        Self { high, low }
+    }
+
+    // Check boundary
+    pub fn check(&self, offset: isize) -> bool {
+        let sign = offset.signum();
+        let offset_abs = offset.unsigned_abs();
+        if sign == -1 {
+            self.high >= offset_abs
+        } else if sign == 1 {
+            self.low >= offset_abs
+        } else {
+            // sign == 0
+            true
+        }
+    }
+
+    // Check boundary
+    pub fn check_sized(&self, offset: isize, size: usize) -> bool {
+        let end = offset + (size as isize) - 1;
+        let sign = end.signum();
+        let end_abs = end.unsigned_abs();
+        if sign == -1 {
+            self.high >= end_abs
+        } else if sign == 1 {
+            self.low >= end_abs
+        } else {
+            // sign == 0
+            true
+        }
+    }
+
+    // Calculate new bounds from bounds and offset
+    // No boundary checking in here
+    pub fn offset(&self, offset: isize) -> Self {
+        let sign = offset.signum();
+        let offset_abs = offset.unsigned_abs();
+
+        let high: usize = if sign == -1 {
+            self.high - offset_abs
+        } else if sign == 1 {
+            self.high + offset_abs
+        } else {
+            self.high
+        };
+
+        let low: usize = if sign == -1 {
+            self.low + offset_abs
+        } else if sign == 1 {
+            self.low - offset_abs
+        } else {
+            self.low
+        };
+
+        Self { high, low }
+    }
+}
diff --git a/crates/lune-std-ffi/src/ffi/ffi_box.rs b/crates/lune-std-ffi/src/ffi/ffi_box.rs
index 49fb1d2..f7d593c 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_box.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_box.rs
@@ -11,12 +11,11 @@
 
 use std::boxed::Box;
 
-use core::ffi::c_void;
 use mlua::prelude::*;
 
-use super::association_names::BOX_REF_INNER;
+use super::association_names::REF_INNER;
 use super::ffi_association::set_association;
-use super::ffi_ref::FfiRange;
+use super::ffi_bounds::FfiRefBounds;
 use super::ffi_ref::FfiRef;
 
 pub struct FfiBox(Box<[u8]>);
@@ -36,29 +35,10 @@ impl FfiBox {
         Self(vec_heap.into_boxed_slice())
     }
 
-    pub fn size(&self) -> usize {
-        self.0.len()
-    }
-
     // pub fn copy(&self, target: &mut FfiBox) {}
 
-    pub fn get_ptr(&self) -> *mut c_void {
-        self.0.as_ptr() as *mut c_void
-    }
-
+    // Todo: if too big, print as another format
     pub fn stringify(&self) -> String {
-        let mut buff = String::from(" ");
-        for i in &self.0 {
-            buff.push_str(i.to_string().as_str());
-            buff.push_str(", ");
-        }
-        buff.pop();
-        buff.pop();
-        buff.push(' ');
-        buff
-    }
-
-    pub fn binary_print(&self) -> String {
         let mut buff: String = String::with_capacity(self.size() * 10 - 2);
         for (pos, value) in self.0.iter().enumerate() {
             for i in 0..8 {
@@ -75,42 +55,51 @@ impl FfiBox {
         buff
     }
 
-    // bad naming. i have no idea what should i use
+    // Make FfiRef from box, with boundary checking
     pub fn luaref<'lua>(
         lua: &'lua Lua,
         this: LuaAnyUserData<'lua>,
         offset: Option<isize>,
     ) -> LuaResult<LuaAnyUserData<'lua>> {
-        let target = this.borrow::<FfiBox>()?;
-        let ptr = if let Some(t) = offset {
-            if t < 0 || t >= (target.size() as isize) {
+        let mut target = this.borrow_mut::<FfiBox>()?;
+        let mut bounds = FfiRefBounds::new(0, target.size());
+        let mut ptr = target.get_ptr();
+
+        // Calculate offset
+        if let Some(t) = offset {
+            if !bounds.check(t) {
                 return Err(LuaError::external(format!(
                     "Offset is out of bounds. box.size: {}. offset got {}",
                     target.size(),
                     t
                 )));
             }
-            unsafe { target.get_ptr().offset(t) }
-        } else {
-            target.get_ptr()
-        };
+            ptr = unsafe { target.get_ptr().offset(t) };
+            bounds = bounds.offset(t);
+        }
 
-        let luaref = lua.create_userdata(FfiRef::new(
-            ptr,
-            Some(FfiRange {
-                low: 0,
-                high: target.size() as isize,
-            }),
-        ))?;
+        let luaref = lua.create_userdata(FfiRef::new(ptr.cast(), Some(bounds)))?;
 
-        set_association(lua, BOX_REF_INNER, luaref.clone(), this.clone())?;
+        // Makes box alive longer then ref
+        set_association(lua, REF_INNER, &luaref, &this)?;
 
         Ok(luaref)
     }
 
+    // Fill every field with 0
     pub fn zero(&mut self) {
         self.0.fill(0u8);
     }
+
+    // Get size of box
+    pub fn size(&self) -> usize {
+        self.0.len()
+    }
+
+    // Get raw ptr
+    pub fn get_ptr(&mut self) -> *mut u8 {
+        self.0.as_mut_ptr()
+    }
 }
 
 impl LuaUserData for FfiBox {
@@ -119,6 +108,7 @@ impl LuaUserData for FfiBox {
     }
 
     fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
+        // For convenience, :zero returns self.
         methods.add_function_mut("zero", |_, this: LuaAnyUserData| {
             this.borrow_mut::<FfiBox>()?.zero();
             Ok(this)
@@ -130,8 +120,6 @@ impl LuaUserData for FfiBox {
                 Ok(luaref)
             },
         );
-        methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
-            Ok(this.binary_print())
-        });
+        methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| Ok(this.stringify()));
     }
 }
diff --git a/crates/lune-std-ffi/src/ffi/ffi_helper.rs b/crates/lune-std-ffi/src/ffi/ffi_helper.rs
index ffa32f2..87a905e 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_helper.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_helper.rs
@@ -1,5 +1,3 @@
-use std::ffi::c_void;
-
 use mlua::prelude::*;
 
 use super::ffi_box::FfiBox;
@@ -13,12 +11,14 @@ pub const FFI_STATUS_NAMES: [&str; 4] = [
     "ffi_status_FFI_BAD_ARGTYPE",
 ];
 
+// Get raw pointer from userdata
+// TODO: boundary check
 pub unsafe fn get_ptr_from_userdata(
     userdata: &LuaAnyUserData,
     offset: Option<isize>,
-) -> LuaResult<*mut c_void> {
+) -> LuaResult<*mut ()> {
     let ptr = if userdata.is::<FfiBox>() {
-        userdata.borrow::<FfiBox>()?.get_ptr()
+        userdata.borrow_mut::<FfiBox>()?.get_ptr().cast()
     } else if userdata.is::<FfiRef>() {
         userdata.borrow::<FfiRef>()?.get_ptr()
     } else {
@@ -26,7 +26,7 @@ pub unsafe fn get_ptr_from_userdata(
     };
 
     let ptr = if let Some(t) = offset {
-        ptr.offset(t)
+        ptr.cast::<u8>().offset(t).cast()
     } else {
         ptr
     };
diff --git a/crates/lune-std-ffi/src/ffi/ffi_lib.rs b/crates/lune-std-ffi/src/ffi/ffi_lib.rs
index bf87015..70c81c6 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_lib.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_lib.rs
@@ -39,9 +39,9 @@ impl FfiLib {
                 .map_err(|err| LuaError::external(format!("{err}")))?
         };
 
-        let luasym = lua.create_userdata(FfiRef::new(*sym, None))?;
+        let luasym = lua.create_userdata(FfiRef::new((*sym).cast(), None))?;
 
-        set_association(lua, SYM_INNER, luasym.clone(), this.clone())?;
+        set_association(lua, SYM_INNER, &luasym, &this)?;
 
         Ok(luasym)
     }
diff --git a/crates/lune-std-ffi/src/ffi/ffi_raw.rs b/crates/lune-std-ffi/src/ffi/ffi_raw.rs
index 6a82cd9..c32ca76 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_raw.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_raw.rs
@@ -1,5 +1,5 @@
-use core::ffi::c_void;
-use std::{convert, mem::transmute, ptr};
+// use core::ffi::c_void;
+// use std::{convert, mem::transmute, ptr};
 
 // This is raw data coming from outside.
 // Users must convert it to a Lua value, reference, or box to use it.
diff --git a/crates/lune-std-ffi/src/ffi/ffi_ref.rs b/crates/lune-std-ffi/src/ffi/ffi_ref.rs
index a5ebc70..1150eb1 100644
--- a/crates/lune-std-ffi/src/ffi/ffi_ref.rs
+++ b/crates/lune-std-ffi/src/ffi/ffi_ref.rs
@@ -1,10 +1,10 @@
-use core::ffi::c_void;
 use std::ptr;
 
 use mlua::prelude::*;
 
 use super::association_names::REF_INNER;
-use super::ffi_association::set_association;
+use super::ffi_association::{get_association, set_association};
+use super::ffi_bounds::FfiRefBounds;
 
 // A referenced space. It is possible to read and write through types.
 // This operation is not safe. This may cause a memory error in Lua
@@ -12,22 +12,17 @@ use super::ffi_association::set_association;
 // If it references an area managed by Lua,
 // the box will remain as long as this reference is alive.
 
-pub struct FfiRange {
-    pub(crate) high: isize,
-    pub(crate) low: isize,
-}
-
 pub struct FfiRef {
-    ptr: *mut c_void,
-    range: Option<FfiRange>,
+    ptr: *mut (),
+    range: Option<FfiRefBounds>,
 }
 
 impl FfiRef {
-    pub fn new(ptr: *mut c_void, range: Option<FfiRange>) -> Self {
+    pub fn new(ptr: *mut (), range: Option<FfiRefBounds>) -> Self {
         Self { ptr, range }
     }
 
-    // bad naming. i have no idea what should i use
+    // Make FfiRef from ref
     pub fn luaref<'lua>(
         lua: &'lua Lua,
         this: LuaAnyUserData<'lua>,
@@ -35,52 +30,59 @@ impl FfiRef {
         let target = this.borrow::<FfiRef>()?;
 
         let luaref = lua.create_userdata(FfiRef::new(
-            ptr::from_ref(&target.ptr) as *mut c_void,
-            Some(FfiRange {
+            ptr::from_ref(&target.ptr) as *mut (),
+            Some(FfiRefBounds {
                 low: 0,
-                high: size_of::<usize>() as isize,
+                high: size_of::<usize>(),
             }),
         ))?;
 
-        set_association(lua, REF_INNER, luaref.clone(), this.clone())?;
+        // If the ref holds a box, make sure the new ref also holds the box
+        if let Some(t) = get_association(lua, REF_INNER, &this)? {
+            set_association(lua, REF_INNER, &luaref, t)?;
+        }
 
         Ok(luaref)
     }
 
-    pub fn get_ptr(&self) -> *mut c_void {
+    pub fn get_ptr(&self) -> *mut () {
         self.ptr
     }
 
     pub unsafe fn deref(&self) -> Self {
-        Self::new(*self.ptr.cast::<*mut c_void>(), None)
+        Self::new(*self.ptr.cast::<*mut ()>(), None)
     }
 
     pub unsafe fn offset(&self, offset: isize) -> LuaResult<Self> {
-        let range = if let Some(ref t) = self.range {
-            let high = t.high - offset;
-            let low = t.low - offset;
-
-            if low > 0 || high < 0 {
+        if let Some(ref t) = self.range {
+            if !t.check(offset) {
                 return Err(LuaError::external(format!(
-                    "Offset is out of bounds. low: {}, high: {}. offset got {}",
-                    t.low, t.high, offset
+                    "Offset is out of bounds. high: {}, low: {}. offset got {}",
+                    t.high, t.low, offset
                 )));
             }
+        }
+        let range = self.range.as_ref().map(|t| t.offset(offset));
 
-            Some(FfiRange { high, low })
-        } else {
-            None
-        };
-
-        Ok(Self::new(self.ptr.offset(offset), range))
+        Ok(Self::new(
+            self.ptr.cast::<u8>().offset(offset).cast(),
+            range,
+        ))
     }
 }
 
 impl LuaUserData for FfiRef {
     fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
-        methods.add_method("deref", |_, this, ()| {
-            let ffiref = unsafe { this.deref() };
-            Ok(ffiref)
+        methods.add_function("deref", |lua, this: LuaAnyUserData| {
+            let inner = get_association(lua, REF_INNER, &this)?;
+            let ffiref = this.borrow::<FfiRef>()?;
+            let result = lua.create_userdata(unsafe { ffiref.deref() })?;
+
+            if let Some(t) = inner {
+                set_association(lua, REF_INNER, &result, &t)?;
+            }
+
+            Ok(result)
         });
         methods.add_method("offset", |_, this, offset: isize| {
             let ffiref = unsafe { this.offset(offset)? };
diff --git a/crates/lune-std-ffi/src/ffi/mod.rs b/crates/lune-std-ffi/src/ffi/mod.rs
index 4812028..dd36905 100644
--- a/crates/lune-std-ffi/src/ffi/mod.rs
+++ b/crates/lune-std-ffi/src/ffi/mod.rs
@@ -1,4 +1,5 @@
 pub(super) mod ffi_association;
+pub(super) mod ffi_bounds;
 pub(super) mod ffi_box;
 pub(super) mod ffi_helper;
 pub(super) mod ffi_lib;
@@ -8,6 +9,5 @@ pub(super) mod ffi_ref;
 
 // Named registry table names
 mod association_names {
-    pub const BOX_REF_INNER: &str = "__box_ref";
     pub const REF_INNER: &str = "__ref_inner";
 }
diff --git a/crates/lune-std-ffi/src/lib.rs b/crates/lune-std-ffi/src/lib.rs
index 1bcfbe0..4a51975 100644
--- a/crates/lune-std-ffi/src/lib.rs
+++ b/crates/lune-std-ffi/src/lib.rs
@@ -6,13 +6,10 @@ use mlua::prelude::*;
 mod c;
 mod ffi;
 
-use crate::c::c_fn::CFn;
-use crate::c::c_struct::CStruct;
-use crate::c::c_type::create_all_types;
-use crate::ffi::ffi_association::get_table;
-use crate::ffi::ffi_box::FfiBox;
-use crate::ffi::ffi_lib::FfiLib;
-use crate::ffi::ffi_platform::get_platform_value;
+use crate::c::{c_fn::CFn, c_struct::CStruct, create_all_types};
+use crate::ffi::{
+    ffi_association::get_table, ffi_box::FfiBox, ffi_lib::FfiLib, ffi_platform::get_platform_value,
+};
 
 /**
     Creates the `ffi` standard library module.
diff --git a/crates/lune-std-ffi/todo.md b/crates/lune-std-ffi/todo.md
index 9eed077..f0a0ab8 100644
--- a/crates/lune-std-ffi/todo.md
+++ b/crates/lune-std-ffi/todo.md
@@ -1,11 +1,3 @@
-use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
-
-// pub fn ffi_get_struct_offsets(
-// abi: ffi_abi,
-// struct_type: *mut ffi_type,
-// offsets: *mut usize,
-// ) -> ffi_status;
-
 - last thing to do
 - [ ] Add tests
 - [ ] Add docs
@@ -23,8 +15,9 @@ use libffi::raw::{ffi_cif, ffi_ptrarray_to_raw};
 - [x] ffi.box(size)
 - [x] .size
 - [x] :zero()
-- [?] :ref(offset?=0) => ref
-  - offset is not impled
+- [x] :ref(offset?=0) => ref
+- [x] tostring
+
 - [~] :copy(box,size?=-1,offset?=0)
   - working on it