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
|