nng_c/
str.rs

1//! C String wrapper
2use core::{fmt, mem};
3
4use alloc::vec::Vec;
5
6///String's static buffer size
7pub const STATIC_SIZE: usize = mem::size_of::<usize>() * 2;
8
9#[derive(Clone)]
10enum State<'a> {
11    //This variant should already include zero char at the end
12    Slice(&'a [u8]),
13    //Static buffer, if slice fits it with zero char
14    Static([u8; STATIC_SIZE]),
15    //Last resort
16    Heap(Vec<u8>),
17}
18
19const _: () = {
20    assert!(mem::size_of::<State>() == 24);
21};
22
23#[repr(transparent)]
24#[derive(Clone)]
25///Wrapper for C string
26pub struct String<'a> {
27    state: State<'a>
28}
29
30impl<'a> String<'a> {
31    #[inline]
32    ///Creates new instance from C string (null terminated)
33    ///
34    ///Returns None if input has no NULL character at the end
35    pub const fn try_new_c(string: &'a [u8]) -> Option<Self> {
36        if string[string.len() - 1] == 0 {
37            Some(Self {
38                state: State::Slice(string)
39            })
40        } else {
41            None
42        }
43    }
44
45    #[inline]
46    ///Creates new instance from C string (null terminated)
47    ///
48    ///Panics if input has no NULL character at the end
49    pub const fn new_c(string: &'a [u8]) -> Self {
50        if string[string.len() - 1] == 0 {
51            Self {
52                state: State::Slice(string)
53            }
54        } else {
55            panic!("string is not NULL terminated")
56        }
57    }
58
59    ///Creates new String.
60    ///
61    ///If `string` ends with null character, then it slice will be used as it is otherwise
62    ///it shall create buffer to store `string` with null terminating character appended
63    pub fn new(string: &'a [u8]) -> Self {
64        let state = if let Some(this) = Self::try_new_c(string) {
65            this.state
66        } else if string.len() < STATIC_SIZE {
67            let mut buffer = [0u8; STATIC_SIZE];
68            buffer[..string.len()].copy_from_slice(string);
69            buffer[string.len()] = 0;
70            State::Static(buffer)
71        } else {
72            let mut buffer = Vec::with_capacity(string.len().saturating_add(1));
73            buffer.extend_from_slice(string);
74            buffer.push(0);
75            State::Heap(buffer)
76        };
77
78        Self {
79            state
80        }
81    }
82
83    ///Returns pointer to the underlying buffer
84    pub fn as_ptr(&self) -> *const u8 {
85        match &self.state {
86            State::Heap(buf) => buf.as_ptr(),
87            State::Static(buf) => buf.as_ptr() as _,
88            State::Slice(buf) => buf.as_ptr(),
89        }
90    }
91
92    ///Returns String without null termination character
93    pub fn as_bytes(&self) -> &[u8] {
94        match &self.state {
95            State::Heap(buf) => &buf[..buf.len()-1],
96            State::Static(buf) => {
97                let mut cursor = 0;
98                while cursor < STATIC_SIZE {
99                    if buf[cursor] == 0 {
100                        break;
101                    }
102                    cursor = cursor.saturating_add(1);
103                }
104                &buf[..cursor]
105            },
106            State::Slice(buf) => &buf[..buf.len()-1],
107        }
108    }
109}
110
111impl fmt::Debug for String<'_> {
112    #[inline]
113    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
114        fmt::Debug::fmt(self.as_bytes(), fmt)
115    }
116}
117
118impl PartialEq<[u8]> for String<'_> {
119    #[inline(always)]
120    fn eq(&self, other: &[u8]) -> bool {
121        PartialEq::eq(self.as_bytes(), other)
122    }
123}
124
125impl PartialEq<&[u8]> for String<'_> {
126    #[inline(always)]
127    fn eq(&self, other: &&[u8]) -> bool {
128        PartialEq::eq(self.as_bytes(), *other)
129    }
130}
131
132impl PartialEq<str> for String<'_> {
133    #[inline(always)]
134    fn eq(&self, other: &str) -> bool {
135        PartialEq::eq(self.as_bytes(), other.as_bytes())
136    }
137}
138
139impl<'a> From<&'a [u8]> for String<'a> {
140    #[inline]
141    fn from(value: &'a [u8]) -> Self {
142        Self::new(value)
143    }
144}
145
146impl<'a> From<&'a str> for String<'a> {
147    #[inline]
148    fn from(value: &'a str) -> Self {
149        Self::new(value.as_bytes())
150    }
151}