#include "sys/encoding.h" #include "base/allocator.h" #include #include static bool utf8_codepoint(const unsigned char *input, size_t length, size_t *used, uint32_t *codepoint) { const unsigned char first = input[0]; size_t width, index; uint32_t value; if (first < 0x80) { width = 1; value = first; } else if (first >= 0xc2 && first <= 0xdf) { width = 2; value = first & 0x1f; } else if (first >= 0xe0 && first <= 0xef) { width = 3; value = first & 0x0f; } else if (first >= 0xf0 && first <= 0xf4) { width = 4; value = first & 0x07; } else { return false; } if (width > length) { return false; } for (index = 1; index < width; ++index) { if ((input[index] & 0xc0) != 0x80) { return false; } value = (value << 6) | (input[index] & 0x3f); } if ((width == 3 && ((first == 0xe0 && input[1] < 0xa0) || (first == 0xed && input[1] >= 0xa0))) || (width == 4 && ((first == 0xf0 && input[1] < 0x90) || (first == 0xf4 && input[1] >= 0x90)))) { return false; } *used = width; *codepoint = value; return true; } tiny_encoding_status_t tiny_utf8_to_utf16(const char *input, size_t input_length, uint16_t **output, size_t *output_length) { size_t offset = 0, units = 0; uint16_t *result; if (output == NULL || output_length == NULL) { return TINY_ENCODING_INVALID; } *output = NULL; *output_length = 0; if (input == NULL && input_length != 0) { return TINY_ENCODING_INVALID; } while (offset < input_length) { size_t used; uint32_t point; if (!utf8_codepoint( (const unsigned char *)input + offset, input_length - offset, &used, &point)) { return TINY_ENCODING_INVALID; } if (units > SIZE_MAX - (point >= 0x10000 ? 2U : 1U)) { return TINY_ENCODING_OVERFLOW; } units += point >= 0x10000 ? 2U : 1U; offset += used; } if (units == SIZE_MAX || units + 1 > SIZE_MAX / sizeof(*result)) { return TINY_ENCODING_OVERFLOW; } result = malloc((units + 1) * sizeof(*result)); if (result == NULL) { return TINY_ENCODING_NO_MEMORY; } offset = 0; units = 0; while (offset < input_length) { size_t used; uint32_t point; (void)utf8_codepoint( (const unsigned char *)input + offset, input_length - offset, &used, &point); if (point < 0x10000) { result[units++] = (uint16_t)point; } else { point -= 0x10000; result[units++] = (uint16_t)(0xd800 + (point >> 10)); result[units++] = (uint16_t)(0xdc00 + (point & 0x3ff)); } offset += used; } result[units] = 0; *output = result; *output_length = units; return TINY_ENCODING_OK; } tiny_encoding_status_t tiny_utf16_to_utf8(const uint16_t *input, size_t input_length, char **output, size_t *output_length) { size_t index = 0, bytes = 0; char *result; if (output == NULL || output_length == NULL) { return TINY_ENCODING_INVALID; } *output = NULL; *output_length = 0; if (input == NULL && input_length != 0) { return TINY_ENCODING_INVALID; } while (index < input_length) { uint32_t point = input[index++]; if (point >= 0xd800 && point <= 0xdbff) { if (index >= input_length || input[index] < 0xdc00 || input[index] > 0xdfff) { return TINY_ENCODING_INVALID; } point = 0x10000 + ((point - 0xd800) << 10) + (input[index++] - 0xdc00); } else if (point >= 0xdc00 && point <= 0xdfff) { return TINY_ENCODING_INVALID; } if (bytes > SIZE_MAX - (point < 0x80 ? 1U : point < 0x800 ? 2U : point < 0x10000 ? 3U : 4U)) { return TINY_ENCODING_OVERFLOW; } bytes += point < 0x80 ? 1U : point < 0x800 ? 2U : point < 0x10000 ? 3U : 4U; } if (bytes == SIZE_MAX) { return TINY_ENCODING_OVERFLOW; } result = malloc(bytes + 1); if (result == NULL) { return TINY_ENCODING_NO_MEMORY; } index = 0; bytes = 0; while (index < input_length) { uint32_t point = input[index++]; if (point >= 0xd800 && point <= 0xdbff) { point = 0x10000 + ((point - 0xd800) << 10) + (input[index++] - 0xdc00); } if (point < 0x80) { result[bytes++] = (char)point; } else if (point < 0x800) { result[bytes++] = (char)(0xc0 | (point >> 6)); result[bytes++] = (char)(0x80 | (point & 0x3f)); } else if (point < 0x10000) { result[bytes++] = (char)(0xe0 | (point >> 12)); result[bytes++] = (char)(0x80 | ((point >> 6) & 0x3f)); result[bytes++] = (char)(0x80 | (point & 0x3f)); } else { result[bytes++] = (char)(0xf0 | (point >> 18)); result[bytes++] = (char)(0x80 | ((point >> 12) & 0x3f)); result[bytes++] = (char)(0x80 | ((point >> 6) & 0x3f)); result[bytes++] = (char)(0x80 | (point & 0x3f)); } } result[bytes] = '\0'; *output = result; *output_length = bytes; return TINY_ENCODING_OK; }