Skip to content

Commit 442c4e0

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 b515fad commit 442c4e0

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
@@ -1792,6 +1793,92 @@ impl From<Vec<GString>> for Value {
17921793
impl_from_glib_container_as_vec_string!(GString, *const c_char);
17931794
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
17941795

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

0 commit comments

Comments
 (0)