aboutsummaryrefslogtreecommitdiffstats
path: root/libcharconv_joined.c
blob: e8ab45ff6ccf628d9f4a93e9866c22802b671916 (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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* See LICENSE file for copyright and license details. */
#include "lib-common.h"


static struct {
	uint_least32_t a;
	uint_least32_t b;
	uint_least32_t to;
} pairs[] = {
	{(uint_least32_t)'?', (uint_least32_t)'!', UINT32_C(0x2048)},
	{(uint_least32_t)'?', (uint_least32_t)'?', UINT32_C(0x2047)},
	{(uint_least32_t)'!', (uint_least32_t)'!', UINT32_C(0x203C)},
	{(uint_least32_t)'!', (uint_least32_t)'?', UINT32_C(0x2049)},
	{(uint_least32_t)':', (uint_least32_t)':', UINT32_C(0x2E2C)},
	{(uint_least32_t)'|', (uint_least32_t)'|', UINT32_C(0x2016)},
	{UINT32_C(0x23CB), UINT32_C(0x23BE), UINT32_C(0x23C9)},
	{UINT32_C(0x23CC), UINT32_C(0x23BF), UINT32_C(0x23CA)}
};


enum libcharconv_result
libcharconv_joined(const char *s, size_t slen, size_t *n, uint_least32_t *cp, size_t *ncp)
{
	uint_least32_t a, b, c;
	size_t i, alen, blen;
	*n = 0;
	while (slen) {
		alen = libcharconv_decode_utf8_(s, slen, &a);
		if (alen > slen) {
			if (*n)
				goto no_conv;
			return LIBCHARCONV_INDETERMINATE;
		}
		if (!alen) {
			*n += 1u;
			slen -= 1u;
			s = &s[1];
			continue;
		}

		if (UINT32_C(0x1681) <= a && a <= UINT32_C(0x1694)) {
			if (*n)
				goto no_conv;
			if (slen == alen)
				return LIBCHARCONV_INDETERMINATE;
			blen = libcharconv_decode_utf8_(&s[alen], slen - alen, &b);
			if (blen > slen)
				return LIBCHARCONV_INDETERMINATE;
			if (!blen)
				goto no_conv;
			if (UINT32_C(0x1681) > b || b > UINT32_C(0x1694))
				goto no_conv_consume;
			if ((a - UINT32_C(0x1681)) / 5u != (b - UINT32_C(0x1681)) / 5u)
				goto no_conv_consume;
			c = (a - UINT32_C(0x1681)) % 5u + (b - UINT32_C(0x1681)) % 5u + 1u;
			if (c >= 5u)
				goto no_conv_consume;
			a = c += (a - UINT32_C(0x1681)) / 5u * 5u + UINT32_C(0x1681);
			alen += blen;
			for (;;) {
				if (slen == alen)
					goto conv_if_end_calc;
				blen = libcharconv_decode_utf8_(&s[alen], slen - alen, &b);
				if (blen > slen)
					return LIBCHARCONV_INDETERMINATE;
				if (!blen)
					goto conv_calc;
				if (UINT32_C(0x1681) > b || b > UINT32_C(0x1694))
					goto conv_calc;
				if ((a - UINT32_C(0x1681)) / 5u != (b - UINT32_C(0x1681)) / 5u)
					goto conv_calc;
				b = (a - UINT32_C(0x1681)) % 5u + (b - UINT32_C(0x1681)) % 5u + 1u;
				if (b >= 5u)
					goto conv_calc;
				a = c = b + (a - UINT32_C(0x1681)) / 5u * 5u + UINT32_C(0x1681);
				alen += blen;
			}
			goto conv_calc;
		}

		for (i = 0u; i < sizeof(pairs) / sizeof(*pairs); i++) {
			if (a != pairs[i].a)
				continue;
			if (*n)
				goto no_conv;
			if (slen == alen)
				return LIBCHARCONV_INDETERMINATE;
			blen = libcharconv_decode_utf8_(&s[alen], slen - alen, &b);
			if (blen > slen)
				return LIBCHARCONV_INDETERMINATE;
			if (!blen)
				goto no_conv;
			if (b == pairs[i].b)
				goto conv;
		}

		*n += alen;
		s = &s[alen];
		slen -= alen;
	}
no_conv:
	return LIBCHARCONV_NO_CONVERT;

no_conv_consume:
	*n += alen;
	return LIBCHARCONV_NO_CONVERT;

conv:
	if (*n)
		goto no_conv;
	if (*ncp)
		*cp = c = pairs[i].to;
	*n += alen + blen;
	*ncp = 1u;
	return LIBCHARCONV_CONVERTED;

conv_calc:
	if (*n)
		goto no_conv;
	if (*ncp)
		*cp = c;
	*n += alen;
	*ncp = 1u;
	return LIBCHARCONV_CONVERTED;

conv_if_end_calc:
	if (*n)
		goto no_conv;
	if (*ncp)
		*cp = c;
	*n += alen;
	*ncp = 1u;
	return LIBCHARCONV_CONVERT_IF_END;
}