qubit_codec_text/codec/
utf16_u16_codec.rs1use crate::{
9 Charset,
10 CharsetCodec,
11 CharsetDecodeError,
12 CharsetDecodeErrorKind,
13 CharsetDecodeResult,
14 CharsetEncodeError,
15 CharsetEncodeErrorKind,
16 CharsetEncodeProbe,
17 CharsetEncodeResult,
18 Unicode,
19 Utf16,
20};
21use qubit_codec::Codec;
22
23#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
56pub struct Utf16U16Codec;
57
58impl Utf16U16Codec {
59 #[must_use]
65 #[inline(always)]
66 pub const fn charset(self) -> Charset {
67 Charset::UTF_16
68 }
69}
70
71impl CharsetCodec for Utf16U16Codec {
72 #[inline(always)]
78 fn charset(&self) -> Charset {
79 Charset::UTF_16
80 }
81}
82
83impl CharsetEncodeProbe for Utf16U16Codec {
84 #[inline(always)]
95 fn encode_len(
96 &self,
97 ch: char,
98 _index: usize,
99 ) -> CharsetEncodeResult<usize> {
100 Ok(Utf16::unit_len(ch))
101 }
102}
103
104unsafe impl Codec for Utf16U16Codec {
105 type Value = char;
106 type Unit = u16;
107 type DecodeError = CharsetDecodeError;
108 type EncodeError = CharsetEncodeError;
109
110 #[inline(always)]
111 fn min_units_per_value(&self) -> core::num::NonZeroUsize {
112 core::num::NonZeroUsize::MIN
113 }
114
115 #[inline(always)]
116 fn max_units_per_value(&self) -> core::num::NonZeroUsize {
117 unsafe {
119 core::num::NonZeroUsize::new_unchecked(Utf16::MAX_UNITS_PER_CHAR)
120 }
121 }
122
123 #[inline(always)]
124 unsafe fn decode_unchecked(
125 &self,
126 input: &[u16],
127 index: usize,
128 ) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
129 let (ch, consumed) = decode_units_prefix(input, index)?;
130 debug_assert!(consumed.get() <= input.len() - index);
131 Ok((ch, consumed))
132 }
133
134 #[inline(always)]
135 unsafe fn encode_unchecked(
136 &self,
137 ch: &char,
138 output: &mut [u16],
139 index: usize,
140 ) -> CharsetEncodeResult<usize> {
141 let written = encode_units_char(*ch, output, index)?;
142 debug_assert_eq!(written, ch.len_utf16());
143 debug_assert!(written <= output.len() - index);
144 Ok(written)
145 }
146}
147
148#[inline]
182fn decode_units_prefix(
183 input: &[u16],
184 index: usize,
185) -> CharsetDecodeResult<(char, core::num::NonZeroUsize)> {
186 if index > input.len() {
187 let kind = CharsetDecodeErrorKind::InvalidInputIndex {
188 input_len: input.len(),
189 };
190 return Err(CharsetDecodeError::new(Charset::UTF_16, kind, index));
191 }
192 if index == input.len() {
193 let kind = CharsetDecodeErrorKind::IncompleteSequence {
194 required: 1,
195 available: 0,
196 };
197 return Err(CharsetDecodeError::new(Charset::UTF_16, kind, index));
198 }
199 let first = input[index];
200 if Utf16::is_high_surrogate(first) {
201 if !has_units(input.len(), index, 2) {
202 let kind = CharsetDecodeErrorKind::IncompleteSequence {
203 required: 2,
204 available: input.len() - index,
205 };
206 return Err(CharsetDecodeError::new(Charset::UTF_16, kind, index));
207 }
208 let second = input[index + 1];
209 match Utf16::compose_pair(first, second).and_then(Unicode::to_char) {
210 Some(ch) => {
211 Ok((ch, unsafe { core::num::NonZeroUsize::new_unchecked(2) }))
213 }
214 None => {
215 let kind = CharsetDecodeErrorKind::MalformedSequence {
216 value: Some(second as u32),
217 };
218 Err(CharsetDecodeError::new(
219 Charset::UTF_16,
220 kind,
221 required_index(index, 1),
222 )
223 .with_consumed(2))
224 }
225 }
226 } else if Utf16::is_low_surrogate(first) {
227 let kind = CharsetDecodeErrorKind::MalformedSequence {
228 value: Some(first as u32),
229 };
230 Err(CharsetDecodeError::new(Charset::UTF_16, kind, index))
231 } else {
232 let ch = char::from_u32(first as u32)
233 .expect("non-surrogate UTF-16 unit is a scalar value");
234 Ok((ch, core::num::NonZeroUsize::MIN))
235 }
236}
237
238#[inline]
258fn encode_units_char(
259 ch: char,
260 output: &mut [u16],
261 index: usize,
262) -> CharsetEncodeResult<usize> {
263 if index > output.len() {
264 let kind = CharsetEncodeErrorKind::BufferTooSmall {
265 required: required_index(index, 1),
266 available: 0,
267 };
268 return Err(CharsetEncodeError::new(Charset::UTF_16, kind, index));
269 }
270 let length = Utf16::unit_len(ch);
271 let available = output.len() - index;
272 if available < length {
273 let kind = CharsetEncodeErrorKind::BufferTooSmall {
274 required: required_index(index, length),
275 available,
276 };
277 return Err(CharsetEncodeError::new(Charset::UTF_16, kind, index));
278 }
279 let code_point = ch as u32;
280 if length == 1 {
281 output[index] = code_point as u16;
282 } else {
283 output[index] = Utf16::high_surrogate(code_point)
284 .expect("supplementary scalar has high surrogate");
285 output[index + 1] = Utf16::low_surrogate(code_point)
286 .expect("supplementary scalar has low surrogate");
287 }
288 Ok(length)
289}
290
291#[inline(always)]
292const fn has_units(len: usize, index: usize, required_units: usize) -> bool {
293 match index.checked_add(required_units) {
294 Some(end) => len >= end,
295 None => false,
296 }
297}
298
299#[inline(always)]
300const fn required_index(index: usize, required_units: usize) -> usize {
301 match index.checked_add(required_units) {
302 Some(required) => required,
303 None => usize::MAX,
304 }
305}