LCOV - code coverage report
Current view: top level - http/util - utf8.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 86.5 % 37 32
Test Date: 2026-02-20 15:38:22 Functions: 100.0 % 1 1

            Line data    Source code
       1              : #ifndef THINGER_HTTP_UTF8_HPP
       2              : #define THINGER_HTTP_UTF8_HPP
       3              : 
       4              : #include <cstddef>
       5              : #include <cstdint>
       6              : #include <string>
       7              : #include <string_view>
       8              : #include <fstream>
       9              : 
      10              : namespace thinger::http::utf8 {
      11              : 
      12              : // UTF-8 validation based on Unicode 6.0 Table 3-7 (Well-Formed UTF-8 Byte Sequences)
      13              : // http://www.unicode.org/versions/Unicode6.0.0/ch03.pdf - page 94
      14              : //
      15              : // Returns true if the data is valid UTF-8, false otherwise.
      16           32 : inline bool is_valid(const uint8_t* data, size_t len) {
      17          394 :     while (len > 0) {
      18          364 :         const uint8_t b1 = data[0];
      19              : 
      20              :         size_t bytes;
      21              : 
      22              :         // U+0000..U+007F: 00..7F
      23          364 :         if (b1 <= 0x7F) {
      24          352 :             bytes = 1;
      25              : 
      26              :         // U+0080..U+07FF: C2..DF, 80..BF
      27           12 :         } else if (b1 >= 0xC2 && b1 <= 0xDF) {
      28            2 :             if (len < 2 || data[1] < 0x80 || data[1] > 0xBF) return false;
      29            2 :             bytes = 2;
      30              : 
      31              :         // U+0800..U+FFFF: 3-byte sequences
      32           10 :         } else if (b1 >= 0xE0 && b1 <= 0xEF) {
      33            6 :             if (len < 3) return false;
      34            6 :             const uint8_t b2 = data[1];
      35            6 :             const uint8_t b3 = data[2];
      36            6 :             if (b3 < 0x80 || b3 > 0xBF) return false;
      37              : 
      38            6 :             if (b1 == 0xE0) {
      39              :                 // U+0800..U+0FFF: E0, A0..BF, 80..BF
      40            0 :                 if (b2 < 0xA0 || b2 > 0xBF) return false;
      41            6 :             } else if (b1 == 0xED) {
      42              :                 // U+D000..U+D7FF: ED, 80..9F, 80..BF (surrogates excluded)
      43            0 :                 if (b2 < 0x80 || b2 > 0x9F) return false;
      44              :             } else {
      45              :                 // U+1000..U+CFFF, U+E000..U+FFFF: E1..EC/EE..EF, 80..BF, 80..BF
      46            6 :                 if (b2 < 0x80 || b2 > 0xBF) return false;
      47              :             }
      48            6 :             bytes = 3;
      49              : 
      50              :         // U+10000..U+10FFFF: 4-byte sequences
      51           10 :         } else if (b1 >= 0xF0 && b1 <= 0xF4) {
      52            2 :             if (len < 4) return false;
      53            2 :             const uint8_t b2 = data[1];
      54            2 :             const uint8_t b3 = data[2];
      55            2 :             const uint8_t b4 = data[3];
      56            2 :             if (b3 < 0x80 || b3 > 0xBF) return false;
      57            2 :             if (b4 < 0x80 || b4 > 0xBF) return false;
      58              : 
      59            2 :             if (b1 == 0xF0) {
      60              :                 // U+10000..U+3FFFF: F0, 90..BF, 80..BF, 80..BF
      61            2 :                 if (b2 < 0x90 || b2 > 0xBF) return false;
      62            0 :             } else if (b1 == 0xF4) {
      63              :                 // U+100000..U+10FFFF: F4, 80..8F, 80..BF, 80..BF
      64            0 :                 if (b2 < 0x80 || b2 > 0x8F) return false;
      65              :             } else {
      66              :                 // U+40000..U+FFFFF: F1..F3, 80..BF, 80..BF, 80..BF
      67            0 :                 if (b2 < 0x80 || b2 > 0xBF) return false;
      68              :             }
      69            2 :             bytes = 4;
      70              : 
      71            2 :         } else {
      72              :             // Invalid leading byte (C0, C1, F5..FF, or continuation byte 80..BF)
      73            2 :             return false;
      74              :         }
      75              : 
      76          362 :         data += bytes;
      77          362 :         len -= bytes;
      78              :     }
      79              : 
      80           30 :     return true;
      81              : }
      82              : 
      83              : // Convenience overload for string_view
      84              : inline bool is_valid(std::string_view sv) {
      85              :     return is_valid(reinterpret_cast<const uint8_t*>(sv.data()), sv.size());
      86              : }
      87              : 
      88              : // Check if a file contains valid UTF-8 content
      89              : inline bool file_is_valid(const std::string& file_path) {
      90              :     std::ifstream input(file_path, std::ifstream::binary);
      91              :     if (!input.is_open()) return false;
      92              : 
      93              :     char buffer[4096];
      94              :     while (input.read(buffer, sizeof(buffer)) || input.gcount() > 0) {
      95              :         if (!is_valid(reinterpret_cast<const uint8_t*>(buffer), static_cast<size_t>(input.gcount()))) {
      96              :             return false;
      97              :         }
      98              :         if (input.eof()) break;
      99              :     }
     100              : 
     101              :     return true;
     102              : }
     103              : 
     104              : } // namespace thinger::http::utf8
     105              : 
     106              : #endif // THINGER_HTTP_UTF8_HPP
        

Generated by: LCOV version 2.0-1