* Copyright (C) 2022-2024 Cyril Hrubis <metan@ucw.cz>
*/
* @file ujson_utf.h
* @brief Unicode helper macros and functions.
*/
#ifndef UJSON_UTF_H
#define UJSON_UTF_H
#include <stdint.h>
#include <stddef.h>
#define UJSON_UTF8_IS_ASCII(ch) (!((ch) & 0x80))
#define UJSON_UTF8_IS_NBYTE(ch) (((ch) & 0xc0) == 0x80)
#define UJSON_UTF8_IS_2BYTE(ch) (((ch) & 0xe0) == 0xc0)
#define UJSON_UTF8_IS_3BYTE(ch) (((ch) & 0xf0) == 0xe0)
#define UJSON_UTF8_IS_4BYTE(ch) (((ch) & 0xf8) == 0xf0)
#define UJSON_UTF8_NBYTE_MASK 0x3f
* @brief Parses next unicode character in UTF-8 string.
* @param str A pointer to the C string.
* @return A unicode character or 0 on error or end of the string.
*/
static inline uint32_t ujson_utf8_next(const char **str)
{
uint32_t s0 = *str[0];
(*str)++;
if (UJSON_UTF8_IS_ASCII(s0))
return s0;
uint32_t s1 = *str[0];
if (!UJSON_UTF8_IS_NBYTE(s1))
return 0;
s1 &= UJSON_UTF8_NBYTE_MASK;
(*str)++;
if (UJSON_UTF8_IS_2BYTE(s0))
return (s0 & 0x1f)<<6 | s1;
uint32_t s2 = *str[0];
if (!UJSON_UTF8_IS_NBYTE(s2))
return 0;
s2 &= UJSON_UTF8_NBYTE_MASK;
(*str)++;
if (UJSON_UTF8_IS_3BYTE(s0))
return (s0 & 0x0f)<<12 | s1<<6 | s2;
(*str)++;
uint32_t s3 = *str[0];
if (!UJSON_UTF8_IS_NBYTE(s2))
return 0;
s3 &= UJSON_UTF8_NBYTE_MASK;
if (UJSON_UTF8_IS_4BYTE(s0))
return (s0 & 0x07)<<18 | s1<<12 | s2<<6 | s3;
return 0;
}
* @brief Returns number of bytes next character is occupying in an UTF-8 string.
*
* @param str A pointer to a string.
* @param off An offset into the string, must point to a valid multibyte boundary.
* @return Number of bytes next character occupies, zero on string end and -1 on failure.
*/
int8_t ujson_utf8_next_chsz(const char *str, size_t off);
* @brief Returns number of bytes previous character is occupying in an UTF-8 string.
*
* @param str A pointer to a string.
* @param off An offset into the string, must point to a valid multibyte boundary.
* @return Number of bytes previous character occupies, and -1 on failure.
*/
int8_t ujson_utf8_prev_chsz(const char *str, size_t off);
* @brief Returns a number of characters in UTF-8 string.
*
* Returns number of characters in an UTF-8 string, which may be less or equal
* to what strlen() reports.
*
* @param str An UTF-8 string.
* @return Number of characters in the string.
*/
size_t ujson_utf8_strlen(const char *str);
* @brief Returns a number of bytes needed to store unicode character into UTF-8.
*
* @param unicode A unicode character.
* @return Number of utf8 bytes required to store a unicode character.
*/
static inline unsigned int ujson_utf8_bytes(uint32_t unicode)
{
if (unicode < 0x0080)
return 1;
if (unicode < 0x0800)
return 2;
if (unicode < 0x10000)
return 3;
return 4;
}
* @brief Writes an unicode character into a UTF-8 buffer.
*
* The buffer _must_ be large enough!
*
* @param unicode A unicode character.
* @param buf A byte buffer.
* @return A number of bytes written.
*/
static inline int ujson_to_utf8(uint32_t unicode, char *buf)
{
if (unicode < 0x0080) {
buf[0] = unicode & 0x007f;
return 1;
}
if (unicode < 0x0800) {
buf[0] = 0xc0 | (0x1f & (unicode>>6));
buf[1] = 0x80 | (0x3f & unicode);
return 2;
}
if (unicode < 0x10000) {
buf[0] = 0xe0 | (0x0f & (unicode>>12));
buf[1] = 0x80 | (0x3f & (unicode>>6));
buf[2] = 0x80 | (0x3f & unicode);
return 3;
}
buf[0] = 0xf0 | (0x07 & (unicode>>18));
buf[1] = 0x80 | (0x3f & (unicode>>12));
buf[2] = 0x80 | (0x3f & (unicode>>6));
buf[3] = 0x80 | (0x3f & unicode);
return 4;
}
#endif