LCOV - code coverage report
Current view: top level - src - ctype.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 178 191 93.2 %
Date: 2022-06-15 20:16:21 Functions: 45 53 84.9 %
Branches: 53 72 73.6 %

           Branch data     Line data    Source code
       1                 :            : #ifdef RUN_TESTS
       2                 :            : # include <gtest/gtest.h>
       3                 :            : #endif
       4                 :            : /** @file ctype.cc
       5                 :            :  * @brief Functions for identifying and converting unicode characters and representation forms.
       6                 :            :  */
       7                 :            : 
       8                 :            : #include <bitset>
       9                 :            : #include <algorithm>
      10                 :            : #include <cstring>
      11                 :            : #include <array>
      12                 :            : 
      13                 :            : /*
      14                 :            : #if __cplusplus <= 201800L
      15                 :            : # include <codecvt>
      16                 :            : # include <locale>
      17                 :            : #endif
      18                 :            : */
      19                 :            : 
      20                 :            : #include "data/unidata.cc"
      21                 :            : #include "ctype.hh"
      22                 :            : #include "log2.hh"
      23                 :            : 
      24                 :            : /** A bitset structure that can be initialized at compile-time
      25                 :            :  * and read at both compile-time and run-time.
      26                 :            :  * @param N number of bits that this set can store.
      27                 :            :  */
      28                 :            : template<std::size_t N>
      29                 :            : class constexpr_bitset
      30                 :            : {
      31                 :            : public:
      32                 :            :     using elem_t = unsigned long; ///< Internal type of atoms in this array.
      33                 :            :     static constexpr std::size_t WordSize = sizeof(elem_t) * 8;          ///< Number of bits per elem_t.
      34                 :            :     static constexpr std::size_t nwords   = (N + WordSize-1) / WordSize; ///< Number of elem_t's stored.
      35                 :            :     static constexpr elem_t      full = ~elem_t{}; ///< Bitmask of everything set
      36                 :            :     elem_t data[nwords] {}; ///< Storage of data.
      37                 :            : public:
      38                 :            :     /** Set bit at given index.
      39                 :            :      * @param idx Index to set. Counting starts from zero, and must be less than N.
      40                 :            :      */
      41                 :            :     constexpr void set(std::size_t idx)
      42                 :            :     {
      43                 :            :         data[idx/WordSize] |= elem_t(1) << (idx % WordSize);
      44                 :            :     }
      45                 :            :     /** Test bit at given index.
      46                 :            :      * @param idx Index to read. Counting starts from zero, and must be less than N.
      47                 :            :      * @returns Whether the given bit is set.
      48                 :            :      */
      49                 :            :     constexpr bool test(std::size_t idx) const
      50                 :            :     {
      51                 :            :         return data[idx/WordSize] & (elem_t(1) << (idx % WordSize));
      52                 :            :     }
      53                 :            : 
      54                 :            :     /** Set a range of bits.
      55                 :            :      * @param idx   First bit to set
      56                 :            :      * @param count Count of bits to set
      57                 :            :      */
      58                 :            :     constexpr void set_n(std::size_t idx, std::size_t count)
      59                 :            :     {
      60                 :            :         std::size_t widx    = idx / WordSize;
      61                 :            :         std::size_t woffs   = idx % WordSize;
      62                 :            :         std::size_t rembits = WordSize - woffs;
      63                 :            : 
      64                 :            :         while(count >= rembits)
      65                 :            :         {
      66                 :            :             data[widx++] |= full << woffs;
      67                 :            :             count  -= rembits;
      68                 :            :             woffs   = 0;
      69                 :            :             rembits = WordSize;
      70                 :            :         }
      71                 :            :         if(count > 0)
      72                 :            :         {
      73                 :            :             data[widx] |= ~(full << count) << woffs;
      74                 :            :         }
      75                 :            :     }
      76                 :            : };
      77                 :            : 
      78                 :            : /** An array structure that can be initialized at compile-time
      79                 :            :  * and read at both compile-time and run-time.
      80                 :            :  * The number of elements and the bit-width of each element
      81                 :            :  * must be specified at compile-time.
      82                 :            :  * All elements have a maximum bit-width,
      83                 :            :  * and exactly that number of bits is used to store the data.
      84                 :            :  */
      85                 :            : template<unsigned nwords, unsigned bits_per_elem>
      86                 :            : class bitval_array
      87                 :            : {
      88                 :            : public:
      89                 :            :     using elem_t = unsigned long; ///< Internal type of atoms in this array.
      90                 :            :     static constexpr unsigned elems_per_mapword = sizeof(elem_t)*8 / bits_per_elem; ///< Number of elements in each atom.
      91                 :            :     static constexpr unsigned n_mapwords        = (nwords + elems_per_mapword-1) / elems_per_mapword; ///< Number of atoms needed to store data.
      92                 :            : public:
      93                 :            :     elem_t data[n_mapwords] {}; ///< Storage of data.
      94                 :            : 
      95                 :            :     /** Set value at given index.
      96                 :            :      *
      97                 :            :      * @param index Index to set at. Counting starts from zero, and must be less than nwords.
      98                 :            :      * @param value Value to assign to that index.
      99                 :            :      */
     100                 :            :     constexpr void set(std::size_t index, unsigned value)
     101                 :            :     {
     102                 :            :         data[get_idx(index)] |= elem_t(value) << ((index % elems_per_mapword) * bits_per_elem);
     103                 :            :     }
     104                 :            :     /** Read value at given index.
     105                 :            :      *
     106                 :            :      * @param index Index to read at.
     107                 :            :      * @returns The value at given index.
     108                 :            :      **/
     109                 :     147067 :     constexpr unsigned get(std::size_t index) const
     110                 :            :     {
     111                 :     147067 :         return get_from(data[get_idx(index)], index);
     112                 :            :     }
     113                 :            :     /** Determine the internal array index at which the given index is stored.
     114                 :            :      * @param index Index to query.
     115                 :            :      * @returns The internal array index.
     116                 :            :      */
     117                 :     147067 :     static constexpr unsigned get_idx(std::size_t index)
     118                 :            :     {
     119                 :     147067 :         return index / elems_per_mapword;
     120                 :            :     }
     121                 :            :     /** Read value from internal element.
     122                 :            :      * @param elem  Copy of the internal element that stores the value.
     123                 :            :      * @param index Index from which to read.
     124                 :            :      * @returns The value at given index.
     125                 :            :      */
     126                 :     147067 :     static constexpr unsigned get_from(elem_t elem, std::size_t index)
     127                 :            :     {
     128                 :     147067 :         return (elem >> ((index % elems_per_mapword) * bits_per_elem))
     129                 :     147067 :              % (1u << bits_per_elem);
     130                 :            :     }
     131                 :            : };
     132                 :            : 
     133                 :            : #define USE_MAP2
     134                 :            : 
     135                 :            : /** A compile-time initialized bitset structure
     136                 :            :  * that stores the data as compactly as possible. */
     137                 :            : template<std::size_t N, std::size_t maxdim = 252, std::size_t maxdim2 = 84>
     138                 :            : class compressed_bitset
     139                 :            : {
     140                 :            :     using reftype = constexpr_bitset<N>;
     141                 :            :     using elem_t  = typename reftype::elem_t;
     142                 :            :     static constexpr std::size_t WordSize = reftype::WordSize;
     143                 :            :     static constexpr std::size_t nwords   = reftype::nwords;
     144                 :            :     static constexpr elem_t      full     = reftype::full;
     145                 :            : 
     146                 :            :     using mapelem_t = elem_t;//unsigned char;
     147                 :            :     static constexpr unsigned bits_per_elem     = log2ceil(maxdim + 2);
     148                 :            :     static_assert( maxdim <= (1u<<bits_per_elem) );
     149                 :            : 
     150                 :            :     // For "nwords", specify whether that is
     151                 :            :     //               fully 0
     152                 :            :     //               fully 1
     153                 :            :     //               something else: included in container
     154                 :            :     elem_t container[maxdim] {};
     155                 :            : 
     156                 :            :     using map1_type = bitval_array<nwords, bits_per_elem>;
     157                 :            : 
     158                 :            : #ifdef USE_MAP2
     159                 :            :     static constexpr unsigned bits_per_elem2    = log2ceil(maxdim2);
     160                 :            :     static_assert( maxdim2 <= (1u<<bits_per_elem2) );
     161                 :            :     typename map1_type::elem_t container2[maxdim2] {};
     162                 :            :     bitval_array<map1_type::n_mapwords, bits_per_elem2> map2;
     163                 :            : #else
     164                 :            :     map1_type map1;
     165                 :            : #endif
     166                 :            : 
     167                 :            : public:
     168                 :            :     /** Initializes the compressed set using a constexpr_bitset.
     169                 :            :      * @param b An instance of constexpr_bitset to initialized using.
     170                 :            :      */
     171                 :            :     constexpr void init(reftype&& b)
     172                 :            :     {
     173                 :            : #ifdef USE_MAP2
     174                 :            :         map1_type map1{};
     175                 :            : #endif
     176                 :            :         unsigned dim=0;
     177                 :            :         for(unsigned a=0; a<nwords; ++a)
     178                 :            :             if(b.data[a] == 0)
     179                 :            :                 map1.set(a, 0);
     180                 :            :             else if(b.data[a] == full)
     181                 :            :                 map1.set(a, 1);
     182                 :            :             else
     183                 :            :             {
     184                 :            :                 container[dim] = b.data[a];
     185                 :            :                 for(unsigned p=0; p<=dim; ++p)
     186                 :            :                     if(container[p] == b.data[a])
     187                 :            :                     {
     188                 :            :                         map1.set(a, p+2);
     189                 :            :                         if(p == dim) ++dim;
     190                 :            :                         break;
     191                 :            :                     }
     192                 :            :             }
     193                 :            : #ifdef USE_MAP2
     194                 :            :         // next, compress map1 similarly.
     195                 :            : 
     196                 :            :         unsigned dim2=0;
     197                 :            :         for(unsigned a=0; a<map1_type::n_mapwords; ++a)
     198                 :            :         {
     199                 :            :             container2[dim2] = map1.data[a];
     200                 :            :             for(unsigned p=0; p<=dim2; ++p)
     201                 :            :                 if(container2[p] == map1.data[a])
     202                 :            :                 {
     203                 :            :                     map2.set(a, p);
     204                 :            :                     if(p == dim2) ++dim2;
     205                 :            :                     break;
     206                 :            :                 }
     207                 :            :         }
     208                 :            : #endif
     209                 :            :     }
     210                 :            : 
     211                 :            :     /** Tests whether the given index is set.
     212                 :            :      * @param idx Index to set
     213                 :            :      * @returns True if the given index is set.
     214                 :            :      */
     215                 :            :     bool test(std::size_t idx) const;// __attribute__((noinline));
     216                 :            : };
     217                 :            : 
     218                 :            : template<std::size_t N, std::size_t maxdim, std::size_t maxdim2>
     219                 :     147067 : bool compressed_bitset<N,maxdim,maxdim2>::test(std::size_t idx) const
     220                 :            : {
     221         [ +  - ]:     147067 :     if(idx >= N) return false;
     222                 :            : 
     223                 :            : #ifdef USE_MAP2
     224                 :     147067 :     unsigned w1 = map2.get(map1_type::get_idx(idx/WordSize));
     225                 :     147067 :     unsigned w  = map1_type::get_from(container2[w1], idx/WordSize);
     226                 :            : #else
     227                 :            :     unsigned w = map1.get(idx/WordSize);
     228                 :            : #endif
     229         [ +  + ]:     147067 :     if(w & ~1u) return container[w-2] & (elem_t(1) << (idx % WordSize));
     230                 :     146868 :     else        return w&1;
     231                 :            : }
     232                 :            : 
     233                 :            : constexpr unsigned cap = 0x10FFFE; // Co_table includes 0x10fffd
     234                 :            : 
     235                 :            : template<std::size_t maxdim, std::size_t maxdim2>
     236                 :            : static constexpr auto BuildIntervals(std::initializer_list<std::pair<const std::pair<char32_t,char32_t>*, std::size_t>> arrays)
     237                 :            : {
     238                 :            :     constexpr_bitset<cap> set;
     239                 :            :     for(auto a: arrays)
     240                 :            :         for(std::size_t n=0; n<a.second; ++n)
     241                 :            :             set.set_n(a.first[n].first, a.first[n].second - a.first[n].first + 1);
     242                 :            :     compressed_bitset<cap,maxdim,maxdim2> result;
     243                 :            :     result.init(std::move(set));
     244                 :            :     return result;
     245                 :            : }
     246                 :            : 
     247                 :            : /* Much of the following code is autogenerated. */
     248                 :            : 
     249                 :            : #define B(c) /*std::pair<const std::pair<char32_t,unsigned>*, std::size_t>*/{&c##_table[0],std::size(c##_table)}
     250                 :          3 : bool isupper(char32_t c)
     251                 :            : {
     252                 :          3 :     static constexpr auto intervals = BuildIntervals<62,21>({B(Lu)});
     253                 :          3 :     return intervals.test(c);
     254                 :            : }
     255                 :          3 : bool islower(char32_t c)
     256                 :            : {
     257                 :          3 :     static constexpr auto intervals = BuildIntervals<69,24>({B(Ll)});
     258                 :          3 :     return intervals.test(c);
     259                 :            : }
     260                 :          5 : bool isalpha(char32_t c)
     261                 :            : {
     262                 :          5 :     static constexpr auto intervals = BuildIntervals<256,94>({B(Mc),B(Me),B(Mn),B(Lm),B(Lt),B(Lo),B(Ll),B(Lu)});
     263                 :          5 :     return intervals.test(c);
     264                 :            : }
     265                 :          5 : bool isalnum(char32_t c)
     266                 :            : {
     267                 :          5 :     static constexpr auto intervals = BuildIntervals<275,100>({B(Mc),B(Me),B(Mn),B(Lm),B(Lt),B(Lo),B(Ll),B(Lu),B(Nl),B(No),B(Nd)});
     268                 :          5 :     return intervals.test(c);
     269                 :            : }
     270                 :          4 : bool isalnum_(char32_t c)
     271                 :            : {
     272                 :          4 :     static constexpr std::array cd2_table{ std::pair<char32_t,char32_t>{0x200C,0x200D} };
     273                 :          4 :     static constexpr auto intervals = BuildIntervals<259,95>({B(Lm),B(Lt),B(Lo),B(Ll),B(Lu),B(Pc),B(Nl),B(Mn),B(Mc),B(Nd),B(cd2)});
     274                 :          4 :     return intervals.test(c);
     275                 :            : }
     276                 :          5 : bool isdigit(char32_t c)
     277                 :            : {
     278                 :          5 :     static constexpr auto intervals = BuildIntervals<11,24>({B(Nd)});
     279                 :          5 :     return intervals.test(c);
     280                 :            : }
     281                 :          5 : bool isxdigit(char32_t c)
     282                 :            : {
     283                 :          5 :     static constexpr std::array hex_table{ std::pair<char32_t,char32_t>{U'a',U'f'}, std::pair<char32_t,char32_t>{U'A',U'F'} };
     284                 :          5 :     static constexpr auto intervals = BuildIntervals<12,24>({B(Nd),B(hex)});
     285                 :          5 :     return intervals.test(c);
     286                 :            : }
     287                 :          2 : bool ispunct(char32_t c)
     288                 :            : {
     289                 :          2 :     static constexpr auto intervals = BuildIntervals<108,48>({B(Pf),B(Pi),B(Pc),B(Pd),B(Pe),B(Ps),B(Po)});
     290                 :          2 :     return intervals.test(c);
     291                 :            : }
     292                 :            : static constexpr std::array bl_table{ std::pair<char32_t,char32_t>{U'\t',U'\t'} };
     293                 :            : static constexpr std::array ws_table{ std::pair<char32_t,char32_t>{U'\r',U'\r'},
     294                 :            :                                       std::pair<char32_t,char32_t>{U'\n',U'\n'},
     295                 :            :                                       std::pair<char32_t,char32_t>{U'\f',U'\f'} };
     296                 :          5 : bool isspace(char32_t c)
     297                 :            : {
     298                 :          5 :     static constexpr auto intervals = BuildIntervals<6,6>({B(Zp),B(Zl),B(Zs),B(bl),B(ws)});
     299                 :          5 :     return intervals.test(c);
     300                 :            : }
     301                 :          6 : bool isspace_punct(char32_t c)
     302                 :            : {
     303                 :          6 :     static constexpr auto intervals = BuildIntervals<108,48>({B(Zp),B(Zl),B(Zs),B(bl),B(ws),B(Pf),B(Pi),B(Pc),B(Pd),B(Pe),B(Ps),B(Po)});
     304                 :          6 :     return intervals.test(c);
     305                 :            : }
     306                 :          5 : bool isblank(char32_t c)
     307                 :            : {
     308                 :          5 :     static constexpr auto intervals = BuildIntervals<6,6>({B(Zs),B(bl)});
     309                 :          5 :     return intervals.test(c);
     310                 :            : }
     311                 :          0 : bool isctrl(char32_t c)
     312                 :            : {
     313                 :          0 :     static constexpr auto intervals = BuildIntervals<19,15>({B(Co),B(Cs),B(Cf),B(Cc),B(Zl),B(Zp)});
     314                 :          0 :     return intervals.test(c);
     315                 :            : }
     316                 :          2 : bool isprint(char32_t c)
     317                 :            : {
     318                 :          2 :     static constexpr auto intervals = BuildIntervals<268,102>({B(Co),B(Nl),B(Mc),B(Me),B(Mn),B(Lm),B(Lt),B(Pf),B(No),B(Pi),
     319                 :            :     B(Lo),B(So),B(Ll),B(Pc),B(Sk),B(Lu),B(Nd),B(Pd),B(Sm),B(Pe),B(Ps),B(Sc),B(Po),B(Zs)});
     320                 :          2 :     return intervals.test(c);
     321                 :            : }
     322                 :          3 : bool isgraph(char32_t c)
     323                 :            : {
     324                 :          3 :     static constexpr auto intervals = BuildIntervals<270,102>({B(Co),B(Nl),B(Mc),B(Me),B(Mn),B(Lm),B(Lt),B(Pf),B(No),B(Pi),
     325                 :            :     B(Lo),B(So),B(Ll),B(Pc),B(Sk),B(Lu),B(Nd),B(Pd),B(Sm),B(Pe),B(Ps),B(Sc),B(Po)});
     326                 :          3 :     return intervals.test(c);
     327                 :            : }
     328                 :          0 : bool isnotword(char32_t c)
     329                 :            : {
     330                 :          0 :     static constexpr auto intervals = BuildIntervals<112,56>({B(Co),B(Cs),B(Zp),B(Zl),B(Pf),B(Cf),B(Pi),
     331                 :            :     B(Pc),B(Pd),B(Pe),B(Ps),B(Po),B(Zs),B(Cc)});
     332                 :          0 :     return intervals.test(c);
     333                 :            : }
     334                 :     147014 : bool isdouble(char32_t c)
     335                 :            : {
     336                 :     147014 :     static constexpr auto intervals = BuildIntervals<64,34>({B(width)});
     337                 :     147014 :     return intervals.test(c);
     338                 :            : }
     339                 :            : 
     340                 :            : 
     341                 :        120 : static bool casecomp(const std::tuple<char32_t,char32_t,int>& v, char32_t c)
     342                 :            : {
     343                 :        120 :     return std::get<0>(v) < c;
     344                 :            : }
     345                 :            : template<typename I>
     346                 :            : static char32_t caseconv(I begin, I end, char32_t c) __attribute__((noinline));
     347                 :            : 
     348                 :            : template<typename I>
     349                 :         12 : static char32_t caseconv(I begin, I end, char32_t c)
     350                 :            : {
     351                 :         12 :     auto i = std::lower_bound(begin, end, c, casecomp);
     352   [ +  -  +  +  :         12 :     if(i == end || c < std::get<0>(*i) || c > std::get<1>(*i))
                   -  + ]
     353                 :            :     {
     354         [ +  + ]:          8 :         if(i != begin)
     355                 :            :         {
     356         [ +  - ]:          4 :             --i;
     357   [ +  -  +  + ]:          4 :             if(c < std::get<0>(*i) || c > std::get<1>(*i)) return c;
     358                 :            :         }
     359                 :            :         else
     360                 :          4 :             return c;
     361                 :            :     }
     362                 :          6 :     return c - std::get<2>(*i);
     363                 :            : }
     364                 :            : 
     365                 :          4 : char32_t tolower(char32_t c)
     366                 :            : {
     367                 :          4 :     return caseconv(std::begin(tolower_table), std::end(tolower_table), c);
     368                 :            : }
     369                 :          4 : char32_t toupper(char32_t c)
     370                 :            : {
     371                 :          4 :     return caseconv(std::begin(toupper_table), std::end(toupper_table), c);
     372                 :            : }
     373                 :          4 : char32_t totitle(char32_t c)
     374                 :            : {
     375                 :          4 :     return caseconv(std::begin(totitle_table), std::end(totitle_table), c);
     376                 :            : }
     377                 :            : 
     378                 :            : /** This function is optimized for performance.
     379                 :            :  * A simple (but incomplete) implementation is shown in if-0.
     380                 :            :  * Surrogate pairs are detected and parsed properly, if they appear within a single string.
     381                 :            :  */
     382                 :       9996 : std::u32string FromUTF8(std::string_view s)
     383                 :            : {
     384                 :       9996 :     std::u32string result;
     385                 :       9996 :     unsigned cache = 0/*, bytesleft = 0*/;
     386                 :       9996 :     constexpr unsigned bytesleftshift = 2, bytesleftmask = (1u << bytesleftshift)-1;
     387                 :       9996 :     constexpr unsigned con = 0b00000000000000000101010101011011u;
     388         [ +  + ]:     145531 :     for(unsigned char c: s)
     389                 :            :     {
     390                 :            :     #if 0
     391                 :            :         if(bytesleft > 0)           { cache = cache * 0x40 + (c & 0x3F); --bytesleft; }
     392                 :            :         else if((c & 0xE0) == 0xC0) { cache = c & 0x1F; bytesleft=1; }
     393                 :            :         else if((c & 0xF0) == 0xE0) { cache = c & 0x0F; bytesleft=2; }
     394                 :            :         else if((c & 0xF8) == 0xF0) { cache = c & 0x07; bytesleft=3; }
     395                 :            :         else                        { cache = c & 0x7F;              }
     396                 :            :         if(!bytesleft)
     397                 :            :         {
     398                 :            :             result += char32_t(cache);
     399                 :            :         }
     400                 :            :     #else
     401                 :     135535 :         unsigned c0 = (cache & bytesleftmask);
     402                 :     135535 :         unsigned c1 = (((cache >> bytesleftshift) * 0x40u + (c & 0x3F)) << bytesleftshift) + (c0-1);
     403                 :     135535 :         unsigned c2 = ((con << ((c >> 4)*2)) & 0xFFFFFFFFu) >> 30; // number of trailing bytes (0-3) given this first byte
     404                 :     135535 :         c2         += ((c & (0x070F1F7Fu >> (c2*8))) << bytesleftshift);
     405         [ +  + ]:     135535 :         cache = c0 ? c1 : c2;
     406                 :            : 
     407         [ +  + ]:     135535 :         if(!(cache & bytesleftmask))
     408                 :            :         {
     409                 :      74529 :             char32_t c = cache >> bytesleftshift;
     410         [ +  + ]:      74529 :             if(__builtin_expect(result.empty(), false)
     411         [ -  + ]:      64535 :             || __builtin_expect(c < 0xDC00 || c > 0xDFFF, true)
     412   [ +  +  -  -  :      74529 :             || __builtin_expect(result.back() < 0xD800 || result.back() > 0xDBFF, false))
                   -  - ]
     413         [ +  - ]:     210064 :                 result += c;
     414                 :            :             else
     415                 :          0 :                 result.back() = (result.back() - 0xD800u)*0x400u + (c - 0xDC00u) + 0x10000u;
     416                 :            :         }
     417                 :            :     #endif
     418                 :            :     }
     419                 :       9996 :     return result;
     420                 :          0 : }
     421                 :            : 
     422                 :            : /**
     423                 :            :  * This function is optimized for performance.
     424                 :            :  */
     425                 :      20138 : std::string ToUTF8(std::u32string_view s)
     426                 :            : {
     427                 :      20138 :     std::string result;
     428                 :      20138 :     alignas(16) static constexpr unsigned S[4] = {0x7F,0x3F,0x3F,0x3F}, q[4] = {0xFF,0,0,0}, o[4] = {0,0x80,0x80,0x80};
     429         [ +  + ]:      40846 :     for(char32_t c: s)
     430                 :            :     {
     431                 :            :         /**/
     432                 :      20708 :         unsigned n = (c >= 0x80u) + (c >= 0x800u) + (c >= 0x10000u);
     433                 :            :         /*
     434                 :            :         alignas(4) char rbuf[4] =
     435                 :            :             { char((((0xF0E0C000u) >> (n*8)) & 0xFF) + (c >> 6*n)),
     436                 :            :               char(0x80 + ((c >> 6*(n-1)) & 0x3F)),
     437                 :            :               char(0x80 + ((c >> 6*(n-2)) & 0x3F)),
     438                 :            :               char(0x80 + ((c >> 6*(n-3)) & 0x3F)) };
     439                 :            :         result.append(rbuf, n+1);
     440                 :            :         */
     441                 :            :         /*
     442                 :            :         alignas(16) unsigned w[4] = { (((0xF0E0C000u) >> (n*8u)) & 0xFFu), 0,0,0 };
     443                 :            :         alignas(16) constexpr unsigned s[4] = {0x7F,0x3F,0x3F,0x3F}, o[4] = {0,0x80,0x80,0x80};
     444                 :            :         #pragma omp simd
     445                 :            :         for(unsigned m=0; m<4; ++m) w[m] += ((c >> ((n-m)*6u) & s[m]) + o[m]);// << (m*8u);
     446                 :            :         result.append(w, w+n+1);*/
     447                 :      20708 :         unsigned val = 0xF0E0C000u >> (n*8u);
     448                 :      20708 :         alignas(16) unsigned w[4]={val,val,val,val};
     449                 :            :         #pragma omp simd
     450         [ +  + ]:     103540 :         for(unsigned m=0; m<4; ++m)
     451                 :      82832 :             w[m] = ((w[m] & q[m]) + ((c >> ((n-m)*6u) & S[m]) + o[m])) << (m*8u);
     452                 :            :         unsigned sum = 0;
     453                 :            :         #pragma omp simd
     454         [ +  + ]:     103540 :         for(unsigned m=0; m<4; ++m) sum += w[m];
     455                 :            :         alignas(4) char temp[4];
     456         [ +  + ]:     103540 :         for(unsigned m=0; m<4; ++m) temp[m] = sum >> (m*8);
     457         [ +  - ]:      20708 :         result.append(temp, n+1);
     458                 :            :         /*
     459                 :            :         unsigned val = ((((0xF0E0C000u) >> (n*8u)) & 0xFFu) + (c >> 6u*n))
     460                 :            :                      + 0x80808000u
     461                 :            :                      + (((c >> 6u*(n-1)) & 0x3Fu) << 8u)
     462                 :            :                      + (((c >> 6u*(n-2)) & 0x3Fu) << 16u)
     463                 :            :                      + (((c >> 6u*(n-3)) & 0x3Fu) << 24u);
     464                 :            :         alignas(4) char rbuf[4] = {char(val),char(val>>8),char(val>>16),char(val>>24)};
     465                 :            :         result.append(rbuf, n+1);
     466                 :            :         */
     467                 :            :         /**/
     468                 :            :         /*
     469                 :            :         if(c < 0x80)
     470                 :            :         {
     471                 :            :             result += c;
     472                 :            :         }
     473                 :            :         else if(c < 0x800)
     474                 :            :         {
     475                 :            :             result += char(0xC0 + (c>>6));
     476                 :            :             result += char(0x80 + (c & 0x3F));
     477                 :            :         }
     478                 :            :         else if(c < 0x10000)
     479                 :            :         {
     480                 :            :             result += char(0xE0 + (c>>12));
     481                 :            :             result += char(0x80 + ((c>>6) & 0x3F));
     482                 :            :             result += char(0x80 + (c & 0x3F));
     483                 :            :         }
     484                 :            :         else
     485                 :            :         {
     486                 :            :             result += char(0xF0 + (c>>18));
     487                 :            :             result += char(0x80 + ((c>>12) & 0x3F));
     488                 :            :             result += char(0x80 + ((c>>6) & 0x3F));
     489                 :            :             result += char(0x80 + (c & 0x3F));
     490                 :            :         }
     491                 :            :         */
     492                 :            :     }
     493                 :      20138 :     return result;
     494                 :          0 : }
     495                 :            : 
     496                 :            : template<typename C>
     497                 :            : alignas(32) static const C spaces[32]={' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
     498                 :            :                                            ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
     499                 :            : template<std::size_t Nbytes, typename C, typename T = unsigned>
     500                 :      37507 : static inline bool compa(const void* a)
     501                 :            : {
     502                 :      37507 :     const T* aa = (const T*)a;
     503                 :      37507 :     const T* bb = (const T*)spaces<C>;
     504                 :      37507 :     T result = ~T();
     505                 :            :     #pragma omp simd reduction(&:result)
     506         [ +  + ]:     112521 :     for(unsigned n=0; n<Nbytes / sizeof(T); ++n)
     507                 :      75014 :         result &= (aa[n] == bb[n]);
     508                 :            :     // If result==0, then a mismatch was found
     509                 :      37507 :     return result;
     510                 :            : }
     511                 :            : 
     512                 :            : //#include <iostream>
     513                 :          8 : std::size_t CountIndent(std::u32string_view text, std::size_t begin)
     514                 :            : {
     515                 :          8 :     typedef std::remove_const_t<std::remove_reference_t<decltype(text[0])>> C;
     516                 :          8 :     std::size_t oldbegin = begin, size = text.size();
     517                 :          8 :     const auto data = text.data();
     518                 :            : 
     519                 :            : /**/
     520                 :            : #if defined(__clang__) || defined(__ICC)
     521                 :            :     while(begin+32u/sizeof(C) <= size && compa<32u,C,unsigned long>(data+begin)) { begin += 32u/sizeof(C); }
     522                 :            :     while(begin+8u/sizeof(C) <= size && std::memcmp(data+begin, spaces<C>, 8u)==0) { begin += 8u/sizeof(C); }
     523                 :            : #else
     524   [ +  +  +  + ]:      37511 :     while(begin+16u/sizeof(C) <= size && compa<16u,C,unsigned long>(data+begin)) { begin += 16u/sizeof(C); }
     525   [ +  +  +  + ]:          8 :     if(begin+8u/sizeof(C) <= size && std::memcmp(data+begin, spaces<C>, 8u)==0) { begin += 8u/sizeof(C); }
     526                 :            : #endif
     527                 :            : /**/
     528                 :            : 
     529                 :            :     //if(begin+16u <= size && !compa<16u,C,unsigned int>(data+begin)) { begin += 16u; }
     530                 :            :     //if(begin+8u <= size && !compa<8u,unsigned long>(data+begin, spaces<C>)) { begin += 8u;  }
     531                 :            :     //while(begin < size && data[begin]==' ') { begin += 1u; }
     532                 :          8 :     std::size_t pos = text.find_first_not_of(C(' '), begin);
     533         [ +  + ]:          8 :     if(pos == text.npos) pos = text.size();
     534                 :            :     //std::cerr << "Indent in <" << ToUTF8(text.substr(oldbegin)) << "> (oldbegin="<<oldbegin<<",begin="<<begin<<",pos="<<pos<<" is " << (pos-oldbegin) << "\n";
     535                 :          8 :     return pos - oldbegin;
     536                 :            : }
     537                 :            : 
     538                 :            : /** Converts a single CP437 codepoint to a unicode codepoint.
     539                 :            :  * This code is generated with constablecom.
     540                 :            :  */
     541                 :          7 : static unsigned short cp437_uni(unsigned n)
     542                 :            : {
     543                 :            :     /* Minmax for 0..47: 161, 8976 */
     544                 :            :     /* Minmax for 48..54: 9474, 9571 */
     545                 :            :     /* Minmax for 55..121: 160, 9632 */
     546                 :          7 :     static const unsigned short cp437_uni_uni_tab[122] =
     547                 :            :     {
     548                 :            :         199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 
     549                 :            :         236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, 
     550                 :            :         220, 162, 163, 165, 8359,402, 225, 237, 243, 250, 241, 209, 170, 
     551                 :            :         186, 191, 8976,172, 189, 188, 161, 171, 187, 9474,9508,9569,9570,
     552                 :            :         9558,9557,9571,9564,9563,9488,9492,9524,9516,9500,9472,9532,9566,
     553                 :            :         9567,9562,9556,9577,9574,9568,9552,9580,9575,9576,9572,9573,9561,
     554                 :            :         9560,9554,9555,9579,9578,9496,9484,9608,9604,9612,9616,9600,945, 
     555                 :            :         223, 915, 960, 931, 963, 181, 964, 934, 920, 937, 948, 8734,966, 
     556                 :            :         949, 8745,8801,177, 8805,8804,8992,8993,247, 8776,176, 8729,183, 
     557                 :            :         8730,8319,178, 9632,160, 
     558                 :            :     };
     559                 :          7 :     return 
     560                 :            :         (n<128)? (n)/*128*/
     561                 :            :           : (n<189)
     562                 :          2 :             ? (n<176)? cp437_uni_uni_tab[n-128]/*48*/
     563                 :          0 :               : (n<179)? (n + 9441)/*3*/
     564                 :          0 :                 : (n<186)? cp437_uni_uni_tab[n-131]/*7*/
     565                 :          0 :                          : ( 6*n + 8437)/*3*/
     566   [ +  +  +  -  :          7 :             : cp437_uni_uni_tab[n-134]/*67*/;
          +  -  -  -  -  
                      - ]
     567                 :            : }
     568                 :            : 
     569                 :          1 : std::u32string FromCP437(std::string_view s)
     570                 :            : {
     571                 :          1 :     std::u32string result;
     572         [ +  + ]:          8 :     for(unsigned char c: s)
     573         [ +  - ]:         14 :         result += (char32_t)cp437_uni(c);
     574                 :          1 :     return result;
     575                 :          0 : }
     576                 :            : 
     577                 :            : #ifdef RUN_TESTS
     578                 :          3 : TEST(ctype, boolean_tests)
     579                 :            : {
     580                 :          3 :     EXPECT_TRUE(isupper(U'A')); EXPECT_FALSE(isupper(U'a')); EXPECT_FALSE(isupper(U'0'));
     581                 :          3 :     EXPECT_TRUE(islower(U'a')); EXPECT_FALSE(islower(U'A')); EXPECT_FALSE(islower(U'0'));
     582                 :          5 :     EXPECT_TRUE(isalpha(U'A')); EXPECT_FALSE(isalpha(U'5')); EXPECT_TRUE(isalpha(U'ä')); EXPECT_TRUE(isalpha(U'ε')); EXPECT_FALSE(isalpha(U'¬'));
     583                 :          5 :     EXPECT_TRUE(isalnum(U'A')); EXPECT_TRUE(isalnum(U'5')); EXPECT_TRUE(isalnum(U'ä')); EXPECT_TRUE(isalnum(U'²')); EXPECT_FALSE(isalnum(U'—'));
     584                 :          4 :     EXPECT_TRUE(isalnum_(U'A')); EXPECT_TRUE(isalnum_(U'5')); EXPECT_TRUE(isalnum_(U'_')); EXPECT_FALSE(isalnum_(U'—'));
     585                 :          5 :     EXPECT_FALSE(isdigit(U'B')); EXPECT_TRUE(isdigit(U'5')); EXPECT_FALSE(isdigit(U'ä')); EXPECT_TRUE(isdigit(U'𝟹')); EXPECT_FALSE(isdigit(U'—'));
     586                 :          5 :     EXPECT_TRUE(isxdigit(U'B')); EXPECT_TRUE(isxdigit(U'5')); EXPECT_FALSE(isxdigit(U'ä')); EXPECT_TRUE(isxdigit(U'𝟹')); EXPECT_FALSE(isxdigit(U'—'));
     587                 :          2 :     EXPECT_TRUE(ispunct(U',')); EXPECT_FALSE(ispunct(U'a'));
     588                 :          3 :     EXPECT_TRUE(isspace(U' ')); EXPECT_FALSE(isspace(U'_')); EXPECT_TRUE(isspace(U'\t'));
     589                 :          2 :     EXPECT_TRUE(isspace(U'\n')); EXPECT_TRUE(isspace(U'\r'));
     590                 :          3 :     EXPECT_TRUE(isspace_punct(U' ')); EXPECT_TRUE(isspace_punct(U'_')); EXPECT_TRUE(isspace_punct(U'\t'));
     591                 :          3 :     EXPECT_TRUE(isspace_punct(U'\n')); EXPECT_TRUE(isspace_punct(U'\r')); EXPECT_TRUE(isspace_punct(U'.'));
     592                 :          3 :     EXPECT_TRUE(isblank(U' ')); EXPECT_FALSE(isblank(U'_')); EXPECT_TRUE(isblank(U'\t'));
     593                 :          2 :     EXPECT_FALSE(isblank(U'\n')); EXPECT_FALSE(isblank(U'\r'));
     594                 :          3 :     EXPECT_TRUE(isgraph(U'^')); EXPECT_TRUE(isgraph(U'┐')); EXPECT_TRUE(isgraph(U'a'));
     595                 :          3 :     EXPECT_TRUE(isprint(U'^')); EXPECT_TRUE(isprint(U'┐')); EXPECT_FALSE(isprint(0x84));
     596                 :          4 :     EXPECT_FALSE(isdouble(U'A')); EXPECT_TRUE(isdouble(U'C')); EXPECT_FALSE(isdouble(U'オ')); EXPECT_TRUE(isdouble(U'オ'));
     597                 :          1 : }
     598                 :          3 : TEST(ctype, translation_tests)
     599                 :            : {
     600                 :          1 :     EXPECT_EQ(tolower(U'A'), U'a');
     601                 :          1 :     EXPECT_EQ(tolower(U's'), U's');
     602                 :          1 :     EXPECT_EQ(toupper(U'a'), U'A');
     603                 :          1 :     EXPECT_EQ(toupper(U'S'), U'S');
     604                 :          1 :     EXPECT_EQ(totitle(U'a'), U'A');
     605                 :          1 :     EXPECT_EQ(totitle(U'S'), U'S');
     606                 :            : 
     607                 :          1 :     EXPECT_EQ(toupper(U'A'), U'A');
     608                 :          1 :     EXPECT_EQ(toupper(U's'), U'S');
     609                 :          1 :     EXPECT_EQ(tolower(U'a'), U'a');
     610                 :          1 :     EXPECT_EQ(tolower(U'S'), U's');
     611                 :          1 :     EXPECT_EQ(totitle(U'a'), U'A');
     612                 :          1 :     EXPECT_EQ(totitle(U'S'), U'S');
     613                 :          1 : }
     614                 :          3 : TEST(ctype, charconv_tests)
     615                 :            : {
     616                 :          3 :     EXPECT_EQ(ToUTF8(U"abckääkオk"), "abck\xC3\xA4\xC3\xA4k\xE3\x82\xAAk");
     617                 :          2 :     EXPECT_EQ(FromUTF8("abck\xC3\xA4\xC3\xA4k\xE3\x82\xAAk"), U"abckääkオk");
     618                 :          2 :     EXPECT_EQ(FromCP437("abck\x84\x84k"), U"abckääk");
     619                 :          1 : }
     620                 :          3 : TEST(ctype, count_indent)
     621                 :            : {
     622                 :          1 :     EXPECT_EQ(CountIndent(U""), 0u);
     623                 :          2 :     EXPECT_EQ(CountIndent(U"s    t    "), 0u);
     624                 :          2 :     EXPECT_EQ(CountIndent(U"    s    t    "), 4u);
     625                 :          2 :     EXPECT_EQ(CountIndent(U"    "), 4u);
     626                 :          2 :     EXPECT_EQ(CountIndent(U"    ", 1), 3u);
     627                 :          2 :     EXPECT_EQ(CountIndent(U"    s    t    ", 5), 4u);
     628                 :          2 :     EXPECT_EQ(CountIndent(U"    s    t    ", 7), 2u);
     629                 :          1 :     EXPECT_EQ(CountIndent(std::u32string(150000, U' ')), 150000u);
     630                 :          1 : }
     631                 :            : #endif

Generated by: LCOV version 1.16