Loading...
Searching...
No Matches
utf8_checker.hpp
Go to the documentation of this file.
/*
* RESTinio
*/
#pragma once
#include <restinio/compiler_features.hpp>
#include <cstdint>
namespace restinio
{
namespace utils
{
//
// utf8_checker_t
//
class utf8_checker_t
{
enum class state_t
{
wait_first_byte,
wait_second_of_two,
wait_second_of_three,
wait_second_of_four,
wait_third_of_three,
wait_third_of_four,
wait_fourth_of_four,
invalid,
};
std::uint32_t m_current_symbol = 0u;
state_t m_state{ state_t::wait_first_byte };
void
on_first_byte( std::uint8_t byte ) noexcept
{
if( byte <= 0x7Fu )
{
m_state = state_t::wait_first_byte;
m_current_symbol = byte;
}
else if( 0xC0u == (byte & 0xE0u) )
{
m_state = state_t::wait_second_of_two;
m_current_symbol = (byte & 0x1Fu);
}
else if( 0xE0u == (byte & 0xF0u) )
{
m_state = state_t::wait_second_of_three;
m_current_symbol = (byte & 0x0Fu);
}
else if( 0xF0u == (byte & 0xF8u) )
{
m_state = state_t::wait_second_of_four;
m_current_symbol = (byte & 0x07u);
}
else
{
// Because UTF-8 can represent only ranges from:
//
// 0000 0000-0000 007F
// 0000 0080-0000 07FF
// 0000 0800-0000 FFFF
// 0001 0000-0010 FFFF
//
// There is no need to check masks like 0b111110xx and so on.
//
// See https://datatracker.ietf.org/doc/html/rfc3629
//
m_state = state_t::invalid;
}
}
void
on_second_of_two( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
// Check for overlong sequence.
// The valid range for two bytes representation is 0x0080..0x07FF.
if( m_current_symbol < 0x0080u )
{
// The value is too small, it's overlong.
m_state = state_t::invalid;
}
else
// Three is no need to check the result value against
// invalid ranges (0xD800..0xDFFF and 0x110000..)
// because two bytes only represents 0x0080..0x07FF.
m_state = state_t::wait_first_byte;
}
else
{
m_state = state_t::invalid;
}
}
void
on_second_of_three( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
m_state = state_t::wait_third_of_three;
}
else
{
m_state = state_t::invalid;
}
}
void
on_second_of_four( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
m_state = state_t::wait_third_of_four;
}
else
{
m_state = state_t::invalid;
}
}
void
on_third_of_three( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
// Check for overlong sequence.
// The valid range for three bytes representation is 0x0800..0xFFFF.
if( m_current_symbol < 0x0800u )
{
// The value is too small, it's overlong.
m_state = state_t::invalid;
}
else
{
// It's necessary to check illigal points 0xD800..0xDFFF.
if( m_current_symbol >= 0xD800 && m_current_symbol <= 0xDFFF )
m_state = state_t::invalid;
else
m_state = state_t::wait_first_byte;
}
}
else
{
m_state = state_t::invalid;
}
}
void
on_third_of_four( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
m_state = state_t::wait_fourth_of_four;
}
else
{
m_state = state_t::invalid;
}
}
void
on_fourth_of_four( std::uint8_t byte ) noexcept
{
if( 0x80u == (byte & 0xC0u) )
{
m_current_symbol <<= 6;
m_current_symbol |= (byte & 0x3Fu);
// Check for overlong sequence.
// The valid range for three bytes representation is 0x10000..0x10FFFF.
if( m_current_symbol < 0x10000u )
{
// The value is too small, it's overlong.
m_state = state_t::invalid;
}
else
{
// It's necessary to check for values above 0x10FFFF.
// There is no need to check 0xD800..0xDFFF range because
// it was already handled by overlong check.
if( m_current_symbol >= 0x110000 )
m_state = state_t::invalid;
else
m_state = state_t::wait_first_byte;
}
}
else
{
m_state = state_t::invalid;
}
}
public:
utf8_checker_t() = default;
RESTINIO_NODISCARD
bool
process_byte( std::uint8_t byte ) noexcept
{
switch( m_state )
{
case state_t::wait_first_byte:
on_first_byte( byte );
break;
case state_t::wait_second_of_two:
on_second_of_two( byte );
break;
case state_t::wait_second_of_three:
on_second_of_three( byte );
break;
case state_t::wait_second_of_four:
on_second_of_four( byte );
break;
case state_t::wait_third_of_three:
on_third_of_three( byte );
break;
case state_t::wait_third_of_four:
on_third_of_four( byte );
break;
case state_t::wait_fourth_of_four:
on_fourth_of_four( byte );
break;
case state_t::invalid:
// Nothing to do.
break;
}
return (state_t::invalid != m_state);
}
RESTINIO_NODISCARD
bool
finalized() const noexcept
{
return state_t::wait_first_byte == m_state;
}
void
reset() noexcept
{
m_current_symbol = 0u;
m_state = state_t::wait_first_byte;
}
RESTINIO_NODISCARD
std::uint32_t
current_symbol() const noexcept { return m_current_symbol; }
};
} /* namespace utils */
} /* namespace restinio */
restinio::utils::utf8_checker_t
Helper class for checking UTF-8 byte sequence during parsing URI or incoming byte stream.
Definition: utf8_checker.hpp:33
restinio::utils::utf8_checker_t::on_fourth_of_four
void on_fourth_of_four(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:204
restinio::utils::utf8_checker_t::on_third_of_three
void on_third_of_three(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:158
restinio::utils::utf8_checker_t::on_second_of_three
void on_second_of_three(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:126
restinio::utils::utf8_checker_t::state_t
state_t
Enumeration of all possible checker states.
Definition: utf8_checker.hpp:36
restinio::utils::utf8_checker_t::state_t::wait_third_of_three
@ wait_third_of_three
restinio::utils::utf8_checker_t::state_t::wait_second_of_four
@ wait_second_of_four
restinio::utils::utf8_checker_t::state_t::wait_fourth_of_four
@ wait_fourth_of_four
restinio::utils::utf8_checker_t::state_t::wait_third_of_four
@ wait_third_of_four
restinio::utils::utf8_checker_t::state_t::wait_second_of_three
@ wait_second_of_three
restinio::utils::utf8_checker_t::state_t::wait_second_of_two
@ wait_second_of_two
restinio::utils::utf8_checker_t::state_t::wait_first_byte
@ wait_first_byte
restinio::utils::utf8_checker_t::state_t::invalid
@ invalid
restinio::utils::utf8_checker_t::on_second_of_four
void on_second_of_four(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:142
restinio::utils::utf8_checker_t::on_third_of_four
void on_third_of_four(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:188
restinio::utils::utf8_checker_t::on_first_byte
void on_first_byte(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:59
restinio::utils::utf8_checker_t::finalized
RESTINIO_NODISCARD bool finalized() const noexcept
Definition: utf8_checker.hpp:316
restinio::utils::utf8_checker_t::process_byte
RESTINIO_NODISCARD bool process_byte(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:271
restinio::utils::utf8_checker_t::m_current_symbol
std::uint32_t m_current_symbol
The current UNICODE symbol.
Definition: utf8_checker.hpp:53
restinio::utils::utf8_checker_t::current_symbol
RESTINIO_NODISCARD std::uint32_t current_symbol() const noexcept
Definition: utf8_checker.hpp:342
restinio::utils::utf8_checker_t::utf8_checker_t
utf8_checker_t()=default
restinio::utils::utf8_checker_t::reset
void reset() noexcept
Definition: utf8_checker.hpp:325
restinio::utils::utf8_checker_t::on_second_of_two
void on_second_of_two(std::uint8_t byte) noexcept
Definition: utf8_checker.hpp:99
restinio::utils::utf8_checker_t::m_state
state_t m_state
The current state of the checker.
Definition: utf8_checker.hpp:56
compiler_features.hpp
Detection of compiler version and absence of various features.
RESTINIO_NODISCARD
#define RESTINIO_NODISCARD
Definition: compiler_features.hpp:36
restinio
Definition: asio_include.hpp:21
const
#define const
Definition: zconf.h:230