/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include namespace ams::util { namespace { constexpr inline const u8 CodePointByteLengthTable[0x100] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; constexpr ALWAYS_INLINE size_t GetCodePointByteLength(u8 c) { return CodePointByteLengthTable[c]; } constexpr ALWAYS_INLINE bool IsValidTail(u8 c) { return (c & 0xC0) == 0x80; } constexpr inline bool VerifyCode(const u8 *code, size_t size) { switch (size) { case 1: break; case 2: if (!IsValidTail(code[1])) { return false; } break; case 3: if (code[0] == 0xE0 && (code[1] & 0x20) == 0x00) { return false; } if (code[0] == 0xED && (code[1] & 0x20) != 0x00) { return false; } if (!IsValidTail(code[1]) || !IsValidTail(code[2])) { return false; } break; case 4: if (code[0] == 0xF0 && (code[1] & 0x30) == 0x00) { return false; } if (code[0] == 0xF4 && (code[1] & 0x30) != 0x00) { return false; } if (!IsValidTail(code[1]) || !IsValidTail(code[2]) || !IsValidTail(code[3])) { return false; } break; default: return false; } return true; } } bool VerifyUtf8String(const char *str, size_t size) { return GetCodePointCountOfUtf8String(str, size) != -1; } int GetCodePointCountOfUtf8String(const char *str, size_t size) { /* Check pre-conditions. */ AMS_ASSERT(str != nullptr); AMS_ASSERT(size > 0); /* Parse codepoints. */ int count = 0; while (size > 0) { /* Get and check the current codepoint. */ const u8 *code = reinterpret_cast(str); const size_t code_size = GetCodePointByteLength(code[0]); if (code_size > size || !VerifyCode(code, code_size)) { return -1; } /* Advance. */ str += code_size; size -= code_size; /* Increment count. */ ++count; } return count; } }