diff --git a/CMakeLists.txt b/CMakeLists.txt index 32a547dc3..b55852194 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -421,6 +421,7 @@ set(LIBFASTFETCH_SRC src/common/impl/path.c src/common/impl/FFPlatform.c src/common/impl/smbios.c + src/common/impl/strutil.c src/detection/bluetoothradio/bluetoothradio.c src/detection/bootmgr/bootmgr.c src/detection/chassis/chassis.c diff --git a/src/common/impl/strutil.c b/src/common/impl/strutil.c new file mode 100644 index 000000000..3a801f7e8 --- /dev/null +++ b/src/common/impl/strutil.c @@ -0,0 +1,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); +} diff --git a/src/common/strutil.h b/src/common/strutil.h index b8174b3fe..00a8a0839 100644 --- a/src/common/strutil.h +++ b/src/common/strutil.h @@ -82,84 +82,9 @@ static inline bool ffCharIsDigit(char c) { // Parse one UTF-8 character, returning consumed byte count and display width. // Invalid / incomplete sequence falls back to one-byte width=1. // If the Unicode codepoint is non-printable, width becomes 0. -static inline 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; - } +uint8_t ffUtf8CharLenWidth(const char* str, uint32_t length, uint8_t* width); - 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; -} - -static inline 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); -} +uint32_t ffUtf8StrWidth(const char* str, uint32_t length); static inline bool ffCharIsHexDigit(char c) { return ffCharIsDigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');