Skip to content

Commit b2d8e6c

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 0963351 commit b2d8e6c

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
@@ -265,6 +265,7 @@ impl GStr {
265265
Ok(())
266266
}
267267
}
268+
pub const NONE: Option<&'static GStr> = None;
268269
}
269270

270271
// rustdoc-stripper-ignore-next
@@ -1864,6 +1865,92 @@ impl From<Vec<GString>> for Value {
18641865
impl_from_glib_container_as_vec_string!(GString, *const c_char);
18651866
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
18661867

1868+
// rustdoc-stripper-ignore-next
1869+
/// A trait to accept both <code>&[str]</code> or <code>&[GStr]</code> as an argument.
1870+
pub trait IntoGStr {
1871+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T;
1872+
}
1873+
1874+
impl IntoGStr for &GStr {
1875+
#[inline]
1876+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1877+
f(self)
1878+
}
1879+
}
1880+
1881+
impl IntoGStr for GString {
1882+
#[inline]
1883+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1884+
f(self.as_gstr())
1885+
}
1886+
}
1887+
1888+
impl IntoGStr for &GString {
1889+
#[inline]
1890+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1891+
f(self.as_gstr())
1892+
}
1893+
}
1894+
1895+
// Limit borrowed from rust std CStr optimization:
1896+
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/small_c_string.rs#L10
1897+
const MAX_STACK_ALLOCATION: usize = 384;
1898+
1899+
impl IntoGStr for &str {
1900+
#[inline]
1901+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1902+
if self.len() < MAX_STACK_ALLOCATION {
1903+
let mut s = mem::MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
1904+
let ptr = s.as_mut_ptr() as *mut u8;
1905+
let gs = unsafe {
1906+
ptr::copy_nonoverlapping(self.as_ptr(), ptr, self.len());
1907+
ptr.add(self.len()).write(0);
1908+
GStr::from_utf8_with_nul_unchecked(slice::from_raw_parts(ptr, self.len() + 1))
1909+
};
1910+
f(gs)
1911+
} else {
1912+
f(GString::from(self).as_gstr())
1913+
}
1914+
}
1915+
}
1916+
1917+
impl IntoGStr for String {
1918+
#[inline]
1919+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1920+
if self.len() < MAX_STACK_ALLOCATION {
1921+
self.as_str().run_with_gstr(f)
1922+
} else {
1923+
f(GString::from(self).as_gstr())
1924+
}
1925+
}
1926+
}
1927+
1928+
impl IntoGStr for &String {
1929+
#[inline]
1930+
fn run_with_gstr<T, F: FnOnce(&GStr) -> T>(self, f: F) -> T {
1931+
self.as_str().run_with_gstr(f)
1932+
}
1933+
}
1934+
1935+
pub const NONE_STR: Option<&'static str> = None;
1936+
1937+
// rustdoc-stripper-ignore-next
1938+
/// A trait to accept both <code>[Option]&lt;&[str]></code> or <code>[Option]&lt;&[GStr]></code> as
1939+
/// an argument.
1940+
pub trait IntoOptionalGStr {
1941+
fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T;
1942+
}
1943+
1944+
impl<S: IntoGStr> IntoOptionalGStr for Option<S> {
1945+
#[inline]
1946+
fn run_with_gstr<T, F: FnOnce(Option<&GStr>) -> T>(self, f: F) -> T {
1947+
match self {
1948+
Some(t) => t.run_with_gstr(|s| f(Some(s))),
1949+
None => f(None),
1950+
}
1951+
}
1952+
}
1953+
18671954
#[cfg(test)]
18681955
#[allow(clippy::disallowed_names)]
18691956
mod tests {

0 commit comments

Comments
 (0)