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    #[test]
183    fn test_encode() {
184        {
185            const S: &str = "abc你好";
186            const B1: &[u8; 9] = encode!(utf8, S);
187            const B2: &[u8] = encode!(utf8, S);
188            const B3: &[u8; 10] = encode_z!(utf8, S);
189            let mut ans = S.as_bytes().to_owned();
190            assert_eq!(B1, ans.as_slice());
191            assert_eq!(B2, B1);
192            ans.push(0);
193            assert_eq!(B3, ans.as_slice());
194        }
195        {
196            const S: &str = "abc你好𤭢";
197            const B1: &[u16; 7] = encode!(utf16, S);
198            const B2: &[u16; 8] = encode_z!(utf16, S);
199            let mut ans = S.encode_utf16().collect::<Vec<_>>();
200            assert_eq!(B1, ans.as_slice());
201            ans.push(0);
202            assert_eq!(B2, ans.as_slice());
203        }
204    }
205}