mirror of https://github.com/meshcore-dev/MeshCore
committed by
GitHub
4 changed files with 103 additions and 7 deletions
@ -0,0 +1,56 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
#include <stdint.h> |
||||
|
|
||||
|
namespace mesh { |
||||
|
|
||||
|
inline bool isUtf8Continuation(uint8_t byte) { |
||||
|
return (byte & 0xC0) == 0x80; |
||||
|
} |
||||
|
|
||||
|
inline size_t validUtf8PrefixLength(const char* text, size_t max_bytes) { |
||||
|
if (text == nullptr) return 0; |
||||
|
|
||||
|
size_t offset = 0; |
||||
|
while (text[offset] != '\0') { |
||||
|
const uint8_t first = static_cast<uint8_t>(text[offset]); |
||||
|
size_t sequence_length = 0; |
||||
|
|
||||
|
if (first <= 0x7F) { |
||||
|
sequence_length = 1; |
||||
|
} else if (first >= 0xC2 && first <= 0xDF) { |
||||
|
sequence_length = 2; |
||||
|
} else if (first >= 0xE0 && first <= 0xEF) { |
||||
|
sequence_length = 3; |
||||
|
} else if (first >= 0xF0 && first <= 0xF4) { |
||||
|
sequence_length = 4; |
||||
|
} else { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (offset + sequence_length > max_bytes) break; |
||||
|
|
||||
|
bool complete = true; |
||||
|
for (size_t i = 1; i < sequence_length; i++) { |
||||
|
if (text[offset + i] == '\0' || !isUtf8Continuation(static_cast<uint8_t>(text[offset + i]))) { |
||||
|
complete = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (!complete) break; |
||||
|
|
||||
|
if (sequence_length == 3) { |
||||
|
const uint8_t second = static_cast<uint8_t>(text[offset + 1]); |
||||
|
if ((first == 0xE0 && second < 0xA0) || (first == 0xED && second > 0x9F)) break; |
||||
|
} else if (sequence_length == 4) { |
||||
|
const uint8_t second = static_cast<uint8_t>(text[offset + 1]); |
||||
|
if ((first == 0xF0 && second < 0x90) || (first == 0xF4 && second > 0x8F)) break; |
||||
|
} |
||||
|
|
||||
|
offset += sequence_length; |
||||
|
} |
||||
|
return offset; |
||||
|
} |
||||
|
|
||||
|
} // namespace mesh
|
||||
@ -0,0 +1,38 @@ |
|||||
|
#include <gtest/gtest.h> |
||||
|
|
||||
|
#include <helpers/UTF8Helpers.h> |
||||
|
|
||||
|
TEST(UTF8Helpers, KeepsCompleteNameWithinLimit) { |
||||
|
const char* name = "Example RPT 🔋🇵🇱"; |
||||
|
|
||||
|
EXPECT_EQ(24u, mesh::validUtf8PrefixLength(name, 24)); |
||||
|
} |
||||
|
|
||||
|
TEST(UTF8Helpers, StopsBeforeCodePointCrossingLimit) { |
||||
|
const char* name = "Example RPT 🔋🇵🇱"; |
||||
|
|
||||
|
EXPECT_EQ(20u, mesh::validUtf8PrefixLength(name, 23)); |
||||
|
} |
||||
|
|
||||
|
TEST(UTF8Helpers, RejectsMalformedAndTruncatedSequences) { |
||||
|
const char overlong[] = {'A', static_cast<char>(0xC0), static_cast<char>(0xAF), 0}; |
||||
|
const char surrogate[] = {'A', static_cast<char>(0xED), static_cast<char>(0xA0), static_cast<char>(0x80), 0}; |
||||
|
const char out_of_range[] = {'A', static_cast<char>(0xF4), static_cast<char>(0x90), static_cast<char>(0x80), static_cast<char>(0x80), 0}; |
||||
|
const char truncated[] = {'A', static_cast<char>(0xF0), static_cast<char>(0x9F), 0}; |
||||
|
|
||||
|
EXPECT_EQ(1u, mesh::validUtf8PrefixLength(overlong, sizeof(overlong))); |
||||
|
EXPECT_EQ(1u, mesh::validUtf8PrefixLength(surrogate, sizeof(surrogate))); |
||||
|
EXPECT_EQ(1u, mesh::validUtf8PrefixLength(out_of_range, sizeof(out_of_range))); |
||||
|
EXPECT_EQ(1u, mesh::validUtf8PrefixLength(truncated, sizeof(truncated))); |
||||
|
} |
||||
|
|
||||
|
TEST(UTF8Helpers, RejectsUnexpectedContinuationByte) { |
||||
|
const char invalid[] = {'A', static_cast<char>(0x80), 'B', 0}; |
||||
|
|
||||
|
EXPECT_EQ(1u, mesh::validUtf8PrefixLength(invalid, sizeof(invalid))); |
||||
|
} |
||||
|
|
||||
|
int main(int argc, char **argv) { |
||||
|
::testing::InitGoogleTest(&argc, argv); |
||||
|
return RUN_ALL_TESTS(); |
||||
|
} |
||||
Loading…
Reference in new issue