CStr API extensions

I'm proposing two groups of enhancements to CStr:

  1. Ability to convert a c_char pointer to a CStr pointer. This is useful to create an owning wrapper type around a CStr allocated by a C library. This could even be a generic LibcBox<CStr>, which only needs to care about CStr in a constructor method.

    One or more of the following:

    unsafe fn non_null_from_raw(ptr: NonNull<c_char>) -> NonNull<CStr>;
    unsafe fn ptr_from_raw(ptr: *const c_char) -> *const CStr;
    unsafe fn ptr_mut_from_raw(ptr: *mut c_char) -> *mut CStr;
    

    For the owned-wrapper use-case, the NonNull variant would be most convenient, but similar types tend to offer raw-ptr based APIs instead.

    For now, these would have almost the same safety precondition as CStr::from_ptr:

    • The memory pointed to by ptr must contain a valid nul terminator at the end of the string.
    • ptr must be valid for reads of bytes up to and including the nul terminator. This means in particular:
      • The entire memory range of this CStr must be contained within a single allocated object!
      • The pointer may be null for the *const and *mut variants
    • The nul terminator must be within isize::MAX from ptr (not sure if this one should be included)

    Note: This operation is intended to be a 0-cost cast but it is currently implemented with an up-front calculation of the length of the string. This is not guaranteed to always be the case.

    If CStr is changed to a type that doesn't store the length, these methods will still work, but the safety preconditions could be removed and the function marked as safe.

  2. Enable accessing a CStr as a [NonZero<u8>]

    A CStr without the terminator has exactly the same invariants as [NonZero<u8>]. This means manipulating it as [NonZero<u8>] is safe and infallible.

    Possible functions:

    fn to_non_null_bytes(self: &CStr) -> &[NonZero<u8>];
    fn to_non_null_bytes_mut(self: &mut CStr) -> &mut [NonZero<u8>];
    impl AsRef<[NonZero<u8>]> for CStr;
    impl AsRef<[NonZero<u8>]> for CString;
    impl AsMut<[NonZero<u8>]> for CStr;
    impl AsMut<[NonZero<u8>]> for CString;
    

    The above functions would have O(1) runtime for now, and O(n) if CStr doesn't store its length.

    CString could add support for infallible concatenation of non-zero slices:

    impl Extend<NonZero<u8>] for CString;
    fn extend_from_slice(&mut self, slice: &[NonZero<u8>]);
    

    It would be possible to use c_char instead of, or in addition to, u8 in these APIs. But the unpredictable signedness of that type is annoying, so I prefer u8.

1 Like