Commit 4a4a46c2 authored by Iliar Rabet's avatar Iliar Rabet Committed by Benjamin Cabé
Browse files

util: adds the utf8_count_chars function

The function utf8_count_chars counts the number of UTF-encoded characters

Signed-off-by: default avatarIliar Rabet <iliar.rabet@gmail.com>
parent 8993a5ed
Loading
Loading
Loading
Loading
+16 −0
Changes for include/zephyr/sys/util.h: 16 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>


/** @brief Number of bits that make up a type */
#define NUM_BITS(t) (sizeof(t) * BITS_PER_BYTE)
@@ -688,6 +690,20 @@ char *utf8_trunc(char *utf8_str);
 */
char *utf8_lcpy(char *dst, const char *src, size_t n);

/**
 * @brief Counts the characters in a UTF-8 encoded string @p s
 *
 * Counts the number of UTF-8 characters (code points) in a null-terminated string.
 * This function steps through each UTF-8 sequence by checking leading byte patterns.
 * It does not fully validate UTF-8 correctness, only counts characters.
 *
 * @param s The input string
 *
 * @return Number of UTF-8 characters in @p s on success or (negative) error code
 *  otherwise.
 */
ssize_t utf8_count_chars(const char *s);

#define __z_log2d(x) (32 - __builtin_clz(x) - 1)
#define __z_log2q(x) (64 - __builtin_clzll(x) - 1)
#define __z_log2(x) (sizeof(__typeof__(x)) > 4 ? __z_log2q(x) : __z_log2d(x))
+45 −0
Changes for lib/utils/utf8.c: 45 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -7,12 +7,15 @@
#include <stdint.h>
#include <string.h>
#include <zephyr/sys/__assert.h>
#include <errno.h>
#include <sys/types.h>

#define ASCII_CHAR 0x7F
#define SEQUENCE_FIRST_MASK 0xC0
#define SEQUENCE_LEN_2_BYTE 0xC0
#define SEQUENCE_LEN_3_BYTE 0xE0
#define SEQUENCE_LEN_4_BYTE 0xF0
#define MSB_SET 0x80

char *utf8_trunc(char *utf8_str)
{
@@ -79,3 +82,45 @@ char *utf8_lcpy(char *dst, const char *src, size_t n)

	return dst;
}

ssize_t utf8_count_chars(const char *s)
{
	ssize_t count = 0;
	const char *p = s; /* getting a pointer to increment */

	while (*p != '\0') {
		if ((*p & MSB_SET) == 0) { /* 1-byte character: 0xxxxxxx */
			p += 1;
		} else if ((*p & SEQUENCE_LEN_3_BYTE) == SEQUENCE_FIRST_MASK) {
			/* 2-byte character: 110xxxxx */
			if ((p[1] & SEQUENCE_FIRST_MASK) != MSB_SET) {
				/* invalid continuation */
				return -EINVAL;
			}
			p += 2;
		} else if ((*p & SEQUENCE_LEN_4_BYTE) == SEQUENCE_LEN_3_BYTE) {
			/* 3-byte character: 1110xxxx */
			if ((p[1] & SEQUENCE_FIRST_MASK) != MSB_SET
				|| (p[2] & SEQUENCE_FIRST_MASK) != MSB_SET) {
				/* invalid continuation */
				return -EINVAL;
			}
			p += 3;
		} else if ((*p & 0xF8) == SEQUENCE_LEN_4_BYTE) {
			/* 4-byte character: 11110xxx */
			if ((p[1] & SEQUENCE_FIRST_MASK) != MSB_SET
				|| (p[2] & SEQUENCE_FIRST_MASK) != MSB_SET
				|| (p[3] & SEQUENCE_FIRST_MASK) != MSB_SET) {
				/* invalid continuation */
				return -EINVAL;
			}
			p += 4;
		} else {
			/* Invalid UTF-8 byte (return) */
			return -EINVAL;
		}
		count++;
	}

	return count;
}