Skip to content

Commit d4335b5

Browse files
jf2048sdroege
authored andcommitted
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 3caed0f commit d4335b5

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
@@ -273,6 +273,7 @@ impl GStr {
273273
Ok(())
274274
}
275275
}
276+
pub const NONE: Option<&'static GStr> = None;
276277
}
277278

278279
// rustdoc-stripper-ignore-next
@@ -1862,6 +1863,92 @@ impl From<Vec<GString>> for Value {
18621863
impl_from_glib_container_as_vec_string!(GString, *const c_char);
18631864
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
18641865

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

0 commit comments

Comments
 (0)