use std::collections::HashMap;
use std::ffi::{CString, CStr};
use std::mem;
use std::ptr;
use libc::{c_void, c_char, size_t};
use glib_ffi;
pub trait Ptr: Copy + 'static {
fn is_null(&self) -> bool;
fn from<X>(ptr: *mut X) -> Self;
}
impl<T: 'static> Ptr for *const T {
#[inline]
fn is_null(&self) -> bool { (*self).is_null() }
#[inline]
fn from<X>(ptr: *mut X) -> *const T { ptr as *const T }
}
impl<T: 'static> Ptr for *mut T {
#[inline]
fn is_null(&self) -> bool { (*self).is_null() }
#[inline]
fn from<X>(ptr: *mut X) -> *mut T { ptr as *mut T }
}
pub trait Uninitialized {
unsafe fn uninitialized() -> Self;
}
#[inline]
pub unsafe fn uninitialized<T: Uninitialized>() -> T {
T::uninitialized()
}
pub trait ToBool: Copy {
fn to_bool(self) -> bool;
}
impl ToBool for bool {
#[inline]
fn to_bool(self) -> bool {
self
}
}
impl ToBool for glib_ffi::gboolean {
#[inline]
fn to_bool(self) -> bool {
!(self == glib_ffi::GFALSE)
}
}
#[inline]
pub fn some_if<B: ToBool, T>(cond: B, val: T) -> Option<T> {
if cond.to_bool() {
Some(val)
}
else {
None
}
}
pub struct Stash<'a, P: Copy, T: ?Sized + ToGlibPtr<'a, P>> (pub P, pub <T as ToGlibPtr<'a, P>>::Storage);
pub struct StashMut<'a, P: Copy, T: ?Sized> (pub P, pub <T as ToGlibPtrMut<'a, P>>::Storage)
where T: ToGlibPtrMut<'a, P>;
pub trait ToGlib {
type GlibType;
fn to_glib(&self) -> Self::GlibType;
}
impl ToGlib for () {
type GlibType = ();
#[inline]
fn to_glib(&self) -> () {
()
}
}
impl ToGlib for bool {
type GlibType = glib_ffi::gboolean;
#[inline]
fn to_glib(&self) -> glib_ffi::gboolean {
if *self { glib_ffi::GTRUE } else { glib_ffi::GFALSE }
}
}
pub trait ToGlibPtr<'a, P: Copy> {
type Storage;
fn to_glib_none(&'a self) -> Stash<'a, P, Self>;
fn to_glib_full(&self) -> P {
unimplemented!();
}
}
pub trait ToGlibPtrMut<'a, P: Copy> {
type Storage;
fn to_glib_none_mut(&'a mut self) -> StashMut<P, Self>;
}
impl<'a, P: Ptr, T: ToGlibPtr<'a, P>> ToGlibPtr<'a, P> for Option<T> {
type Storage = Option<<T as ToGlibPtr<'a, P>>::Storage>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, P, Option<T>> {
self.as_ref().map_or(Stash(Ptr::from::<()>(ptr::null_mut()), None), |s| {
let s = s.to_glib_none();
Stash(s.0, Some(s.1))
})
}
#[inline]
fn to_glib_full(&self) -> P {
self.as_ref().map_or(Ptr::from::<()>(ptr::null_mut()), |s| s.to_glib_full())
}
}
impl <'a, 'opt: 'a, P: Ptr, T: ToGlibPtrMut<'a, P>> ToGlibPtrMut<'a, P> for Option<&'opt mut T> {
type Storage = Option<<T as ToGlibPtrMut<'a, P>>::Storage>;
#[inline]
fn to_glib_none_mut(&'a mut self) -> StashMut<'a, P, Option<&'opt mut T>> {
self.as_mut().map_or(StashMut(Ptr::from::<()>(ptr::null_mut()), None), |s| {
let s = s.to_glib_none_mut();
StashMut(s.0, Some(s.1))
})
}
}
impl<'a, P: Ptr, T: ?Sized + ToGlibPtr<'a, P>> ToGlibPtr<'a, P> for &'a T {
type Storage = <T as ToGlibPtr<'a, P>>::Storage;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, P, Self> {
let s = (*self).to_glib_none();
Stash(s.0, s.1)
}
#[inline]
fn to_glib_full(&self) -> P {
(*self).to_glib_full()
}
}
impl<'a> ToGlibPtr<'a, *const c_char> for str {
type Storage = CString;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *const c_char, Self> {
let tmp = CString::new(self).unwrap();
Stash(tmp.as_ptr(), tmp)
}
#[inline]
fn to_glib_full(&self) -> *const c_char {
unsafe {
glib_ffi::g_strndup(self.as_ptr() as *const c_char, self.len() as size_t)
as *const c_char
}
}
}
impl<'a> ToGlibPtr<'a, *mut c_char> for str {
type Storage = CString;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut c_char, Self> {
let tmp = CString::new(self).unwrap();
Stash(tmp.as_ptr() as *mut c_char, tmp)
}
#[inline]
fn to_glib_full(&self) -> *mut c_char {
unsafe {
glib_ffi::g_strndup(self.as_ptr() as *mut c_char, self.len() as size_t)
}
}
}
impl <'a> ToGlibPtr<'a, *const c_char> for String {
type Storage = CString;
#[inline]
fn to_glib_none(&self) -> Stash<'a, *const c_char, String> {
let tmp = CString::new(&self[..]).unwrap();
Stash(tmp.as_ptr(), tmp)
}
#[inline]
fn to_glib_full(&self) -> *const c_char {
unsafe {
glib_ffi::g_strndup(self.as_ptr() as *const c_char, self.len() as size_t)
as *const c_char
}
}
}
impl<'a, P: Ptr, T: ToGlibPtr<'a, P>> ToGlibPtr<'a, *mut P> for [T] {
type Storage = PtrArray<'a, P, T>;
#[inline]
fn to_glib_none(&'a self) -> Stash<'a, *mut P, Self> {
let mut tmp_vec: Vec<_> =
self.into_iter().map(|v| v.to_glib_none()).collect();
let mut ptr_vec: Vec<_> =
tmp_vec.iter_mut().map(|v| v.0).collect();
unsafe {
let zero = mem::zeroed();
ptr_vec.push(zero);
}
Stash(ptr_vec.as_mut_ptr(), PtrArray(ptr_vec, tmp_vec))
}
}
pub struct PtrArray<'a, P: Ptr, T: ?Sized + ToGlibPtr<'a, P>> (Vec<P>, Vec<Stash<'a, P, T>>);
impl<'a, P: Ptr, T: ToGlibPtr<'a, P> + 'a> PtrArray<'a, P, T> {
pub fn len(&self) -> usize {
self.1.len()
}
}
impl<'a> ToGlibPtr<'a, *mut glib_ffi::GHashTable> for HashMap<String, String> {
type Storage = (HashTable);
#[inline]
fn to_glib_none(&self) -> Stash<'a, *mut glib_ffi::GHashTable, Self> {
let ptr = self.to_glib_full();
Stash(ptr, HashTable(ptr))
}
#[inline]
fn to_glib_full(&self) -> *mut glib_ffi::GHashTable {
unsafe {
let ptr = glib_ffi::g_hash_table_new_full(Some(glib_ffi::g_str_hash),
Some(glib_ffi::g_str_equal), Some(glib_ffi::g_free), Some(glib_ffi::g_free));
for (k, v) in self {
glib_ffi::g_hash_table_insert(ptr, k.to_glib_full() as *mut _,
v.to_glib_full() as *mut _);
}
ptr
}
}
}
pub struct HashTable(*mut glib_ffi::GHashTable);
impl Drop for HashTable {
fn drop(&mut self) {
unsafe { glib_ffi::g_hash_table_unref(self.0) }
}
}
pub trait FromGlib<T>: Sized {
fn from_glib(val: T) -> Self;
}
#[inline]
pub fn from_glib<G, T: FromGlib<G>>(val: G) -> T {
FromGlib::from_glib(val)
}
impl FromGlib<glib_ffi::gboolean> for bool {
#[inline]
fn from_glib(val: glib_ffi::gboolean) -> bool {
!(val == glib_ffi::GFALSE)
}
}
impl FromGlib<i32> for Option<u32> {
#[inline]
fn from_glib(val: i32) -> Option<u32> {
if val >= 0 {
Some(val as u32)
}
else {
None
}
}
}
impl FromGlib<i64> for Option<u64> {
#[inline]
fn from_glib(val: i64) -> Option<u64> {
if val >= 0 {
Some(val as u64)
}
else {
None
}
}
}
impl FromGlib<i32> for Option<u64> {
#[inline]
fn from_glib(val: i32) -> Option<u64> {
FromGlib::from_glib(val as i64)
}
}
pub trait FromGlibPtr<P: Ptr>: Sized {
unsafe fn from_glib_none(ptr: P) -> Self;
unsafe fn from_glib_full(ptr: P) -> Self;
unsafe fn from_glib_borrow(_ptr: P) -> Self {
unimplemented!();
}
}
#[inline]
pub unsafe fn from_glib_none<P: Ptr, T: FromGlibPtr<P>>(ptr: P) -> T {
FromGlibPtr::from_glib_none(ptr)
}
#[inline]
pub unsafe fn from_glib_full<P: Ptr, T: FromGlibPtr<P>>(ptr: P) -> T {
FromGlibPtr::from_glib_full(ptr)
}
#[inline]
pub unsafe fn from_glib_borrow<P: Ptr, T: FromGlibPtr<P>>(ptr: P) -> T {
FromGlibPtr::from_glib_borrow(ptr)
}
impl<P: Ptr, T: FromGlibPtr<P>> FromGlibPtr<P> for Option<T> {
#[inline]
unsafe fn from_glib_none(ptr: P) -> Option<T> {
if ptr.is_null() { None }
else { Some(from_glib_none(ptr)) }
}
#[inline]
unsafe fn from_glib_full(ptr: P) -> Option<T> {
if ptr.is_null() { None }
else { Some(from_glib_full(ptr)) }
}
}
impl FromGlibPtr<*const c_char> for String {
#[inline]
unsafe fn from_glib_none(ptr: *const c_char) -> Self {
assert!(!ptr.is_null());
String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()).into_owned()
}
#[inline]
unsafe fn from_glib_full(ptr: *const c_char) -> Self {
let res = from_glib_none(ptr);
glib_ffi::g_free(ptr as *mut _);
res
}
}
impl FromGlibPtr<*mut c_char> for String {
#[inline]
unsafe fn from_glib_none(ptr: *mut c_char) -> Self {
assert!(!ptr.is_null());
String::from_utf8_lossy(CStr::from_ptr(ptr).to_bytes()).into_owned()
}
#[inline]
unsafe fn from_glib_full(ptr: *mut c_char) -> Self {
let res = from_glib_none(ptr);
glib_ffi::g_free(ptr as *mut _);
res
}
}
pub trait FromGlibPtrContainer<P: Ptr, PP: Ptr>: Sized {
unsafe fn from_glib_none(ptr: PP) -> Self;
unsafe fn from_glib_none_num(ptr: PP, num: usize) -> Self;
unsafe fn from_glib_container(ptr: PP) -> Self;
unsafe fn from_glib_container_num(ptr: PP, num: usize) -> Self;
unsafe fn from_glib_full(ptr: PP) -> Self;
unsafe fn from_glib_full_num(ptr: PP, num: usize) -> Self;
}
unsafe fn c_array_len<P: Ptr>(mut ptr: *const P) -> usize {
let mut len = 0;
if !ptr.is_null() {
while !(*ptr).is_null() {
len += 1;
ptr = ptr.offset(1);
}
}
len
}
impl <P: Ptr, T: FromGlibPtr<P>>
FromGlibPtrContainer<P, *const P>
for Vec<T> {
unsafe fn from_glib_none(ptr: *const P) -> Vec<T> {
let num = c_array_len(ptr);
Vec::from_glib_none_num(ptr, num)
}
unsafe fn from_glib_none_num(mut ptr: *const P, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let mut res = Vec::with_capacity(num);
while !(*ptr).is_null() {
res.push(from_glib_none(*ptr));
ptr = ptr.offset(1);
}
res
}
unsafe fn from_glib_container(ptr: *const P) -> Vec<T> {
let num = c_array_len(ptr);
FromGlibPtrContainer::from_glib_container_num(ptr, num)
}
unsafe fn from_glib_container_num(ptr: *const P, num: usize) -> Vec<T> {
let res = FromGlibPtrContainer::from_glib_none_num(ptr, num);
glib_ffi::g_free(ptr as *mut _);
res
}
unsafe fn from_glib_full(ptr: *const P) -> Vec<T> {
let num = c_array_len(ptr);
FromGlibPtrContainer::from_glib_full_num(ptr, num)
}
unsafe fn from_glib_full_num(mut ptr: *const P, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let mut res = Vec::with_capacity(num);
while !(*ptr).is_null() {
res.push(from_glib_full(*ptr));
ptr = ptr.offset(1);
}
glib_ffi::g_free(ptr as *mut _);
res
}
}
impl <P: Ptr, T: FromGlibPtr<P>>
FromGlibPtrContainer<P, *mut P>
for Vec<T> {
unsafe fn from_glib_none(ptr: *mut P) -> Vec<T> {
FromGlibPtrContainer::from_glib_none(ptr as *const P)
}
unsafe fn from_glib_none_num(ptr: *mut P, num: usize) -> Vec<T> {
FromGlibPtrContainer::from_glib_none_num(ptr as *const P, num)
}
unsafe fn from_glib_container(ptr: *mut P) -> Vec<T> {
FromGlibPtrContainer::from_glib_container(ptr as *const P)
}
unsafe fn from_glib_container_num(ptr: *mut P, num: usize) -> Vec<T> {
FromGlibPtrContainer::from_glib_container_num(ptr as *const P, num)
}
unsafe fn from_glib_full(ptr: *mut P) -> Vec<T> {
FromGlibPtrContainer::from_glib_full(ptr as *const P)
}
unsafe fn from_glib_full_num(ptr: *mut P, num: usize) -> Vec<T> {
FromGlibPtrContainer::from_glib_full_num(ptr as *const P, num)
}
}
unsafe fn slist_len(mut ptr: *mut glib_ffi::GSList) -> usize {
let mut len = 0;
while !ptr.is_null() {
ptr = (*ptr).next;
len += 1;
}
len
}
impl <P: Ptr, T: FromGlibPtr<P>> FromGlibPtrContainer<P, *mut glib_ffi::GSList> for Vec<T> {
unsafe fn from_glib_none(ptr: *mut glib_ffi::GSList) -> Vec<T> {
let num = slist_len(ptr);
FromGlibPtrContainer::from_glib_none_num(ptr, num)
}
unsafe fn from_glib_none_num(mut ptr: *mut glib_ffi::GSList, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let mut res = Vec::with_capacity(num);
while !ptr.is_null() {
let item_ptr: P = Ptr::from((*ptr).data);
if !item_ptr.is_null() {
res.push(from_glib_none(item_ptr));
}
ptr = (*ptr).next;
}
res
}
unsafe fn from_glib_container(ptr: *mut glib_ffi::GSList) -> Vec<T> {
let num = slist_len(ptr);
FromGlibPtrContainer::from_glib_container_num(ptr, num)
}
unsafe fn from_glib_container_num(ptr: *mut glib_ffi::GSList, num: usize) -> Vec<T> {
let res = FromGlibPtrContainer::from_glib_none_num(ptr, num);
if !ptr.is_null() {
glib_ffi::g_slist_free(ptr as *mut _);
}
res
}
unsafe fn from_glib_full(ptr: *mut glib_ffi::GSList) -> Vec<T> {
let num = slist_len(ptr);
FromGlibPtrContainer::from_glib_container_num(ptr, num)
}
unsafe fn from_glib_full_num(mut ptr: *mut glib_ffi::GSList, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let orig_ptr = ptr;
let mut res = Vec::with_capacity(num);
while !ptr.is_null() {
let item_ptr: P = Ptr::from((*ptr).data);
if !item_ptr.is_null() {
res.push(from_glib_full(item_ptr));
}
ptr = (*ptr).next;
}
if !orig_ptr.is_null() {
glib_ffi::g_slist_free(orig_ptr as *mut _);
}
res
}
}
unsafe fn list_len(mut ptr: *mut glib_ffi::GList) -> usize {
let mut len = 0;
while !ptr.is_null() {
ptr = (*ptr).next;
len += 1;
}
len
}
impl <P: Ptr, T: FromGlibPtr<P>> FromGlibPtrContainer<P, *mut glib_ffi::GList> for Vec<T> {
unsafe fn from_glib_none(ptr: *mut glib_ffi::GList) -> Vec<T> {
let num = list_len(ptr);
FromGlibPtrContainer::from_glib_none_num(ptr, num)
}
unsafe fn from_glib_none_num(mut ptr: *mut glib_ffi::GList, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let mut res = Vec::with_capacity(num);
while !ptr.is_null() {
let item_ptr: P = Ptr::from((*ptr).data);
if !item_ptr.is_null() {
res.push(from_glib_none(item_ptr));
}
ptr = (*ptr).next;
}
res
}
unsafe fn from_glib_container(ptr: *mut glib_ffi::GList) -> Vec<T> {
let num = list_len(ptr);
FromGlibPtrContainer::from_glib_container_num(ptr, num)
}
unsafe fn from_glib_container_num(ptr: *mut glib_ffi::GList, num: usize) -> Vec<T> {
let res = FromGlibPtrContainer::from_glib_none_num(ptr, num);
if !ptr.is_null() {
glib_ffi::g_list_free(ptr as *mut _);
}
res
}
unsafe fn from_glib_full(ptr: *mut glib_ffi::GList) -> Vec<T> {
let num = list_len(ptr);
FromGlibPtrContainer::from_glib_container_num(ptr, num)
}
unsafe fn from_glib_full_num(mut ptr: *mut glib_ffi::GList, num: usize) -> Vec<T> {
if num == 0 || ptr.is_null() {
return Vec::new()
}
let orig_ptr = ptr;
let mut res = Vec::with_capacity(num);
while !ptr.is_null() {
let mut item_ptr: P = mem::uninitialized();
let hack: *mut *mut c_void = mem::transmute(&mut item_ptr);
*hack = (*ptr).data;
if !item_ptr.is_null() {
res.push(from_glib_full(item_ptr));
}
ptr = (*ptr).next;
}
if !orig_ptr.is_null() {
glib_ffi::g_list_free(orig_ptr as *mut _);
}
res
}
}
unsafe extern "C" fn read_string_hash_table(key: glib_ffi::gpointer, value: glib_ffi::gpointer,
hash_map: glib_ffi::gpointer) {
let key: String = from_glib_none(key as *const c_char);
let value: String = from_glib_none(value as *const c_char);
let hash_map: &mut HashMap<String, String> = mem::transmute(hash_map);
hash_map.insert(key, value);
}
impl FromGlibPtrContainer<*const c_char, *mut glib_ffi::GHashTable> for HashMap<String, String> {
unsafe fn from_glib_none(ptr: *mut glib_ffi::GHashTable) -> Self {
let mut map = HashMap::new();
glib_ffi::g_hash_table_foreach(ptr, Some(read_string_hash_table), mem::transmute(&mut map));
map
}
unsafe fn from_glib_none_num(ptr: *mut glib_ffi::GHashTable, _: usize) -> Self {
FromGlibPtrContainer::from_glib_none(ptr)
}
unsafe fn from_glib_container(ptr: *mut glib_ffi::GHashTable) -> Self {
FromGlibPtrContainer::from_glib_full(ptr)
}
unsafe fn from_glib_container_num(ptr: *mut glib_ffi::GHashTable, _: usize) -> Self {
FromGlibPtrContainer::from_glib_full(ptr)
}
unsafe fn from_glib_full(ptr: *mut glib_ffi::GHashTable) -> Self {
let map = FromGlibPtrContainer::from_glib_none(ptr);
glib_ffi::g_hash_table_unref(ptr);
map
}
unsafe fn from_glib_full_num(ptr: *mut glib_ffi::GHashTable, _: usize) -> Self {
FromGlibPtrContainer::from_glib_full(ptr)
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use glib_ffi;
use super::*;
#[test]
fn string_hash_map() {
let mut map = HashMap::new();
map.insert("A".into(), "1".into());
map.insert("B".into(), "2".into());
map.insert("C".into(), "3".into());
let ptr: *mut glib_ffi::GHashTable = map.to_glib_full();
let map = unsafe { HashMap::from_glib_full(ptr) };
assert_eq!(map.get("A"), Some(&"1".into()));
assert_eq!(map.get("B"), Some(&"2".into()));
assert_eq!(map.get("C"), Some(&"3".into()));
}
}