1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! A nul-terminated string,which is just a pointer to the string data,
//! it doesn't know the length of the string.

use crate::{
    std_types::RStr,
    utils::ref_as_nonnull,
};

use std::{
    cmp::{PartialEq,Eq},
    fmt::{self, Debug, Display},
    marker::PhantomData,
    ptr::NonNull,
};



/// A utf8 null-terminated string slice.
#[repr(transparent)]
#[derive(Copy,Clone,StableAbi)]
pub struct NulStr<'a>{
    ptr:NonNull<u8>,
    _marker:PhantomData<&'a u8>,
}

unsafe impl Sync for NulStr<'_>{}
unsafe impl Send for NulStr<'_>{}


impl NulStr<'static>{
    /// An empty string.
    pub const EMPTY: Self = NulStr{ptr: ref_as_nonnull(&0), _marker: PhantomData};
}

impl<'a> NulStr<'a>{
    /// Constructs an NulStr from a slice.
    /// 
    /// # Safety
    /// 
    /// `str` must be nul terminated(a 0 byte).
    pub const unsafe fn from_str(str: &'a str) -> Self{
        Self{
            ptr: NonNull::new_unchecked(str.as_ptr() as *mut u8),
            _marker:PhantomData,
        }
    }

    /// Constructs an NulStr from a pointer.
    /// 
    /// # Safety
    /// 
    /// The pointer must point to a utf8 and nul terminated (a 0 byte) sequence of bytes.
    pub const unsafe fn from_ptr(ptr: *const u8) -> Self{
        Self{
            ptr: NonNull::new_unchecked(ptr as *mut u8),
            _marker:PhantomData,
        }
    }

    /// Converts this `NulStr<'a>` to a `&'a str`,including the nul byte.
    ///
    /// # Performance
    ///
    /// This conversion requires traversing through the entire string to 
    /// find the nul byte.
    pub fn to_str_with_nul(&self)->&'a str{
        unsafe{
            let bytes=std::ffi::CStr::from_ptr(self.ptr.as_ptr() as *const _).to_bytes_with_nul();
            std::str::from_utf8_unchecked(bytes)
        }
    }

    /// Converts this `NulStr<'a>` to a `RStr<'a>`,including the nul byte.
    ///
    /// # Performance
    ///
    /// This conversion requires traversing through the entire string to 
    /// find the nul byte.
    pub fn to_rstr_with_nul(&self)->RStr<'a>{
        self.to_str_with_nul().into()
    }

    /// Converts this `NulStr<'a>` to a `&'a str`,not including the nul byte.
    ///
    /// # Performance
    ///
    /// This conversion requires traversing through the entire string to 
    /// find the nul byte.
    pub fn to_str(self)->&'a str{
        unsafe{
            let bytes = std::ffi::CStr::from_ptr(self.ptr.as_ptr() as *const _).to_bytes();
            std::str::from_utf8_unchecked(bytes)
        }
    }

    /// Converts this `NulStr<'a>` to a `RStr<'a>`,not including the nul byte.
    ///
    /// # Performance
    ///
    /// This conversion requires traversing through the entire string to 
    /// find the nul byte.
    pub fn to_rstr(self)->RStr<'a>{
        self.to_str().into()
    }
}


impl<'a> PartialEq for NulStr<'a>{
    fn eq(&self,other:&Self)->bool{
        self.ptr==other.ptr ||
        self.to_str()==other.to_str()
    }
}

impl<'a> Eq for NulStr<'a>{}



impl Display for NulStr<'_> {
    fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result{
        Display::fmt(self.to_str(),f)
    }
}

impl Debug for NulStr<'_> {
    fn fmt(&self,f:&mut fmt::Formatter<'_>)->fmt::Result{
        Debug::fmt(self.to_str(),f)
    }
}