summaryrefslogtreecommitdiffstats
path: root/src/common/impl/strutil.c
blob: 3a801f7e8df739d9b1961c1b239b87174cbae4d8 (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
#include "common/strutil.h"

uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width) {
    if (__builtin_expect(length == 0 || *str == '\0', false)) {
        if (width) {
            *width = 0;
        }
        return 0;
    }

    unsigned char first = (unsigned char) *str;
    if (__builtin_expect(first < 0x80, true)) {
        if (width) {
            *width = 1;
        }
        return 1;
    }

    uint8_t bytes;
    if ((first & 0xE0) == 0xC0) {
        bytes = 2;
    } else if ((first & 0xF0) == 0xE0) {
        bytes = 3;
    } else if ((first & 0xF8) == 0xF0) {
        bytes = 4;
    } else {
        if (width) {
            *width = 1;
        }
        return 1;
    }

    if (length < bytes) {
        if (width) {
            *width = 1;
        }
        return 1;
    }

    for (uint8_t i = 1; i < bytes; ++i) {
        unsigned char continuation = (unsigned char) str[i];
        if (continuation == '\0' || (continuation & 0xC0) != 0x80) {
            if (width) {
                *width = 1;
            }
            return 1;
        }
    }

    uint32_t ucs = (uint32_t) (first & ((1U << (8 - bytes)) - 1));
    for (uint8_t i = 1; i < bytes; ++i) {
        ucs <<= 6;
        ucs |= (uint32_t) ((unsigned char) str[i] & 0x3F);
    }

    int wcWidth = mk_wcwidth(ucs);
    if (width) {
        *width = (uint8_t) (wcWidth < 0 ? 0 : wcWidth);
    }
    return bytes;
}

uint32_t ffUtf8StrWidth(const char* str, uint32_t length) {
    uint32_t result = 0;
    const char* ptr = str;

    while (length > 0 && *ptr != '\0') {
        uint8_t width = 0;
        uint8_t bytes = ffUtf8CharLenWidth(ptr, length, &width);
        if (__builtin_expect(bytes == 0, false)) {
            break;
        }

        result += width;
        ptr += bytes;
        length -= bytes;
    }

    return result > 0 ? result : (uint32_t) (ptr - str);
}