203 lines
7.9 KiB
Rust
203 lines
7.9 KiB
Rust
//! Модуль обработки 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<char, DecodeError>;
|
||
|
||
fn next(&mut self) -> Option<Self::Item> {
|
||
// 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
|
||
}
|
||
|