Skip to content

Commit dbbd938

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 ebfa622 commit dbbd938

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
@@ -1872,6 +1873,92 @@ impl From<Vec<GString>> for Value {
18721873
impl_from_glib_container_as_vec_string!(GString, *const c_char);
18731874
impl_from_glib_container_as_vec_string!(GString, *mut c_char);
18741875

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

0 commit comments

Comments
 (0)