Skip to content
This repository has been archived by the owner on Jun 8, 2021. It is now read-only.

Commit

Permalink
lib: Introduce CStringHolder
Browse files Browse the repository at this point in the history
This new module keeps track of CStrings to help avoid allocating new Strings in
gir's generated code.
  • Loading branch information
philn committed Nov 16, 2018
1 parent 264fa6a commit dcba9dc
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 7 deletions.
173 changes: 173 additions & 0 deletions src/cstringholder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::ffi::CStr;
use std::ops::Deref;
use std::os::raw::c_char;
use std::ptr;
use translate::*;
use types::{StaticType, Type};

use gobject_ffi;
use ffi as glib_ffi;
use value::{FromValueOptional, Value};

#[derive(Debug, Hash, PartialOrd, Ord)]
pub struct CStringHolder {
slice: Box<CStr>,
ptr: *const c_char,
owned: bool,
}

impl CStringHolder {
pub fn new(ptr: *const c_char) -> Self {
assert!(!ptr.is_null());
unsafe {
let slice = CStr::from_ptr(ptr);
Self { ptr, slice: Box::from(slice), owned: true }
}
}

pub fn from(s: &[u8]) -> Self {
let slice = match CStr::from_bytes_with_nul(s) {
Ok(s) => s,
Err(e) => panic!(e),
};
let ptr = slice.as_ptr();
assert!(!ptr.is_null());
Self { ptr, slice: Box::from(slice), owned: false }
}

pub fn as_str(&self) -> &str {
let bytes = self.slice.to_bytes();
match std::str::from_utf8(bytes) {
Ok(s) => s,
Err(e) => {
panic!("UTF-8 conversion failed: {}", e);
},
}
}
}

impl Drop for CStringHolder {
fn drop(&mut self) {
if self.owned {
unsafe {
glib_ffi::g_free(self.ptr as *mut _);
}
}
}
}

impl PartialEq for CStringHolder {
fn eq(&self, other: &CStringHolder) -> bool {
self.slice == other.slice
}
}

impl Eq for CStringHolder {}

impl AsRef<str> for CStringHolder {
fn as_ref(&self) -> &str {
self.as_str()
}
}

impl Deref for CStringHolder {
type Target = str;

fn deref(&self) -> &str {
self.as_str()
}
}

impl From<CStringHolder> for String {
fn from(holder: CStringHolder) -> Self {
String::from(holder.as_str())
}
}

impl From<String> for CStringHolder {
fn from(s: String) -> Self {
CStringHolder::new(s.into_bytes().as_ptr() as *const c_char)
}
}

impl From<CStringHolder> for Box<str> {
fn from(holder: CStringHolder) -> Self {
Box::from(holder.as_str())
}
}

impl FromGlibPtrFull<*const c_char> for CStringHolder {
unsafe fn from_glib_full(ptr: *const c_char) -> Self {
CStringHolder::new(ptr)
}
}

impl FromGlibPtrFull<*mut i8> for CStringHolder {
unsafe fn from_glib_full(ptr: *mut i8) -> Self {
CStringHolder::new(ptr as *const c_char)
}
}

impl FromGlib<*mut i8> for CStringHolder {
fn from_glib(ptr: *mut i8) -> Self {
CStringHolder::new(ptr as *const c_char)
}
}

impl FromGlibPtrNone<*const c_char> for CStringHolder {
unsafe fn from_glib_none(ptr: *const c_char) -> Self {
assert!(!ptr.is_null());
CStringHolder::new(ptr)
}
}

impl FromGlibPtrNone<*mut i8> for CStringHolder {
unsafe fn from_glib_none(ptr: *mut i8) -> Self {
assert!(!ptr.is_null());
CStringHolder::new(ptr)
}
}

impl StaticType for CStringHolder {
fn static_type() -> Type {
unimplemented!();
}
}

impl<'a> FromValueOptional<'a> for CStringHolder {
unsafe fn from_value_optional(value: &'a Value) -> Option<Self> {
from_glib_none(gobject_ffi::g_value_get_string(value.to_glib_none().0))
}
}

impl_from_glib_container_as_vec_string!(CStringHolder, *const c_char);
impl_from_glib_container_as_vec_string!(CStringHolder, *mut c_char);

#[cfg(test)]
mod tests {
use cstringholder::CStringHolder;
use std::ffi::CString;

#[test]
fn test_holder() {
let data = CString::new("foo").unwrap();
let ptr = data.into_raw();

let holder = CStringHolder::new(ptr);
assert_eq!(holder.as_str(), "foo");
let foo: Box<str> = holder.into();
assert_eq!(foo.as_ref(), "foo");
}

#[test]
fn test_holder_from_str() {
let holder = CStringHolder::from(b"foo\0");
assert_eq!(holder.as_str(), "foo");
let foo: Box<str> = holder.into();
assert_eq!(foo.as_ref(), "foo");
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,9 @@ pub mod signal;
pub mod source;
pub use source::*;
mod time_val;
#[macro_use]
pub mod translate;
pub mod cstringholder;
pub mod types;
mod utils;
pub use utils::*;
Expand Down
17 changes: 10 additions & 7 deletions src/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ use std::path::{Path, PathBuf};
use std::ptr;
use libc::{c_char, size_t};
use ffi as glib_ffi;
use cstringholder;

/// A pointer
pub trait Ptr: Copy + 'static {
Expand Down Expand Up @@ -1088,26 +1089,25 @@ impl FromGlibPtrNone<*const c_char> for String {
impl FromGlibPtrFull<*const c_char> for String {
#[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
let holder = cstringholder::CStringHolder::new(ptr);
String::from(holder)
}
}

impl FromGlibPtrNone<*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()
let holder = cstringholder::CStringHolder::new(ptr as *const c_char);
String::from(holder)
}
}

impl FromGlibPtrFull<*mut c_char> for String {
#[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
let res = cstringholder::CStringHolder::new(ptr);
String::from(res)
}
}

Expand Down Expand Up @@ -1378,6 +1378,7 @@ impl_from_glib_container_as_vec_fundamental!(i64);
impl_from_glib_container_as_vec_fundamental!(f32);
impl_from_glib_container_as_vec_fundamental!(f64);

#[macro_export]
macro_rules! impl_from_glib_container_as_vec_string {
($name:ty, $ffi_name:ty) => {
impl FromGlibContainerAsVec<$ffi_name, *const $ffi_name> for $name {
Expand Down Expand Up @@ -1411,6 +1412,7 @@ macro_rules! impl_from_glib_container_as_vec_string {

unsafe fn from_glib_container_num_as_vec(ptr: *mut $ffi_name, num: usize) -> Vec<Self> {
let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
use ffi as glib_ffi;
glib_ffi::g_free(ptr as *mut _);
res
}
Expand All @@ -1424,6 +1426,7 @@ macro_rules! impl_from_glib_container_as_vec_string {
for i in 0..num {
res.push(from_glib_full(ptr::read(ptr.add(i))));
}
use ffi as glib_ffi;
glib_ffi::g_free(ptr as *mut _);
res
}
Expand Down

0 comments on commit dcba9dc

Please sign in to comment.