Skip to content

Commit 7895f19

Browse files
committed
glib: add IntoGStr traits
These traits can accept both &str or &GStr as an argument. A copy is made when passing &str, but avoided when passing &GStr.
1 parent baac1cc commit 7895f19

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

glib/src/gstring.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ impl GStr {
248248
Ok(())
249249
}
250250
}
251+
pub const NONE: Option<&'static GStr> = None;
251252
}
252253

253254
// rustdoc-stripper-ignore-next
@@ -1793,6 +1794,92 @@ impl From<Vec<GString>> for Value {
17931794
impl_from_glib_container_as_vec_string!(GString, *const c_char);
17941795
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
17951796

1797+
// rustdoc-stripper-ignore-next
1798+
/// A trait to accept both <code>&[str]</code> or <code>&[GStr]</code> as an argument.
1799+
pub trait IntoGStr {
1800+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T;
1801+
}
1802+
1803+
impl IntoGStr for &GStr {
1804+
#[inline]
1805+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1806+
f(self)
1807+
}
1808+
}
1809+
1810+
impl IntoGStr for GString {
1811+
#[inline]
1812+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1813+
f(self.as_gstr())
1814+
}
1815+
}
1816+
1817+
impl IntoGStr for &GString {
1818+
#[inline]
1819+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1820+
f(self.as_gstr())
1821+
}
1822+
}
1823+
1824+
// Limit borrowed from rust std CStr optimization:
1825+
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/small_c_string.rs#L10
1826+
const MAX_STACK_ALLOCATION: usize = 384;
1827+
1828+
impl IntoGStr for &str {
1829+
#[inline]
1830+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1831+
if self.len() < MAX_STACK_ALLOCATION {
1832+
let mut s = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
1833+
let ptr = s.as_mut_ptr() as *mut u8;
1834+
let gs = unsafe {
1835+
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
1836+
ptr.add(self.len()).write(0);
1837+
GStr::from_utf8_with_nul_unchecked(slice::from_raw_parts(ptr, self.len() + 1))
1838+
};
1839+
f(gs)
1840+
} else {
1841+
f(GString::from(self).as_gstr())
1842+
}
1843+
}
1844+
}
1845+
1846+
impl IntoGStr for String {
1847+
#[inline]
1848+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1849+
if self.len() < MAX_STACK_ALLOCATION {
1850+
self.as_str().run_with_gstr(f)
1851+
} else {
1852+
f(GString::from(self).as_gstr())
1853+
}
1854+
}
1855+
}
1856+
1857+
impl IntoGStr for &String {
1858+
#[inline]
1859+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1860+
self.as_str().run_with_gstr(f)
1861+
}
1862+
}
1863+
1864+
pub const NONE_STR: Option<&'static str> = None;
1865+
1866+
// rustdoc-stripper-ignore-next
1867+
/// A trait to accept both <code>[Option]&lt;&[str]></code> or <code>[Option]&lt;&[GStr]></code> as
1868+
/// an argument.
1869+
pub trait IntoOptionalGStr {
1870+
fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T;
1871+
}
1872+
1873+
impl<S: IntoGStr> IntoOptionalGStr for Option<S> {
1874+
#[inline]
1875+
fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T {
1876+
match self {
1877+
Some(t) => t.run_with_gstr(|s| f(Some(s))),
1878+
None => f(None),
1879+
}
1880+
}
1881+
}
1882+
17961883
#[cfg(test)]
17971884
#[allow(clippy::disallowed_names)]
17981885
mod tests {

0 commit comments

Comments
 (0)