summaryrefslogtreecommitdiff
path: root/src/runtime/c/gu/ucs.c
blob: 2b1e63cf0112af95256426ab8393ce827ff05b38 (plain)
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
#include <gu/ucs.h>
#include <gu/assert.h>

GU_DEFINE_TYPE(GuUCSExn, abstract, _);

bool
gu_char_is_valid(char c)
{
	if (c < 0) {
		return false;
	} else if (c < 64) {
		return UINT64_C(0xffffffef00003f81) & (UINT64_C(1) << c);
	}
#if CHAR_MAX > 127 // Let's avoid spurious warnings
	else if (c > 127) {
		return false;
	}
#endif	
	return UINT64_C(0x7ffffffefffffffe) & (UINT64_C(1) << (c - 64));
}

char
gu_ucs_char(GuUCS uc, GuExn* err)
{
	if (0 <= uc && uc <= 127) {
		char c = (char) uc;
		if (gu_char_is_valid(c)) {
			return c;
		}
	}
	gu_raise(err, GuUCSExn);
	return 0;
}

size_t
gu_str_to_ucs(const char* cbuf, size_t len, GuUCS* ubuf, GuExn* err)
{
	size_t n = 0;
	while (n < len) {
		char c = cbuf[n];
		if (!gu_char_is_valid(c)) {
			gu_raise(err, GuUCSExn);
			return n;
		}
		ubuf[n] = gu_char_ucs(c);
		n++;
	}
	return n;
}

size_t
gu_ucs_to_str(const GuUCS* ubuf, size_t len, char* cbuf, GuExn* err)
{
	size_t n = 0;
	while (n < len) {
		char c = gu_ucs_char(ubuf[n], err);
		if (!gu_ok(err)) {
			break;
		}
		cbuf[n] = c;
		n++;
	}
	return n;
}


extern inline bool
gu_ucs_valid(GuUCS ucs);

GuUCS
gu_char_ucs(char c)
{
	gu_require(gu_char_is_valid(c));
	GuUCS u = (GuUCS) c;
	gu_ensure(u < 0x80);
	return u;
}