Skip to main content

relibc/header/wchar/
utf8.rs

1//UTF implementation parts for wchar.h.
2//Partially ported from the Sortix libc
3
4use core::{char, slice, str};
5
6use crate::{
7    header::errno,
8    platform::{
9        self,
10        types::{c_char, wchar_t},
11    },
12};
13
14use super::mbstate_t;
15
16// Based on
17// https://github.com/rust-lang/rust/blob/f24ce9b/library/core/src/str/validations.rs#L232-L257,
18// because apparently somebody removed the `pub use` statement from `core::str`.
19
20// https://tools.ietf.org/html/rfc3629
21static UTF8_CHAR_WIDTH: [u8; 256] = [
22    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
23    1, // 0x1F
24    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
25    1, // 0x3F
26    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
27    1, // 0x5F
28    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
29    1, // 0x7F
30    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
31    0, // 0x9F
32    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
33    0, // 0xBF
34    0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
35    2, // 0xDF
36    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF
37    4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF
38];
39
40// Given a first byte, determines how many bytes are in this UTF-8 character.
41#[inline]
42fn utf8_char_width(b: u8) -> usize {
43    UTF8_CHAR_WIDTH[usize::from(b)].into()
44}
45
46//It's guaranteed that we don't have any nullpointers here
47pub unsafe fn mbrtowc(pwc: *mut wchar_t, s: *const c_char, n: usize, ps: *mut mbstate_t) -> usize {
48    let size = utf8_char_width(unsafe { *s } as u8);
49    if size > n {
50        platform::ERRNO.set(errno::EILSEQ);
51        return -2isize as usize;
52    }
53    if size == 0 {
54        platform::ERRNO.set(errno::EILSEQ);
55        return -1isize as usize;
56    }
57
58    let slice = unsafe { slice::from_raw_parts(s.cast::<u8>(), size) };
59    let decoded = str::from_utf8(slice);
60    if decoded.is_err() {
61        platform::ERRNO.set(errno::EILSEQ);
62        return -1isize as usize;
63    }
64
65    let wc = decoded.unwrap();
66
67    let result: wchar_t = wc.chars().next().unwrap() as wchar_t;
68
69    if !pwc.is_null() {
70        unsafe { *pwc = result };
71    }
72
73    if result != 0 { size } else { 0 }
74}
75
76//It's guaranteed that we don't have any nullpointers here
77pub unsafe fn wcrtomb(s: *mut c_char, wc: wchar_t, ps: *mut mbstate_t) -> usize {
78    let dc = char::from_u32(wc as u32);
79
80    if dc.is_none() {
81        platform::ERRNO.set(errno::EILSEQ);
82        return -1isize as usize;
83    }
84
85    let c = dc.unwrap();
86    let size = c.len_utf8();
87    let slice = unsafe { slice::from_raw_parts_mut(s.cast::<u8>(), size) };
88
89    c.encode_utf8(slice);
90
91    size
92}
93
94/// Gets the encoded length of a character. It is used to recognize wide characters
95pub fn get_char_encoded_length(first_byte: u8) -> Option<usize> {
96    if first_byte >> 7 == 0 {
97        Some(1)
98    } else if first_byte >> 5 == 6 {
99        Some(2)
100    } else if first_byte >> 4 == 0xe {
101        Some(3)
102    } else if first_byte >> 3 == 0x1e {
103        Some(4)
104    } else {
105        None
106    }
107}