Skip to content

Commit 6d15bed

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 f8911fd commit 6d15bed

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
@@ -271,6 +271,7 @@ impl GStr {
271271
Ok(())
272272
}
273273
}
274+
pub const NONE: Option<&'static GStr> = None;
274275
}
275276

276277
// rustdoc-stripper-ignore-next
@@ -1870,6 +1871,92 @@ impl From<Vec<GString>> for Value {
18701871
impl_from_glib_container_as_vec_string!(GString, *const c_char);
18711872
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
18721873

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

0 commit comments

Comments
 (0)