Skip to content

Commit 0977820

Browse files
committed
Add glib::CStrV
This type represents a borrowed reference to a `NULL`-terminated array of `NULL`-terminated UTF-8 strings. Signed-off-by: fbrouille <fbrouille@users.noreply.github.com>
1 parent 63875ab commit 0977820

File tree

3 files changed

+221
-5
lines changed

3 files changed

+221
-5
lines changed

glib/src/collections/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pub mod slist;
1313
pub use slist::SList;
1414

1515
pub mod strv;
16-
pub use strv::StrV;
16+
pub use strv::{CStrV, StrV};

glib/src/collections/strv.rs

Lines changed: 219 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,26 @@ impl std::borrow::Borrow<[GStringPtr]> for StrV {
114114
}
115115
}
116116

117+
impl AsRef<CStrV> for StrV {
118+
#[inline]
119+
fn as_ref(&self) -> &CStrV {
120+
self.into()
121+
}
122+
}
123+
124+
impl std::borrow::Borrow<CStrV> for StrV {
125+
#[inline]
126+
fn borrow(&self) -> &CStrV {
127+
self.into()
128+
}
129+
}
130+
117131
impl std::ops::Deref for StrV {
118-
type Target = [GStringPtr];
132+
type Target = CStrV;
119133

120134
#[inline]
121-
fn deref(&self) -> &[GStringPtr] {
122-
self.as_slice()
135+
fn deref(&self) -> &CStrV {
136+
self.into()
123137
}
124138
}
125139

@@ -1359,6 +1373,208 @@ impl<const N: usize> IntoStrV for [&'_ String; N] {
13591373
}
13601374
}
13611375

1376+
// rustdoc-stripper-ignore-next
1377+
/// Representation of a borrowed `NULL`-terminated C array of `NULL`-terminated UTF-8 strings.
1378+
///
1379+
/// It can be constructed safely from a `&StrV` and unsafely from a pointer to a C array.
1380+
/// This type is very similar to `[GStringPtr]`, but with one added constraint: the underlying C array must be `NULL`-terminated.
1381+
#[repr(transparent)]
1382+
pub struct CStrV {
1383+
inner: [GStringPtr],
1384+
}
1385+
1386+
impl CStrV {
1387+
// rustdoc-stripper-ignore-next
1388+
/// Borrows a C array.
1389+
/// # Safety
1390+
///
1391+
/// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1392+
/// pass a pointer that does not uphold this condition.
1393+
#[inline]
1394+
pub unsafe fn from_glib_borrow<'a>(ptr: *const *const c_char) -> &'a CStrV {
1395+
let slice = StrV::from_glib_borrow(ptr);
1396+
&*(slice as *const [GStringPtr] as *const CStrV)
1397+
}
1398+
1399+
// rustdoc-stripper-ignore-next
1400+
/// Borrows a C array.
1401+
/// # Safety
1402+
///
1403+
/// The provided pointer **must** be `NULL`-terminated. It is undefined behavior to
1404+
/// pass a pointer that does not uphold this condition.
1405+
#[inline]
1406+
pub unsafe fn from_glib_borrow_num<'a>(ptr: *const *const c_char, len: usize) -> &'a CStrV {
1407+
let slice = StrV::from_glib_borrow_num(ptr, len);
1408+
&*(slice as *const [GStringPtr] as *const CStrV)
1409+
}
1410+
1411+
// rustdoc-stripper-ignore-next
1412+
/// Returns the underlying pointer.
1413+
///
1414+
/// This is guaranteed to be nul-terminated.
1415+
#[inline]
1416+
pub const fn as_ptr(&self) -> *const *const c_char {
1417+
self.inner.as_ptr() as *const *const _
1418+
}
1419+
}
1420+
1421+
impl fmt::Debug for CStrV {
1422+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1423+
self.inner.fmt(f)
1424+
}
1425+
}
1426+
1427+
unsafe impl Send for CStrV {}
1428+
1429+
unsafe impl Sync for CStrV {}
1430+
1431+
impl PartialEq for CStrV {
1432+
#[inline]
1433+
fn eq(&self, other: &Self) -> bool {
1434+
self.inner == other.inner
1435+
}
1436+
}
1437+
1438+
impl Eq for CStrV {}
1439+
1440+
impl PartialOrd for CStrV {
1441+
#[inline]
1442+
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1443+
Some(self.cmp(other))
1444+
}
1445+
}
1446+
1447+
impl Ord for CStrV {
1448+
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1449+
self.inner.cmp(&other.inner)
1450+
}
1451+
}
1452+
1453+
impl std::hash::Hash for CStrV {
1454+
#[inline]
1455+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1456+
self.inner.hash(state)
1457+
}
1458+
}
1459+
1460+
impl PartialEq<[&'_ str]> for CStrV {
1461+
fn eq(&self, other: &[&'_ str]) -> bool {
1462+
for (a, b) in Iterator::zip(self.iter(), other.iter()) {
1463+
if a != b {
1464+
return false;
1465+
}
1466+
}
1467+
1468+
true
1469+
}
1470+
}
1471+
1472+
impl PartialEq<CStrV> for [&'_ str] {
1473+
#[inline]
1474+
fn eq(&self, other: &CStrV) -> bool {
1475+
other.eq(self)
1476+
}
1477+
}
1478+
1479+
impl Default for &CStrV {
1480+
#[inline]
1481+
fn default() -> Self {
1482+
const SLICE: &[*const c_char] = &[ptr::null()];
1483+
// SAFETY: `SLICE` is indeed a valid nul-terminated array.
1484+
unsafe { CStrV::from_glib_borrow(SLICE.as_ptr()) }
1485+
}
1486+
}
1487+
1488+
impl std::ops::Deref for CStrV {
1489+
type Target = [GStringPtr];
1490+
1491+
#[inline]
1492+
fn deref(&self) -> &[GStringPtr] {
1493+
&self.inner
1494+
}
1495+
}
1496+
1497+
impl<'a> std::iter::IntoIterator for &'a CStrV {
1498+
type Item = &'a GStringPtr;
1499+
type IntoIter = std::slice::Iter<'a, GStringPtr>;
1500+
1501+
#[inline]
1502+
fn into_iter(self) -> Self::IntoIter {
1503+
self.inner.iter()
1504+
}
1505+
}
1506+
1507+
impl<'a> From<&'a StrV> for &'a CStrV {
1508+
fn from(value: &'a StrV) -> Self {
1509+
let slice = value.as_slice();
1510+
// Safety: `&StrV` is a null-terminated C array of nul-terminated UTF-8 strings,
1511+
// therefore `&StrV::as_slice()` return a a null-terminated slice of nul-terminated UTF-8 strings,
1512+
// thus it is safe to convert it to `&CStr`.
1513+
unsafe { &*(slice as *const [GStringPtr] as *const CStrV) }
1514+
}
1515+
}
1516+
1517+
impl FromGlibContainer<*mut c_char, *const *const c_char> for &CStrV {
1518+
unsafe fn from_glib_none_num(ptr: *const *const c_char, num: usize) -> Self {
1519+
CStrV::from_glib_borrow_num(ptr, num)
1520+
}
1521+
1522+
unsafe fn from_glib_container_num(_ptr: *const *const c_char, _num: usize) -> Self {
1523+
unimplemented!();
1524+
}
1525+
1526+
unsafe fn from_glib_full_num(_ptr: *const *const c_char, _num: usize) -> Self {
1527+
unimplemented!();
1528+
}
1529+
}
1530+
1531+
impl FromGlibPtrContainer<*mut c_char, *const *const c_char> for &CStrV {
1532+
#[inline]
1533+
unsafe fn from_glib_none(ptr: *const *const c_char) -> Self {
1534+
CStrV::from_glib_borrow(ptr)
1535+
}
1536+
1537+
unsafe fn from_glib_container(_ptr: *const *const c_char) -> Self {
1538+
unimplemented!();
1539+
}
1540+
1541+
unsafe fn from_glib_full(_ptr: *const *const c_char) -> Self {
1542+
unimplemented!();
1543+
}
1544+
}
1545+
1546+
impl<'a> ToGlibPtr<'a, *const *const c_char> for CStrV {
1547+
type Storage = PhantomData<&'a Self>;
1548+
1549+
#[inline]
1550+
fn to_glib_none(&'a self) -> Stash<'a, *const *const c_char, Self> {
1551+
Stash(self.as_ptr(), PhantomData)
1552+
}
1553+
}
1554+
1555+
impl IntoGlibPtr<*const *const c_char> for &CStrV {
1556+
#[inline]
1557+
fn into_glib_ptr(self) -> *const *const c_char {
1558+
self.as_ptr()
1559+
}
1560+
}
1561+
1562+
impl StaticType for CStrV {
1563+
#[inline]
1564+
fn static_type() -> crate::Type {
1565+
<Vec<String>>::static_type()
1566+
}
1567+
}
1568+
1569+
unsafe impl<'a> crate::value::FromValue<'a> for &'a CStrV {
1570+
type Checker = crate::value::GenericValueTypeChecker<Self>;
1571+
1572+
unsafe fn from_value(value: &'a crate::value::Value) -> Self {
1573+
let ptr = gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *const *const c_char;
1574+
CStrV::from_glib_borrow(ptr)
1575+
}
1576+
}
1577+
13621578
#[cfg(test)]
13631579
mod test {
13641580
use super::*;

glib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ mod exit_code;
120120
pub use exit_code::{ExitCode, InvalidExitCode};
121121

122122
pub mod collections;
123-
pub use collections::{List, PtrSlice, SList, Slice, StrV};
123+
pub use collections::{CStrV, List, PtrSlice, SList, Slice, StrV};
124124

125125
pub use self::auto::*;
126126
#[allow(clippy::too_many_arguments)]

0 commit comments

Comments
 (0)