Files
tverd-plus-tokenizer/src/utf8.rs

203 lines
7.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Модуль обработки 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
}