summaryrefslogtreecommitdiff
path: root/src/lib/string.c
blob: f6fa49d9d4ed64159567f215030c413cfef86b31 (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
#include "aplwc_internal.h"

struct string *
chars_to_string(struct aplwc *aplwc, const char *chars)
{
	const uint8_t *utf8;
	size_t bytecount, charcount;
	struct string *string;
	uint32_t c;
	uint8_t b;
	int seqlen, extra;

	utf8 = (const uint8_t *)chars;
	seqlen = extra = 0;
	for(bytecount = 0, charcount = 0; utf8[bytecount]; bytecount++){
		b = utf8[bytecount];
		if(extra == 0){
			c = 0;
			if((b & 0x80) == 0){
				seqlen = 0;
				c |= (b & 0x7F);
			}else if((b & 0xE0) == 0xC0){
				seqlen = 1;
				c |= (b & 0x1F);
			}else if((b & 0xF0) == 0xE0){
				seqlen = 2;
				c |= (b & 0x0F);
			}else if((b & 0xF8) == 0xF0){
				seqlen = 3;
				c |= (b & 0x7);
			}else{
invalid:
				aplwc->last_error = APLWC_INVALID_UTF8;
				return NULL;
			}
			extra = seqlen;
		}else{
			extra--;
			c <<= 6;
			if((b & 0xC0) == 0x80)
				c |= (b & 0x3F);
			else
				goto invalid;	
		}

		if(extra == 0){
			charcount++;
			switch(seqlen){
			case 0:
				if(NEVER(c > 0x7F))
					goto invalid;
				break;
			case 1:
				if((c < 0x80) || NEVER(c > 0x07FF))
					goto invalid;
				break;
			case 2:
				if((c < 0x0800) || NEVER(c > 0xFFFF))
					goto invalid;
				break;
			case 3:
				if((c < 0x010000) || NEVER(c > 0x10FFFF))
					goto invalid;
				break;
			}
		}
	}
	
	if(extra != 0)
		goto invalid;

	string = memory_alloc_string(&aplwc->memory, bytecount);
	if(string){
		string->bytes = bytecount;
		string->chars = charcount;
		string->utf8 = ((uint8_t *)string)+sizeof(*string);
		memcpy(string->utf8, utf8, bytecount);
	}else
		aplwc->last_error = APLWC_OUT_OF_MEMORY;

	return string;
}