aboutsummaryrefslogtreecommitdiffstats
path: root/libcharconv_decode_utf8_.c
blob: 31a2f58673f7a674222e5d69f0c45c5722837040 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/* See LICENSE file for copyright and license details. */
#include "lib-common.h"


size_t
libcharconv_decode_utf8_(const char *s, size_t slen, uint_least32_t *cp)
{
	uint_least32_t min, max;
	size_t i, n;

	if (slen < 1u)
		return 0u;

	if (!(s[0] & 0x80)) {
		*cp = (uint_least32_t)s[0];
		return 1u;
	} else if ((s[0] & 0xE0) == 0xC0) {
		*cp = (uint_least32_t)s[0] & 0x3Fu;
		n = 2u;
		min = UINT32_C(0x80);
		max = UINT32_C(0x800);
	} else if ((s[0] & 0xF0) == 0xE0) {
		*cp = (uint_least32_t)s[0] & 0x1Fu;
		n = 3u;
		min = UINT32_C(0x800);
		max = UINT32_C(0x10000);
	} else if ((s[0] & 0xF8) == 0xF0) {
		*cp = (uint_least32_t)s[0] & 0x0Fu;
		n = 4u;
		min = UINT32_C(0x10000);
		max = UINT32_C(0x110000);
	} else {
		return 0u;
	}

	if (slen < n)
		return n;

	for (i = 1u; i < n; i++) {
		if ((s[i] & 0xC0) != 0x80)
			return 0u;
		*cp <<= 6;
		*cp |= (uint_least32_t)s[i] & 0x3Fu;
	}

	if (min > *cp || *cp >= max)
		return 0u;

	return n;
}