Files
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

144 lines
4.6 KiB
C

#include <stdint.h>
#include <stdlib.h>
/*
* UTF-8 <-> UTF-32 conversion
*/
// The following code is adapted from https://stackoverflow.com/a/70760522/910109 under
// the terms of CC BY-SA 4.0. The function signature and variable names are changed.
#define UTF8_SURROGATE_LOW_BITS 0x7FF
#define UTF8_MAX_SURROGATE 0xDFFF
#define UTF8_MAX_FOUR_BYTE 0x10FFFF
#define UTF8_ONE_BYTE_BITS 7
#define UTF8_TWO_BYTE_BITS 11
#define UTF8_TWO_BYTE_PREFIX 0xC0
#define UTF8_THREE_BYTE_BITS 16
#define UTF8_THREE_BYTE_PREFIX 0xE0
#define UTF8_FOUR_BYTE_PREFIX 0xF0
#define UTF8_CONTINUATION_BYTE 0x80
#define UTF8_CONTINUATION_MASK 0x3F
/**
* Ensure that buffer has space for AT LEAST 4 bytes before calling this function,
* or a buffer overrun will occur.
* Returns the number of bytes written to buffer (0-4).
* If codepoint is a surrogate value, or is out of range for a Unicode codepoint,
* writes nothing and returns 0.
* Surrogate values are integers from 0xD800 to 0xDFFF, inclusive.
* Valid Unicode codepoint values are non-surrogate integers between
* 0 and 1_114_111 decimal (0x10_FFFF hex), inclusive.
*/
static size_t codepoint_to_utf8(int32_t codepoint, uint8_t *buffer) {
if ((codepoint | UTF8_SURROGATE_LOW_BITS) == UTF8_MAX_SURROGATE
|| codepoint > UTF8_MAX_FOUR_BYTE
|| codepoint < 0) {
return 0;
}
int bytes_written = 0;
if ((codepoint >> UTF8_ONE_BYTE_BITS) == 0) {
*buffer++ = codepoint;
bytes_written = 1;
} else if ((codepoint >> UTF8_TWO_BYTE_BITS) == 0) {
*buffer++ = UTF8_TWO_BYTE_PREFIX | (codepoint >> 6);
bytes_written = 2;
} else if ((codepoint >> UTF8_THREE_BYTE_BITS) == 0) {
*buffer++ = UTF8_THREE_BYTE_PREFIX | (codepoint >> 12);
bytes_written = 3;
} else {
*buffer++ = UTF8_FOUR_BYTE_PREFIX | (codepoint >> 18);
bytes_written = 4;
}
// Intentionally falling through each case
switch (bytes_written) {
case 4:
*buffer++ = UTF8_CONTINUATION_BYTE | ((codepoint >> 12) & UTF8_CONTINUATION_MASK);
// fall through
case 3:
*buffer++ = UTF8_CONTINUATION_BYTE | ((codepoint >> 6) & UTF8_CONTINUATION_MASK);
// fall through
case 2:
*buffer++ = UTF8_CONTINUATION_BYTE | (codepoint & UTF8_CONTINUATION_MASK);
// fall through
default:
return bytes_written;
}
}
// The following code is adapted from https://codereview.stackexchange.com/q/197548 and replies
// under the terms of CC BY-SA 4.0. The function signatures and variable names are changed.
#define UTF8_ONE_BYTE_MASK 0b10000000
#define UTF8_ONE_BYTE_COUNT 0
#define UTF8_TWO_BYTE_MASK 0b11100000
#define UTF8_TWO_BYTE_COUNT 0b11000000
#define UTF8_THREE_BYTE_MASK 0b11110000
#define UTF8_THREE_BYTE_COUNT 0b11100000
#define UTF8_FOUR_BYTE_MASK 0b11111000
#define UTF8_FOUR_BYTE_COUNT 0b11110000
static size_t utf8_codepoint_size(uint8_t text) {
if ((text & UTF8_ONE_BYTE_MASK) == UTF8_ONE_BYTE_COUNT) {
return 1;
}
if ((text & UTF8_TWO_BYTE_MASK) == UTF8_TWO_BYTE_COUNT) {
return 2;
}
if ((text & UTF8_THREE_BYTE_MASK) == UTF8_THREE_BYTE_COUNT) {
return 3;
}
return 4;
}
static size_t utf8_to_codepoints(int32_t *codepoints, uint8_t *text, size_t byte_count) {
size_t n = 0; // index of codepoints
size_t i; // index of text
uint32_t b0, b1, b2, b3 = 0;
for (i = 0; i < byte_count;) {
size_t bytes = utf8_codepoint_size(text[i]);
switch (bytes) {
case 1:
codepoints[n] = (text[i] & ~UTF8_ONE_BYTE_MASK);
break;
case 2:
b0 = (text[i] & ~UTF8_TWO_BYTE_MASK);
b1 = (text[i + 1] & UTF8_CONTINUATION_MASK);
codepoints[n] = (b0 << 6) | b1;
break;
case 3:
b0 = (text[i] & ~UTF8_THREE_BYTE_MASK);
b1 = (text[i + 1] & UTF8_CONTINUATION_MASK);
b2 = (text[i + 2] & UTF8_CONTINUATION_MASK);
codepoints[n] = (b0 << 12) | (b1 << 6) | b2;
break;
case 4:
b0 = (text[i] & ~UTF8_FOUR_BYTE_MASK);
b1 = (text[i + 1] & UTF8_CONTINUATION_MASK);
b2 = (text[i + 2] & UTF8_CONTINUATION_MASK);
b3 = (text[i + 3] & UTF8_CONTINUATION_MASK);
codepoints[n] = (b0 << 18) | (b1 << 12) | (b2 << 6) | b3;
break;
}
i += bytes;
n++;
}
return n;
}