const_str/__ctfe/
encode.rs

1use crate::slice::advance;
2use crate::utf16::CharEncodeUtf16;
3
4pub struct Utf8Encoder {
5    pub nul_terminated: bool,
6}
7
8pub struct Utf16Encoder {
9    pub nul_terminated: bool,
10}
11
12pub struct Encode<'a, T>(pub &'a str, pub T);
13
14impl Encode<'_, Utf8Encoder> {
15    pub const fn output_len(&self) -> usize {
16        if self.1.nul_terminated {
17            self.0.len() + 1
18        } else {
19            self.0.len()
20        }
21    }
22
23    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
24        let bytes = self.0.as_bytes();
25        if self.1.nul_terminated {
26            let mut buf = [0; N];
27            let mut i = 0;
28            while i < bytes.len() {
29                let b = bytes[i];
30                assert!(b != 0);
31                buf[i] = b;
32                i += 1;
33            }
34            assert!(i + 1 == N);
35            buf
36        } else {
37            crate::bytes::clone(bytes)
38        }
39    }
40}
41
42impl Encode<'_, Utf16Encoder> {
43    pub const fn output_len(&self) -> usize {
44        crate::utf16::str_len_utf16(self.0) + (self.1.nul_terminated as usize)
45    }
46
47    pub const fn const_eval<const N: usize>(&self) -> [u16; N] {
48        let mut s = self.0.as_bytes();
49
50        let mut buf = [0; N];
51        let mut pos = 0;
52
53        while let Some((code, count)) = crate::utf8::next_char(s) {
54            s = advance(s, count);
55            let e = CharEncodeUtf16::new(code);
56
57            buf[pos] = e.first();
58            pos += 1;
59
60            if e.has_second() {
61                buf[pos] = e.second();
62                pos += 1;
63            }
64
65            if self.1.nul_terminated {
66                assert!(buf[pos - 1] != 0);
67                if e.has_second() {
68                    assert!(buf[pos - 2] != 0);
69                }
70            }
71        }
72
73        if self.1.nul_terminated {
74            pos += 1;
75        }
76
77        assert!(pos == N);
78
79        buf
80    }
81}
82
83#[doc(hidden)]
84#[macro_export]
85macro_rules! __encoder {
86    (utf8) => {{
87        $crate::__ctfe::Utf8Encoder {
88            nul_terminated: false,
89        }
90    }};
91    (utf8_z) => {{
92        $crate::__ctfe::Utf8Encoder {
93            nul_terminated: true,
94        }
95    }};
96    (utf16) => {{
97        $crate::__ctfe::Utf16Encoder {
98            nul_terminated: false,
99        }
100    }};
101    (utf16_z) => {{
102        $crate::__ctfe::Utf16Encoder {
103            nul_terminated: true,
104        }
105    }};
106}
107
108#[doc(hidden)]
109#[macro_export]
110macro_rules! __encode {
111    ($e: tt, $s: expr) => {{
112        const OUTPUT_LEN: usize = $crate::__ctfe::Encode($s, $crate::__encoder!($e)).output_len();
113        &{ $crate::__ctfe::Encode($s, $crate::__encoder!($e)).const_eval::<OUTPUT_LEN>() }
114    }};
115}
116
117/// Encode a string slice with a specified encoding.
118///
119/// Supported encodings:
120///
121/// + utf8
122/// + utf16
123///
124/// This macro is [const-context only](./index.html#const-context-only).
125///
126/// # Examples
127/// ``` rust
128/// use const_str::encode;
129///
130/// const S: &str = "hello你好";
131///
132/// const S_UTF8: &[u8] = encode!(utf8, S);
133/// assert_eq!(S_UTF8, [104, 101, 108, 108, 111, 228, 189, 160, 229, 165, 189]);
134///
135/// const S_UTF16: &[u16] = encode!(utf16, S);
136/// assert_eq!(S_UTF16, [104, 101, 108, 108, 111, 20320, 22909]);
137/// ```
138///
139#[macro_export]
140macro_rules! encode {
141    (utf8, $s: expr) => {
142        $crate::__encode!(utf8, $s)
143    };
144    (utf16, $s: expr) => {
145        $crate::__encode!(utf16, $s)
146    };
147}
148
149/// Encode a string slice with a specified encoding and append a nul character.
150///
151/// The provided data should not contain any nul bytes in it.
152///
153/// This macro is [const-context only](./index.html#const-context-only).
154///
155/// See also [`const_str::encode!`][crate::encode]
156///
157/// # Examples
158/// ``` rust
159/// use const_str::encode_z;
160///
161/// const S: &str = "hello你好";
162///
163/// const S_UTF8_Z: &[u8] = encode_z!(utf8, S);
164/// assert_eq!(S_UTF8_Z, [104, 101, 108, 108, 111, 228, 189, 160, 229, 165, 189, 0]);
165///
166/// const S_UTF16_Z: &[u16] = encode_z!(utf16, S);
167/// assert_eq!(S_UTF16_Z, [104, 101, 108, 108, 111, 20320, 22909, 0]);
168/// ```
169///
170#[macro_export]
171macro_rules! encode_z {
172    (utf8, $s: expr) => {
173        $crate::__encode!(utf8_z, $s)
174    };
175    (utf16, $s: expr) => {
176        $crate::__encode!(utf16_z, $s)
177    };
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn test_encode() {
186        {
187            const S: &str = "abc你好";
188            const B1: &[u8; 9] = encode!(utf8, S);
189            const B2: &[u8] = encode!(utf8, S);
190            const B3: &[u8; 10] = encode_z!(utf8, S);
191            let mut ans = S.as_bytes().to_owned();
192            assert_eq!(B1, ans.as_slice());
193            assert_eq!(B2, B1);
194            ans.push(0);
195            assert_eq!(B3, ans.as_slice());
196        }
197        {
198            const S: &str = "abc你好𤭢";
199            const B1: &[u16; 7] = encode!(utf16, S);
200            const B2: &[u16; 8] = encode_z!(utf16, S);
201            let mut ans = S.encode_utf16().collect::<Vec<_>>();
202            assert_eq!(B1, ans.as_slice());
203            ans.push(0);
204            assert_eq!(B2, ans.as_slice());
205        }
206    }
207
208    #[test]
209    fn test_encode_runtime() {
210        // Runtime tests for Utf8Encoder
211        let encoder_utf8 = Encode(
212            "test",
213            Utf8Encoder {
214                nul_terminated: false,
215            },
216        );
217        assert_eq!(encoder_utf8.output_len(), 4);
218        let buf: [u8; 4] = encoder_utf8.const_eval();
219        assert_eq!(&buf, b"test");
220
221        let encoder_utf8_z = Encode(
222            "hello",
223            Utf8Encoder {
224                nul_terminated: true,
225            },
226        );
227        assert_eq!(encoder_utf8_z.output_len(), 6);
228        let buf2: [u8; 6] = encoder_utf8_z.const_eval();
229        assert_eq!(&buf2, b"hello\0");
230
231        // Runtime tests for Utf16Encoder
232        let encoder_utf16 = Encode(
233            "abc",
234            Utf16Encoder {
235                nul_terminated: false,
236            },
237        );
238        assert_eq!(encoder_utf16.output_len(), 3);
239        let buf3: [u16; 3] = encoder_utf16.const_eval();
240        assert_eq!(buf3, [b'a' as u16, b'b' as u16, b'c' as u16]);
241
242        let encoder_utf16_z = Encode(
243            "hi",
244            Utf16Encoder {
245                nul_terminated: true,
246            },
247        );
248        assert_eq!(encoder_utf16_z.output_len(), 3);
249        let buf4: [u16; 3] = encoder_utf16_z.const_eval();
250        assert_eq!(buf4, [b'h' as u16, b'i' as u16, 0]);
251
252        // Test with unicode
253        let encoder_unicode = Encode(
254            "你好",
255            Utf16Encoder {
256                nul_terminated: false,
257            },
258        );
259        let len = encoder_unicode.output_len();
260        assert_eq!(len, 2);
261
262        // Test empty string
263        let encoder_empty = Encode(
264            "",
265            Utf8Encoder {
266                nul_terminated: false,
267            },
268        );
269        assert_eq!(encoder_empty.output_len(), 0);
270    }
271}