//! Модуль обработки UTF-8 последовательностей для no_std окружения. //! //! Обеспечивает декодирование UTF-8 байтов в Unicode кодпоинты //! без использования стандартной библиотеки. /// Результат декодирования UTF-8 символа. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DecodeResult { /// Успешно декодирован символ /// - первый параметр: Unicode кодпоинт (char) /// - второй параметр: количество байт, использованных для декодирования (1-4) Ok(char, usize), /// Неполная последовательность (нужно больше байт) Incomplete, /// Некорректная UTF-8 последовательность Invalid, } /// Декодирует следующий UTF-8 символ из байтового буфера. /// /// # Параметры /// - `bytes`: слайс байтов, начинающийся с первого байта UTF-8 последовательности /// /// # Возвращает /// - `DecodeResult::Ok(char, usize)`: успешно декодированный символ и количество использованных байт /// - `DecodeResult::Incomplete`: последовательность неполная (нужно больше байт) /// - `DecodeResult::Invalid`: некорректная UTF-8 последовательность /// /// # Пример /// ``` /// use tverd_plus_tokenizer::utf8::{decode_utf8, DecodeResult}; /// /// let bytes = "Ъ".as_bytes(); /// match decode_utf8(bytes) { /// DecodeResult::Ok(ch, len) => { /// assert_eq!(ch, 'Ъ'); /// assert_eq!(len, 2); /// } /// _ => panic!("Ожидался успешный результат"), /// } /// ``` pub fn decode_utf8(bytes: &[u8]) -> DecodeResult { // TODO: Реализовать декодирование UTF-8 без использования std // Алгоритм: // 1. Проверить первый байт для определения длины последовательности // 2. Проверить, что в буфере достаточно байт // 3. Проверить корректность старших бит последующих байт // 4. Вычислить кодпоинт и вернуть DecodeResult::Ok // 5. Вернуть DecodeResult::Incomplete если не хватает байт // 6. Вернуть DecodeResult::Invalid при некорректной последовательности if bytes.is_empty() { return DecodeResult::Incomplete; } let first = bytes[0]; // ASCII символ (0xxxxxxx) if first & 0x80 == 0 { return DecodeResult::Ok(first as char, 1); } // Двухбайтовая последовательность (110xxxxx 10xxxxxx) if first & 0xE0 == 0xC0 { if bytes.len() < 2 { return DecodeResult::Incomplete; } if bytes[1] & 0xC0 != 0x80 { return DecodeResult::Invalid; } let code_point = ((first & 0x1F) as u32) << 6 | ((bytes[1] & 0x3F) as u32); if code_point < 0x80 { return DecodeResult::Invalid; // Overlong encoding } if let Some(ch) = core::char::from_u32(code_point) { return DecodeResult::Ok(ch, 2); } return DecodeResult::Invalid; } // Трехбайтовая последовательность (1110xxxx 10xxxxxx 10xxxxxx) if first & 0xF0 == 0xE0 { if bytes.len() < 3 { return DecodeResult::Incomplete; } if bytes[1] & 0xC0 != 0x80 || bytes[2] & 0xC0 != 0x80 { return DecodeResult::Invalid; } let code_point = ((first & 0x0F) as u32) << 12 | ((bytes[1] & 0x3F) as u32) << 6 | ((bytes[2] & 0x3F) as u32); if code_point < 0x800 { return DecodeResult::Invalid; // Overlong encoding } if let Some(ch) = core::char::from_u32(code_point) { return DecodeResult::Ok(ch, 3); } return DecodeResult::Invalid; } // Четырехбайтовая последовательность (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx) if first & 0xF8 == 0xF0 { if bytes.len() < 4 { return DecodeResult::Incomplete; } if bytes[1] & 0xC0 != 0x80 || bytes[2] & 0xC0 != 0x80 || bytes[3] & 0xC0 != 0x80 { return DecodeResult::Invalid; } let code_point = ((first & 0x07) as u32) << 18 | ((bytes[1] & 0x3F) as u32) << 12 | ((bytes[2] & 0x3F) as u32) << 6 | ((bytes[3] & 0x3F) as u32); if code_point < 0x10000 || code_point > 0x10FFFF { return DecodeResult::Invalid; } if let Some(ch) = core::char::from_u32(code_point) { return DecodeResult::Ok(ch, 4); } return DecodeResult::Invalid; } DecodeResult::Invalid } /// Итератор по UTF-8 символам в байтовом слайсе. /// /// Позволяет последовательно декодировать UTF-8 символы из байтового буфера. pub struct Utf8Chars<'a> { bytes: &'a [u8], position: usize, } impl<'a> Utf8Chars<'a> { /// Создает новый итератор UTF-8 символов. /// /// # Параметры /// - `bytes`: байтовый слайс для декодирования /// /// # Пример /// ``` /// use tverd_plus_tokenizer::utf8::Utf8Chars; /// /// let text = "Ъ+"; /// let mut chars = Utf8Chars::new(text.as_bytes()); /// ``` pub fn new(bytes: &'a [u8]) -> Self { // TODO: Реализовать создание итератора Self { bytes, position: 0, } } } impl<'a> Iterator for Utf8Chars<'a> { type Item = Result; fn next(&mut self) -> Option { // TODO: Реализовать итерацию по UTF-8 символам if self.position >= self.bytes.len() { return None; } let remaining = &self.bytes[self.position..]; match decode_utf8(remaining) { DecodeResult::Ok(ch, len) => { self.position += len; Some(Ok(ch)) } DecodeResult::Incomplete => { // Неполная последовательность в конце буфера None } DecodeResult::Invalid => { self.position += 1; // Пропускаем некорректный байт Some(Err(DecodeError::InvalidUtf8)) } } } } /// Ошибка декодирования UTF-8. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum DecodeError { /// Некорректная UTF-8 последовательность InvalidUtf8, } #[cfg(test)] mod tests { use super::*; // TODO: Написать тесты для UTF-8 декодирования // - Тест декодирования ASCII символов // - Тест декодирования двухбайтовых последовательностей (кириллица) // - Тест декодирования трехбайтовых последовательностей // - Тест декодирования четырехбайтовых последовательностей // - Тест обработки неполных последовательностей // - Тест обработки некорректных последовательностей // - Тест итератора Utf8Chars }