Evo C++ Library v0.5.1
strscan.h
Go to the documentation of this file.
1 // Evo C++ Library
2 /* Copyright 2019 Justin Crowell
3 Distributed under the BSD 2-Clause License -- see included file LICENSE.txt for details.
4 */
6 
7 #pragma once
8 #ifndef INCL_evo_impl_strscan_h
9 #define INCL_evo_impl_strscan_h
10 
11 #include "type.h"
12 #include "impl/str.h"
13 #include <string.h>
14 
15 #if defined(EVO_CPU)
16  #if defined(_WIN32)
17  #include <intrin.h>
18  #if !defined(EVO_IMPL_SSE42) && defined(EVO_IMPL_SSE2)
19  #pragma intrinsic(_BitScanForward)
20  #pragma intrinsic(_BitScanReverse)
21  #endif
22  #elif defined(EVO_IMPL_SSE42)
23  #include <nmmintrin.h>
24  #elif defined(EVO_IMPL_SSE2)
25  #include <emmintrin.h>
26  #endif
27 #endif
28 
29 namespace evo {
32 
34 
36 namespace impl {
37  // Scan to next non-whitespace char
38  inline const char* str_scan_nws_default(const char* str, const char* end) {
39  for (; str < end; ++str)
40  if (!(*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r'))
41  break;
42  return str;
43  }
44 
45  // Scan to next non-whitespace char in reverse, return new end
46  inline const char* str_scan_nws_default_r(const char* str, const char* end) {
47  while (str < end) {
48  --end;
49  if (!(*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
50  return end + 1;
51  }
52  return end;
53  }
54 
55  // Scan to next non-delim char
56  inline const char* str_scan_ndelim_default(const char* str, const char* end, char delim1, char delim2) {
57  for (; str < end; ++str)
58  if (!(*str == delim1 || *str == delim2))
59  break;
60  return str;
61  }
62 
63  // Scan to next non-delim char in reverse, return new end
64  inline const char* str_scan_ndelim_default_r(const char* str, const char* end, char delim1, char delim2) {
65  while (str < end) {
66  --end;
67  if (!(*end == delim1 || *end == delim2))
68  return end + 1;
69  }
70  return end;
71  }
72 
73  // Scan to next delim
74  inline const char* str_scan_delim_default(const char* str, const char* end, char delim1, char delim2) {
75  for (; str < end; ++str)
76  if (*str == delim1 || *str == delim2)
77  break;
78  return str;
79  }
80 
81  // Scan to next delim in reverse, return new end
82  inline const char* str_scan_delim_default_r(const char* str, const char* end, char delim1, char delim2) {
83  while (str < end) {
84  --end;
85  if (*end == delim1 || *end == delim2)
86  return end + 1;
87  }
88  return end;
89  }
90 
91  // Scan to next delim
92  inline const char* str_scan_delim_default(const char* str, const char* end, const char* delims, uint delim_count) {
93  for (; str < end; ++str)
94  if (::memchr(delims, *str, delim_count) != NULL)
95  break;
96  return str;
97  }
98 
99  // Scan to next delim in reverse, return new end
100  inline const char* str_scan_delim_default_r(const char* str, const char* end, const char* delims, uint delim_count) {
101  while (str < end) {
102  --end;
103  if (::memchr(delims, *end, delim_count) != NULL)
104  return end + 1;
105  }
106  return end;
107  }
108 
109 #if defined(EVO_CPU)
110  static const size_t SSE_BATCH_SIZE = 16;
111  static const size_t SSE_ALIGN16 = 0x0F;
112  static const size_t SSE_ALIGN16_MASK = ~SSE_ALIGN16;
113 #endif
114 
115 #if defined(EVO_IMPL_SSE42)
116  // Scan to next non-whitespace char
117  inline const char* str_scan_nws_cpu(const char* str, const char* end) {
118  if (str < end) {
119  // Scan up to alignment boundary
120  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
121  while (str < align16)
122  if (!(*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r') || ++str == end)
123  return str;
124 
125  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
126  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY;
127  const __m128i ws = _mm_loadu_si128((__m128i*)" \t\n\r\0\0\0\0\0\0\0\0\0\0\0");
128  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
129  for (; str < align16; str += SSE_BATCH_SIZE) {
130  const int i = _mm_cmpistri(ws, _mm_load_si128((__m128i*)str), FLAGS);
131  if (i != (int)SSE_BATCH_SIZE)
132  return str + i;
133  }
134 
135  // Scan remaining chars
136  for (; str < end; ++str)
137  if (!(*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r'))
138  break;
139  }
140  return str;
141  }
142 
143  // Scan to next non-whitespace char in reverse, return new end
144  inline const char* str_scan_nws_cpu_r(const char* str, const char* end) {
145  if (str < end) {
146  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
147  if (align16 > str) {
148  // Scan up to alignment boundary
149  while (end > align16) {
150  --end;
151  if (!(*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
152  return end + 1;
153  }
154 
155  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
156  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_MOST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY;
157  const __m128i ws = _mm_loadu_si128((__m128i*)" \t\n\r\0\0\0\0\0\0\0\0\0\0\0");
158  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
159  while (end > align16) {
160  end -= SSE_BATCH_SIZE;
161  const int i = _mm_cmpistri(ws, _mm_load_si128((__m128i*)end), FLAGS);
162  if (i != (int)SSE_BATCH_SIZE)
163  return end + i + 1;
164  }
165  }
166 
167  // Scan remaining chars
168  while (str < end) {
169  --end;
170  if (!(*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
171  return end + 1;
172  }
173  }
174  return end;
175  }
176 
178 
179  // Scan to next non-delim char
180  inline const char* str_scan_ndelim_cpu(const char* str, const char* end, char delim1, char delim2) {
181  if (str < end) {
182  // Scan up to alignment boundary
183  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
184  while (str < align16)
185  if (!(*str == delim1 || *str == delim2) || ++str == end)
186  return str;
187 
188  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
189  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
190  if (str < align16) {
191  __m128i delims_in;
192  {
193  char buf[SSE_BATCH_SIZE];
194  buf[0] = delim1;
195  ::memset(buf + 1, delim2, SSE_BATCH_SIZE - 1);
196  delims_in = _mm_loadu_si128((__m128i*)buf);
197  }
198 
199  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY;
200  do {
201  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)str), FLAGS);
202  if (i != (int)SSE_BATCH_SIZE)
203  return str + i;
204  str += SSE_BATCH_SIZE;
205  } while (str < align16);
206  }
207 
208  // Scan remaining chars
209  for (; str < end; ++str)
210  if (!(*str == delim1 || *str == delim2))
211  break;
212  }
213  return str;
214  }
215 
216  // Scan to next non-delim char in reverse, return new end
217  inline const char* str_scan_ndelim_cpu_r(const char* str, const char* end, char delim1, char delim2) {
218  if (str < end) {
219  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
220  if (align16 > str) {
221  // Scan up to alignment boundary
222  while (end > align16) {
223  --end;
224  if (!(*end == delim1 || *end == delim2))
225  return end + 1;
226  }
227 
228  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
229  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
230  if (end > align16) {
231  __m128i delims_in;
232  {
233  char buf[SSE_BATCH_SIZE];
234  buf[0] = delim1;
235  ::memset(buf + 1, delim2, SSE_BATCH_SIZE - 1);
236  delims_in = _mm_loadu_si128((__m128i*)buf);
237  }
238 
239  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_MOST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY;
240  do {
241  end -= SSE_BATCH_SIZE;
242  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)end), FLAGS);
243  if (i != (int)SSE_BATCH_SIZE)
244  return end + i + 1;
245  } while (end > align16);
246  }
247  }
248 
249  // Scan remaining chars
250  while (str < end) {
251  --end;
252  if (!(*end == delim1 || *end == delim2))
253  return end + 1;
254  }
255  }
256  return end;
257  }
258 
260 
261  // Scan to next delim
262  inline const char* str_scan_delim_cpu(const char* str, const char* end, char delim1, char delim2) {
263  if (str < end) {
264  // Scan up to alignment boundary
265  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
266  while (str < align16)
267  if (*str == delim1 || *str == delim2 || ++str == end)
268  return str;
269 
270  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
271  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
272  if (str < align16) {
273  __m128i delims_in;
274  {
275  char buf[SSE_BATCH_SIZE];
276  buf[0] = delim1;
277  ::memset(buf + 1, delim2, SSE_BATCH_SIZE - 1);
278  delims_in = _mm_loadu_si128((__m128i*)buf);
279  }
280 
281  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT;
282  do {
283  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)str), FLAGS);
284  if (i != (int)SSE_BATCH_SIZE)
285  return str + i;
286  str += SSE_BATCH_SIZE;
287  } while (str < align16);
288  }
289 
290  // Scan remaining chars
291  for (; str < end; ++str)
292  if (*str == delim1 || *str == delim2)
293  break;
294  }
295  return str;
296  }
297 
298  // Scan to next delim in reverse, return new end
299  inline const char* str_scan_delim_cpu_r(const char* str, const char* end, char delim1, char delim2) {
300  if (str < end) {
301  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
302  if (align16 > str) {
303  // Scan up to alignment boundary
304  while (end > align16) {
305  --end;
306  if (*end == delim1 || *end == delim2)
307  return end + 1;
308  }
309 
310  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
311  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
312  if (end > align16) {
313  __m128i delims_in;
314  {
315  char buf[SSE_BATCH_SIZE];
316  buf[0] = delim1;
317  ::memset(buf + 1, delim2, SSE_BATCH_SIZE - 1);
318  delims_in = _mm_loadu_si128((__m128i*)buf);
319  }
320 
321  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_MOST_SIGNIFICANT;
322  do {
323  end -= SSE_BATCH_SIZE;
324  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)end), FLAGS);
325  if (i != (int)SSE_BATCH_SIZE)
326  return end + i + 1;
327  } while (end > align16);
328  }
329  }
330 
331  // Scan remaining chars
332  while (str < end) {
333  --end;
334  if (*end == delim1 || *end == delim2)
335  return end + 1;
336  }
337  }
338  return end;
339  }
340 
341  // Scan to next delim -- min 1 delim, max 16 delims
342  inline const char* str_scan_delim_cpu(const char* str, const char* end, const char* delims, uint delim_count) {
343  assert( delim_count > 1 );
344  assert( delim_count <= SSE_BATCH_SIZE );
345  if (str < end) {
346  // Scan up to alignment boundary
347  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
348  while (str < align16)
349  if (::memchr(delims, *str, delim_count) != NULL || ++str == end)
350  return str;
351 
352  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
353  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
354  if (str < align16) {
355  __m128i delims_in;
356  {
357  char buf[SSE_BATCH_SIZE];
358  ::memcpy(buf, delims, delim_count);
359  if (delim_count < SSE_BATCH_SIZE)
360  ::memset(buf + delim_count, delims[0], SSE_BATCH_SIZE - delim_count);
361  delims_in = _mm_loadu_si128((__m128i*)buf);
362  }
363 
364  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT;
365  do {
366  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)str), FLAGS);
367  if (i != (int)SSE_BATCH_SIZE)
368  return str + i;
369  str += SSE_BATCH_SIZE;
370  } while (str < align16);
371  }
372 
373  // Scan remaining chars
374  for (; str < end; ++str)
375  if (::memchr(delims, *str, delim_count) != NULL)
376  break;
377  }
378  return str;
379  }
380 
381  // Scan to next delim in reverse, return new end -- min 1 delim, max 16 delims
382  inline const char* str_scan_delim_cpu_r(const char* str, const char* end, const char* delims, uint delim_count) {
383  if (str < end) {
384  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
385  if (align16 > str) {
386  // Scan up to alignment boundary
387  while (end > align16) {
388  --end;
389  if (::memchr(delims, *end, delim_count) != NULL)
390  return end + 1;
391  }
392 
393  // Scan in chunks of batch size using SSE 4.2 instruction: pcmpistri
394  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
395  if (end > align16) {
396  __m128i delims_in;
397  {
398  char buf[SSE_BATCH_SIZE];
399  ::memcpy(buf, delims, delim_count);
400  if (delim_count < SSE_BATCH_SIZE)
401  ::memset(buf + delim_count, delims[0], SSE_BATCH_SIZE - delim_count);
402  delims_in = _mm_loadu_si128((__m128i*)buf);
403  }
404 
405  const int FLAGS = _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_MOST_SIGNIFICANT;
406  do {
407  end -= SSE_BATCH_SIZE;
408  const int i = _mm_cmpistri(delims_in, _mm_load_si128((__m128i*)end), FLAGS);
409  if (i != (int)SSE_BATCH_SIZE)
410  return end + i + 1;
411  } while (end > align16);
412  }
413  }
414 
415  // Scan remaining chars
416  while (str < end) {
417  --end;
418  if (::memchr(delims, *end, delim_count) != NULL)
419  return end + 1;
420  }
421  }
422  return end;
423  }
424 
425 #elif defined(EVO_IMPL_SSE2)
426  // Scan to next non-whitespace char
427  inline const char* str_scan_nws_cpu(const char* str, const char* end) {
428  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
429  if (end > align16) {
430  // Scan up to alignment boundary
431  while (str < align16) {
432  if (!(*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r'))
433  return str;
434  ++str;
435  }
436 
437  // Scan in chunks of batch size using SSE 2 instructions
438  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
439  if (str < align16) {
440  const char* WS = " "
441  "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
442  "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
443  "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r";
444  const __m128i ws1 = _mm_loadu_si128((__m128i*)WS);
445  const __m128i ws2 = _mm_loadu_si128((__m128i*)(WS + SSE_BATCH_SIZE));
446  const __m128i ws3 = _mm_loadu_si128((__m128i*)(WS + (SSE_BATCH_SIZE * 2)));
447  const __m128i ws4 = _mm_loadu_si128((__m128i*)(WS + (SSE_BATCH_SIZE * 3)));
448 
449  __m128i v, n;
450  uint16 r;
451  do {
452  n = _mm_load_si128((__m128i*)str);
453 
454  // pcmpeqb, por
455  v = _mm_or_si128(_mm_cmpeq_epi8(n, ws1), _mm_cmpeq_epi8(n, ws2));
456  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, ws3));
457  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, ws4));
458 
459  // pmovmskb
460  r = (uint16)~_mm_movemask_epi8(v);
461  if (r != 0) {
462  #if defined(_MSC_VER)
463  ulong i;
464  _BitScanForward(&i, r);
465  return str + i;
466  #else
467  return str + __builtin_ffs(r) - 1;
468  #endif
469  }
470  str += SSE_BATCH_SIZE;
471  } while (str < align16);
472  }
473  }
474 
475  // Scan remaining chars
476  for (; str < end; ++str)
477  if (!(*str == ' ' || *str == '\t' || *str == '\n' || *str == '\r'))
478  break;
479  return str;
480  }
481 
482  // Scan to next non-whitespace char in reverse, return new end
483  inline const char* str_scan_nws_cpu_r(const char* str, const char* end) {
484  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
485  if (align16 > str) {
486  // Scan up to alignment boundary
487  while (end > align16) {
488  --end;
489  if (!(*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
490  return end + 1;
491  }
492 
493  // Scan in chunks of batch size using SSE 2 instructions
494  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
495  if (end > align16) {
496  const char* WS = " "
497  "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
498  "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"
499  "\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r\r";
500  const __m128i ws1 = _mm_loadu_si128((__m128i*)WS);
501  const __m128i ws2 = _mm_loadu_si128((__m128i*)(WS + SSE_BATCH_SIZE));
502  const __m128i ws3 = _mm_loadu_si128((__m128i*)(WS + (SSE_BATCH_SIZE * 2)));
503  const __m128i ws4 = _mm_loadu_si128((__m128i*)(WS + (SSE_BATCH_SIZE * 3)));
504 
505  __m128i v, n;
506  uint16 r;
507  do {
508  end -= SSE_BATCH_SIZE;
509  n = _mm_load_si128((__m128i*)end);
510 
511  // pcmpeqb, por
512  v = _mm_or_si128(_mm_cmpeq_epi8(n, ws1), _mm_cmpeq_epi8(n, ws2));
513  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, ws3));
514  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, ws4));
515 
516  // pmovmskb
517  r = (uint16)~_mm_movemask_epi8(v);
518  if (r != 0) {
519  #if defined(_MSC_VER)
520  ulong i;
521  _BitScanReverse(&i, r);
522  return end + i + 1;
523  #else
524  return end + IntegerT<uint>::BITS - __builtin_clz(r);
525  #endif
526  }
527  } while (end > align16);
528  }
529  }
530 
531  // Scan remaining chars
532  while (str < end) {
533  --end;
534  if (!(*end == ' ' || *end == '\t' || *end == '\n' || *end == '\r'))
535  return end + 1;
536  }
537  return end;
538  }
539 
541 
542  // Scan to next non-delim char
543  inline const char* str_scan_ndelim_cpu(const char* str, const char* end, char delim1, char delim2) {
544  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
545  if (end > align16) {
546  // Scan up to alignment boundary
547  while (str < align16)
548  if (!(*str == delim1 || *str == delim2) || ++str == end)
549  return str;
550 
551  // Scan in chunks of batch size using SSE 2 instructions
552  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
553  if (str < align16) {
554  __m128i delim_in1, delim_in2;
555  {
556  char buf[SSE_BATCH_SIZE];
557  ::memset(buf, delim1, SSE_BATCH_SIZE);
558  delim_in1 = _mm_loadu_si128((__m128i*)buf);
559 
560  ::memset(buf, delim2, SSE_BATCH_SIZE);
561  delim_in2 = _mm_loadu_si128((__m128i*)buf);
562  }
563 
564  __m128i n;
565  uint16 r;
566  do {
567  // pcmpeqb, por, pmovmskb
568  n = _mm_load_si128((__m128i*)str);
569  r = (uint16)~_mm_movemask_epi8( _mm_or_si128(_mm_cmpeq_epi8(n, delim_in1), _mm_cmpeq_epi8(n, delim_in2)) );
570  if (r != 0) {
571  #if defined(_MSC_VER)
572  ulong i;
573  _BitScanForward(&i, r);
574  return str + i;
575  #else
576  return str + __builtin_ffs(r) - 1;
577  #endif
578  }
579  str += SSE_BATCH_SIZE;
580  } while(str < align16);
581  }
582  }
583 
584  // Scan remaining chars
585  for (; str < end; ++str)
586  if (!(*str == delim1 || *str == delim2))
587  break;
588  return str;
589  }
590 
591  // Scan to next non-delim char in reverse, return new end
592  inline const char* str_scan_ndelim_cpu_r(const char* str, const char* end, char delim1, char delim2) {
593  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
594  if (align16 > str) {
595  // Scan up to alignment boundary
596  while (end > align16) {
597  --end;
598  if (!(*end == delim1 || *end == delim2))
599  return end + 1;
600  }
601 
602  // Scan in chunks of batch size using SSE 2 instructions
603  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
604  if (end > align16) {
605  __m128i delim_in1, delim_in2;
606  {
607  char buf[SSE_BATCH_SIZE];
608  ::memset(buf, delim1, SSE_BATCH_SIZE);
609  delim_in1 = _mm_loadu_si128((__m128i*)buf);
610 
611  ::memset(buf, delim2, SSE_BATCH_SIZE);
612  delim_in2 = _mm_loadu_si128((__m128i*)buf);
613  }
614 
615  __m128i n;
616  uint16 r;
617  do {
618  // pcmpeqb, por, pmovmskb
619  end -= SSE_BATCH_SIZE;
620  n = _mm_load_si128((__m128i*)end);
621  r = (uint16)~_mm_movemask_epi8( _mm_or_si128(_mm_cmpeq_epi8(n, delim_in1), _mm_cmpeq_epi8(n, delim_in2)) );
622  if (r != 0) {
623  #if defined(_MSC_VER)
624  ulong i;
625  _BitScanReverse(&i, r);
626  return end + i + 1;
627  #else
628  return end + IntegerT<uint>::BITS - __builtin_clz(r);
629  #endif
630  }
631  } while (end > align16);
632  }
633  }
634 
635  // Scan remaining chars
636  while (str < end) {
637  --end;
638  if (!(*end == delim1 || *end == delim2))
639  return end + 1;
640  }
641  return end;
642  }
643 
645 
646  // Scan to next delim
647  inline const char* str_scan_delim_cpu(const char* str, const char* end, char delim1, char delim2) {
648  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
649  if (end > align16) {
650  // Scan up to alignment boundary
651  while (str < align16)
652  if (*str == delim1 || *str == delim2 || ++str == end)
653  return str;
654 
655  // Scan in chunks of batch size using SSE 2 instructions
656  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
657  if (str < align16) {
658  __m128i delim_in1, delim_in2;
659  {
660  char buf[SSE_BATCH_SIZE];
661  ::memset(buf, delim1, SSE_BATCH_SIZE);
662  delim_in1 = _mm_loadu_si128((__m128i*)buf);
663 
664  ::memset(buf, delim2, SSE_BATCH_SIZE);
665  delim_in2 = _mm_loadu_si128((__m128i*)buf);
666  }
667 
668  __m128i n;
669  uint16 r;
670  do {
671  // pcmpeqb, por, pmovmskb
672  n = _mm_load_si128((__m128i*)str);
673  r = (uint16)_mm_movemask_epi8( _mm_or_si128(_mm_cmpeq_epi8(n, delim_in1), _mm_cmpeq_epi8(n, delim_in2)) );
674  if (r != 0) {
675  #if defined(_MSC_VER)
676  ulong i;
677  _BitScanForward(&i, r);
678  return str + i;
679  #else
680  return str + __builtin_ffs(r) - 1;
681  #endif
682  }
683  str += SSE_BATCH_SIZE;
684  } while (str < align16);
685  }
686  }
687 
688  // Scan remaining chars
689  for (; str < end; ++str)
690  if (*str == delim1 || *str == delim2)
691  break;
692  return str;
693  }
694 
695  // Scan to next delim -- min 1 delim, max 16 delims
696  inline const char* str_scan_delim_cpu(const char* str, const char* end, const char* delims, uint delim_count) {
697  assert( delim_count > 1 );
698  assert( delim_count <= SSE_BATCH_SIZE );
699  const char* align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
700  if (end > align16) {
701  // Scan up to alignment boundary
702  while (str < align16)
703  if (::memchr(delims, *str, delim_count) != NULL || ++str == end)
704  return str;
705 
706  // Scan in chunks of batch size using SSE 2 instructions
707  align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
708  if (str < align16) {
709  uint i;
710  __m128i delims_in[SSE_BATCH_SIZE];
711  {
712  char buf[SSE_BATCH_SIZE];
713  for (i = 0; i < delim_count; ++i) {
714  ::memset(buf, delims[i], SSE_BATCH_SIZE);
715  delims_in[i] = _mm_loadu_si128((__m128i*)buf);
716  }
717  }
718 
719  __m128i v, n;
720  uint16 r;
721  do {
722  n = _mm_load_si128((__m128i*)str);
723 
724  // pcmpeqb, por
725  v = _mm_or_si128(_mm_cmpeq_epi8(n, delims_in[0]), _mm_cmpeq_epi8(n, delims_in[1]));
726  for (i = 2; i < delim_count; ++i)
727  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, delims_in[i]));
728 
729  // pmovmskb
730  r = (uint16)_mm_movemask_epi8(v);
731  if (r != 0) {
732  #if defined(_MSC_VER)
733  ulong j;
734  _BitScanForward(&j, r);
735  return str + j;
736  #else
737  return str + __builtin_ffs(r) - 1;
738  #endif
739  }
740  str += SSE_BATCH_SIZE;
741  } while (str < align16);
742  }
743  }
744 
745  // Scan remaining chars
746  for (; str < end; ++str)
747  if (::memchr(delims, *str, delim_count) != NULL)
748  break;
749  return str;
750  }
751 
752  // Scan to next delim in reverse, return new end
753  inline const char* str_scan_delim_cpu_r(const char* str, const char* end, char delim1, char delim2) {
754  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
755  if (align16 > str) {
756  // Scan up to alignment boundary
757  while (end > align16) {
758  --end;
759  if (*end == delim1 || *end == delim2)
760  return end + 1;
761  }
762 
763  // Scan in chunks of batch size using SSE 2 instructions
764  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
765  if (end > align16) {
766  __m128i delim_in1, delim_in2;
767  {
768  char buf[SSE_BATCH_SIZE];
769  ::memset(buf, delim1, SSE_BATCH_SIZE);
770  delim_in1 = _mm_loadu_si128((__m128i*)buf);
771 
772  ::memset(buf, delim2, SSE_BATCH_SIZE);
773  delim_in2 = _mm_loadu_si128((__m128i*)buf);
774  }
775 
776  __m128i n;
777  uint16 r;
778  do {
779  // pcmpeqb, por, pmovmskb
780  end -= SSE_BATCH_SIZE;
781  n = _mm_load_si128((__m128i*)end);
782  r = (uint16)_mm_movemask_epi8( _mm_or_si128(_mm_cmpeq_epi8(n, delim_in1), _mm_cmpeq_epi8(n, delim_in2)) );
783  if (r != 0) {
784  #if defined(_MSC_VER)
785  ulong i;
786  _BitScanReverse(&i, r);
787  return end + i + 1;
788  #else
789  return end + IntegerT<uint>::BITS - __builtin_clz(r);
790  #endif
791  }
792  } while (end > align16);
793  }
794  }
795 
796  // Scan remaining chars
797  while (str < end) {
798  --end;
799  if (*end == delim1 || *end == delim2)
800  return end + 1;
801  }
802  return end;
803  }
804 
805  // Scan to next delim in reverse, return new end -- min 1 delim, max 16 delims
806  inline const char* str_scan_delim_cpu_r(const char* str, const char* end, const char* delims, uint delim_count) {
807  const char* align16 = (char*)((size_t)end & SSE_ALIGN16_MASK);
808  if (align16 > str) {
809  // Scan up to alignment boundary
810  while (end > align16) {
811  --end;
812  if (::memchr(delims, *end, delim_count) != NULL)
813  return end + 1;
814  }
815 
816  // Scan in chunks of batch size using SSE 2 instructions
817  align16 = (char*)( ((size_t)str + SSE_ALIGN16) & SSE_ALIGN16_MASK );
818  if (end > align16) {
819  uint i;
820  __m128i delims_in[SSE_BATCH_SIZE];
821  {
822  char buf[SSE_BATCH_SIZE];
823  for (i = 0; i < delim_count; ++i) {
824  ::memset(buf, delims[i], SSE_BATCH_SIZE);
825  delims_in[i] = _mm_loadu_si128((__m128i*)buf);
826  }
827  }
828 
829  __m128i v, n;
830  uint16 r;
831  do {
832  end -= SSE_BATCH_SIZE;
833  n = _mm_load_si128((__m128i*)end);
834 
835  // pcmpeqb, por
836  v = _mm_or_si128(_mm_cmpeq_epi8(n, delims_in[0]), _mm_cmpeq_epi8(n, delims_in[1]));
837  for (i = 2; i < delim_count; ++i)
838  v = _mm_or_si128(v, _mm_cmpeq_epi8(n, delims_in[i]));
839 
840  // pmovmskb
841  r = (uint16)_mm_movemask_epi8(v);
842  if (r != 0) {
843  #if defined(_MSC_VER)
844  ulong j;
845  _BitScanReverse(&j, r);
846  return end + j + 1;
847  #else
848  return end + IntegerT<uint>::BITS - __builtin_clz(r);
849  #endif
850  }
851  } while (end > align16);
852  }
853  }
854 
855  // Scan remaining chars
856  while (str < end) {
857  --end;
858  if (::memchr(delims, *end, delim_count) != NULL)
859  return end + 1;
860  }
861  return end;
862  }
863 #endif
864 }
867 
877 inline const char* str_scan_nws(const char* str, const char* end) {
878  assert( str != NULL );
879  assert( str <= end );
880 #if defined(EVO_CPU)
881  return impl::str_scan_nws_cpu(str, end);
882 #else
883  return impl::str_scan_nws_default(str, end);
884 #endif
885 }
886 
896 inline const char* str_scan_nws_r(const char* str, const char* end) {
897  assert( str != NULL );
898  assert( str <= end );
899 #if defined(EVO_CPU)
900  return impl::str_scan_nws_cpu_r(str, end);
901 #else
902  return impl::str_scan_nws_default_r(str, end);
903 #endif
904 }
905 
916 inline const char* str_scan_nws(const char* str, const char* end, char delim) {
917  assert( str != NULL );
918  assert( str <= end );
919  switch (delim) {
920  case ' ':
921  while (str < end && (*str == '\t' || *str == '\r' || *str == '\n'))
922  ++str;
923  break;
924  case '\t':
925  while (str < end && (*str == ' ' || *str == '\r' || *str == '\n'))
926  ++str;
927  break;
928  case '\r':
929  while (str < end && (*str == ' ' || *str == '\t' || *str == '\n'))
930  ++str;
931  break;
932  case '\n':
933  while (str < end && (*str == ' ' || *str == '\t' || *str == '\r'))
934  ++str;
935  break;
936  default:
937  #if defined(EVO_CPU)
938  return impl::str_scan_nws_cpu(str, end);
939  #else
940  return impl::str_scan_nws_default(str, end);
941  #endif
942  }
943  return str;
944 }
945 
956 inline const char* str_scan_nws_r(const char* str, const char* end, char delim) {
957  assert( str != NULL );
958  assert( str <= end );
959  switch (delim) {
960  case ' ':
961  while (--end > str && (*end == '\t' || *end == '\r' || *end == '\n'));
962  ++end;
963  break;
964  case '\t':
965  while (--end > str && (*end == ' ' || *end == '\r' || *end == '\n'));
966  ++end;
967  break;
968  case '\r':
969  while (--end > str && (*end == ' ' || *end == '\t' || *end == '\n'));
970  ++end;
971  break;
972  case '\n':
973  while (--end > str && (*end == ' ' || *end == '\t' || *end == '\r'));
974  ++end;
975  break;
976  default:
977  #if defined(EVO_CPU)
978  return impl::str_scan_nws_cpu_r(str, end);
979  #else
980  return impl::str_scan_nws_default_r(str, end);
981  #endif
982  }
983  return end;
984 }
985 
996 inline const char* str_scan_ndelim(const char* str, const char* end, char delim1, char delim2) {
997  assert( str != NULL );
998  assert( str <= end );
999 #if defined(EVO_CPU)
1000  return impl::str_scan_ndelim_cpu(str, end, delim1, delim2);
1001 #else
1002  return impl::str_scan_ndelim_default(str, end, delim1, delim2);
1003 #endif
1004 }
1005 
1017 inline const char* str_scan_ndelim_r(const char* str, const char* end, char delim1, char delim2) {
1018  assert( str != NULL );
1019  assert( str <= end );
1020 #if defined(EVO_CPU)
1021  return impl::str_scan_ndelim_cpu_r(str, end, delim1, delim2);
1022 #else
1023  return impl::str_scan_ndelim_default_r(str, end, delim1, delim2);
1024 #endif
1025 }
1026 
1037 inline const char* str_scan_delim(const char* str, const char* end, char delim1, char delim2) {
1038  assert( str != NULL );
1039  assert( str <= end );
1040 #if defined(EVO_CPU)
1041  return impl::str_scan_delim_cpu(str, end, delim1, delim2);
1042 #else
1043  return impl::str_scan_delim_default(str, end, delim1, delim2);
1044 #endif
1045 }
1046 
1058 inline const char* str_scan_delim_r(const char* str, const char* end, char delim1, char delim2) {
1059  assert( str != NULL );
1060  assert( str <= end );
1061 #if defined(EVO_CPU)
1062  return impl::str_scan_delim_cpu_r(str, end, delim1, delim2);
1063 #else
1064  return impl::str_scan_delim_default_r(str, end, delim1, delim2);
1065 #endif
1066 }
1067 
1079 inline const char* str_scan_delim(const char* str, const char* end, const char* delims, uint delim_count) {
1080  assert( str != NULL );
1081  assert( str <= end );
1082  assert( delim_count > 0 );
1083 #if defined(EVO_CPU)
1084  switch (delim_count) {
1085  case 1: {
1086  str = (char*)::memchr(str, delims[0], end - str);
1087  return (str == NULL ? end : str);
1088  }
1089  case 2:
1090  return impl::str_scan_delim_cpu(str, end, delims[0], delims[1]);
1091  default:
1092  if (delim_count <= impl::SSE_BATCH_SIZE)
1093  return impl::str_scan_delim_cpu(str, end, delims, delim_count);
1094  break;
1095  }
1096 #endif
1097  return impl::str_scan_delim_default(str, end, delims, delim_count);
1098 }
1099 
1112 inline const char* str_scan_delim_r(const char* str, const char* end, const char* delims, uint delim_count) {
1113  assert( str != NULL );
1114  assert( str <= end );
1115  assert( delim_count > 0 );
1116 #if defined(EVO_CPU)
1117  switch (delim_count) {
1118  case 1: {
1119  end = string_memrchr(str, delims[0], end - str);
1120  return (end == NULL ? str : end + 1);
1121  }
1122  case 2:
1123  return impl::str_scan_delim_cpu_r(str, end, delims[0], delims[1]);
1124  default:
1125  if (delim_count <= impl::SSE_BATCH_SIZE)
1126  return impl::str_scan_delim_cpu_r(str, end, delims, delim_count);
1127  break;
1128  }
1129 #endif
1130  return impl::str_scan_delim_default_r(str, end, delims, delim_count);
1131 }
1132 
1143 inline bool str_scan_backtickdel(const char* str, const char* end) {
1144  assert( str != NULL );
1145  assert( str <= end );
1146  const char DEL_CHAR = 0x7F;
1147  --end; // exclude last char at first
1148  while (str < end) {
1149  str = (char*)::memchr(str, '`', end - str);
1150  if (str == NULL)
1151  break;
1152  if (*++str == DEL_CHAR)
1153  return true;
1154  }
1155  return false;
1156 }
1157 
1172 inline const char* str_scan_endq(const char*& startq, const char*& endq, const char* str, const char* end) {
1173  if (str == end) {
1174  startq = str;
1175  endq = end;
1176  return end;
1177  }
1178  assert( str < end );
1179 
1180  const char* p;
1181  switch (*str) {
1182  case '`': {
1183  const char DEL_CHAR = 0x7F;
1184  if (end >= str + 4 && str[1] == DEL_CHAR) {
1185  // Backtick + DEL quoted
1186  p = str + 2;
1187  for (;; ++p) {
1188  p = (char*)::memchr(p, '`', end - p);
1189  if (p == NULL || p + 1 >= end)
1190  break;
1191  if (p[1] == DEL_CHAR) {
1192  startq = str + 2;
1193  endq = p;
1194  return p + 2;
1195  }
1196  }
1197  }
1198  } // fallthrough
1199  case '\'':
1200  case '"': {
1201  const char q = *str;
1202  if (end >= str + 6 && str[1] == q && str[2] == q) {
1203  // Triple char quoted
1204  p = str + 3;
1205  for (;;) {
1206  p = (char*)::memchr(p, q, end - p);
1207  if (p == NULL || p + 2 >= end)
1208  break;
1209  if (p[1] == q) {
1210  if (p[2] == q) {
1211  while (p + 3 < end && p[3] == q)
1212  ++p; // include additional quote chars
1213  startq = str + 3;
1214  endq = p;
1215  return p + 3;
1216  } else
1217  p += 3;
1218  } else
1219  p += 2;
1220  }
1221  }
1222 
1223  // Single char quoted
1224  p = str + 1;
1225  p = (char*)::memchr(p, q, end - p);
1226  if (p == NULL)
1227  break;
1228  startq = str + 1;
1229  endq = p;
1230  return p + 1;
1231  }
1232  default:
1233  break;
1234  }
1235 
1236  // Unquoted
1237  startq = str;
1238  endq = end;
1239  return end;
1240 }
1241 
1257 inline const char* str_scan_endq(const char*& startq, const char*& endq, const char* str, const char* end, char delim) {
1258  if (str == end) {
1259  startq = str;
1260  endq = end;
1261  return end;
1262  }
1263  assert( str < end );
1264 
1265  const char* p;
1266  switch (*str) {
1267  case '`': {
1268  const char DEL_CHAR = 0x7F;
1269  if (end >= str + 4 && str[1] == DEL_CHAR) {
1270  // Backtick + DEL quoted
1271  p = str + 2;
1272  for (;;) {
1273  p = (char*)::memchr(p, '`', end - p);
1274  if (p == NULL || p + 1 >= end)
1275  break;
1276  if (p[1] == DEL_CHAR) {
1277  endq = p;
1278  p = str_scan_nws(p + 2, end, delim);
1279  if (p < end && *p != delim)
1280  continue; // no delim/end here
1281  startq = str + 2;
1282  return p;
1283  }
1284  ++p;
1285  }
1286  }
1287  } // fallthrough
1288  case '\'':
1289  case '"': {
1290  const char q = *str;
1291  if (end >= str + 6 && str[1] == q && str[2] == q) {
1292  // Triple char quoted
1293  p = str + 3;
1294  for (;;) {
1295  p = (char*)::memchr(p, q, end - p);
1296  if (p == NULL || p + 2 >= end)
1297  break;
1298  if (p[1] == q) {
1299  if (p[2] == q) {
1300  while (p + 3 < end && p[3] == q)
1301  ++p; // include additional quote chars
1302  endq = p;
1303  p = str_scan_nws(p + 3, end, delim);
1304  if (p < end && *p != delim)
1305  continue; // no delim/end here
1306  startq = str + 3;
1307  return p;
1308  } else
1309  p += 3;
1310  } else
1311  p += 2;
1312  }
1313  }
1314 
1315  // Single char quoted
1316  p = str + 1;
1317  for (;;) {
1318  p = (char*)::memchr(p, q, end - p);
1319  if (p == NULL)
1320  break;
1321  endq = p;
1322  p = str_scan_nws(p + 1, end, delim);
1323  if (p < end && *p != delim)
1324  continue; // no delim/end here
1325  startq = str + 1;
1326  return p;
1327  }
1328  break;
1329  }
1330  default:
1331  break;
1332  }
1333 
1334  // Unquoted
1335  startq = str;
1336  p = (char*)::memchr(str, delim, end - str);
1337  if (p == NULL) {
1338  endq = end;
1339  return end;
1340  }
1341  endq = p;
1342  return p;
1343 }
1344 
1362 inline const char* str_scan_endq_r(const char*& startq, const char*& endq, const char* str, const char* end, char delim) {
1363  if (str == end) {
1364  startq = str;
1365  endq = end;
1366  return str;
1367  }
1368  assert( str < end );
1369 
1370  const char DEL_CHAR = 0x7F;
1371  const char* p;
1372  switch (*(end - 1)) {
1373  case DEL_CHAR: {
1374  if (end >= str + 4 && *(end - 2) == '`') {
1375  p = end - 2;
1376  for (;;) {
1377  p = string_memrchr(str, DEL_CHAR, p - str);
1378  if (p == NULL || p == str)
1379  break;
1380  if (*--p == '`') {
1381  startq = p + 2;
1382  p = str_scan_nws_r(str, p, delim);
1383  if (p > str && *(p - 1) != delim)
1384  continue; // no delim/end here
1385  endq = end - 2;
1386  return p;
1387  }
1388  }
1389  }
1390  break;
1391  }
1392  case '`':
1393  case '\'':
1394  case '"': {
1395  const char q = *(end - 1);
1396  if (end >= str + 6 && *(end - 2) == q && *(end - 3) == q) {
1397  // Triple char quoted
1398  p = end - 3;
1399  for (;;) {
1400  p = string_memrchr(str, q, p - str);
1401  if (p == NULL || p - 2 < str)
1402  break;
1403  if (*--p == q) {
1404  if (*--p == q) {
1405  while (p > str && *(p - 1) == q)
1406  --p; // include additional quote chars
1407  startq = p + 3;
1408  p = str_scan_nws_r(str, p, delim);
1409  if (p > str && *(p - 1) != delim)
1410  continue; // no delim/end here
1411  endq = end - 3;
1412  return p;
1413  }
1414  }
1415  }
1416  }
1417 
1418  // Single char quoted
1419  p = end - 1;
1420  for (;;) {
1421  p = string_memrchr(str, q, p - str);
1422  if (p == NULL)
1423  break;
1424  startq = p + 1;
1425  p = str_scan_nws_r(str, p, delim);
1426  if (p > str && *(p - 1) != delim)
1427  continue; // no delim/end here
1428  endq = end - 1;
1429  return p;
1430  }
1431  break;
1432  }
1433  default:
1434  break;
1435  }
1436 
1437  // Unquoted
1438  endq = end;
1439  p = string_memrchr(str, delim, end - str);
1440  if (p == NULL) {
1441  startq = str;
1442  return str;
1443  }
1444  startq = p + 1;
1445  return startq;
1446 }
1447 
1464 inline const char* str_scan_endq(const char*& startq, const char*& endq, const char* str, const char* end, char delim1, char delim2) {
1465  if (str == end) {
1466  startq = str;
1467  endq = end;
1468  return end;
1469  }
1470  assert( str < end );
1471 
1472  const char* p;
1473  switch (*str) {
1474  case '`': {
1475  const char DEL_CHAR = 0x7F;
1476  if (end >= str + 4 && str[1] == DEL_CHAR) {
1477  // Backtick + DEL quoted
1478  p = str + 2;
1479  for (;;) {
1480  p = (char*)::memchr(p, '`', end - p);
1481  if (p == NULL || p + 1 >= end)
1482  break;
1483  if (p[1] == DEL_CHAR) {
1484  endq = p;
1485  p = str_scan_nws(p + 2, end);
1486  if (p < end && *p != delim1 && *p != delim2)
1487  continue; // no delim/end here
1488  startq = str + 2;
1489  return p;
1490  }
1491  ++p;
1492  }
1493  }
1494  } // fallthrough
1495  case '\'':
1496  case '"': {
1497  const char q = *str;
1498  if (end >= str + 6 && str[1] == q && str[2] == q) {
1499  // Triple char quoted
1500  p = str + 3;
1501  for (;;) {
1502  p = (char*)::memchr(p, q, end - p);
1503  if (p == NULL || p + 2 >= end)
1504  break;
1505  if (p[1] == q) {
1506  if (p[2] == q) {
1507  while (p + 3 < end && p[3] == q)
1508  ++p; // include additional quote chars
1509  endq = p;
1510  p = str_scan_nws(p + 3, end);
1511  if (p < end && *p != delim1 && *p != delim2)
1512  continue; // no delim/end here
1513  startq = str + 3;
1514  return p;
1515  } else
1516  p += 3;
1517  } else
1518  p += 2;
1519  }
1520  }
1521 
1522  // Single char quoted
1523  p = str + 1;
1524  for (;;) {
1525  p = (char*)::memchr(p, q, end - p);
1526  if (p == NULL)
1527  break;
1528  endq = p;
1529  p = str_scan_nws(p + 1, end);
1530  if (p < end && *p != delim1 && *p != delim2)
1531  continue; // no delim/end here
1532  startq = str + 1;
1533  return p;
1534  }
1535  break;
1536  }
1537  default:
1538  break;
1539  }
1540 
1541  // Unquoted
1542  startq = str;
1543  p = str_scan_delim(str, end, delim1, delim2);
1544  if (p == end) {
1545  endq = end;
1546  return end;
1547  }
1548  endq = p;
1549  return p;
1550 }
1551 
1570 inline const char* str_scan_endq(const char*& startq, const char*& endq, const char* str, const char* end, const char* delims, uint delim_count, char ws_delim=0) {
1571  if (str == end) {
1572  startq = str;
1573  endq = end;
1574  return end;
1575  }
1576  assert( str < end );
1577  assert( delim_count > 0 );
1578 
1579  const char* p;
1580  switch (*str) {
1581  case '`': {
1582  const char DEL_CHAR = 0x7F;
1583  if (end >= str + 4 && str[1] == DEL_CHAR) {
1584  // Backtick + DEL quoted
1585  p = str + 2;
1586  for (;;) {
1587  p = (char*)::memchr(p, '`', end - p);
1588  if (p == NULL || p + 1 >= end)
1589  break;
1590  if (p[1] == DEL_CHAR) {
1591  endq = p;
1592  p = str_scan_nws(p + 2, end, ws_delim);
1593  if (p < end && ::memchr(delims, *p, delim_count) == NULL)
1594  continue; // no delim/end here
1595  startq = str + 2;
1596  return p;
1597  }
1598  ++p;
1599  }
1600  }
1601  } // fallthrough
1602  case '\'':
1603  case '"': {
1604  const char q = *str;
1605  if (end >= str + 6 && str[1] == q && str[2] == q) {
1606  // Triple char quoted
1607  p = str + 3;
1608  for (;;) {
1609  p = (char*)::memchr(p, q, end - p);
1610  if (p == NULL || p + 2 >= end)
1611  break;
1612  if (p[1] == q) {
1613  if (p[2] == q) {
1614  while (p + 3 < end && p[3] == q)
1615  ++p; // include additional quote chars
1616  endq = p;
1617  p = str_scan_nws(p + 3, end, ws_delim);
1618  if (p < end && ::memchr(delims, *p, delim_count) == NULL)
1619  continue; // no delim/end here
1620  startq = str + 3;
1621  return p;
1622  } else
1623  p += 3;
1624  } else
1625  p += 2;
1626  }
1627  }
1628 
1629  // Single char quoted
1630  p = str + 1;
1631  for (;;) {
1632  p = (char*)::memchr(p, q, end - p);
1633  if (p == NULL)
1634  break;
1635  endq = p;
1636  p = str_scan_nws(p + 1, end, ws_delim);
1637  if (p < end && ::memchr(delims, *p, delim_count) == NULL)
1638  continue; // no delim/end here
1639  startq = str + 1;
1640  return p;
1641  }
1642  break;
1643  }
1644  default:
1645  break;
1646  }
1647 
1648  // Unquoted
1649  startq = str;
1650  p = str_scan_delim(str, end, delims, delim_count);
1651  if (p == end) {
1652  endq = end;
1653  return end;
1654  }
1655  endq = p;
1656  return p;
1657 }
1658 
1679 inline const char* str_scan_endq_r(const char*& startq, const char*& endq, const char* str, const char* end, const char* delims, uint delim_count, char ws_delim=0) {
1680  if (str == end) {
1681  startq = str;
1682  endq = end;
1683  return str;
1684  }
1685  assert( str < end );
1686  assert( delim_count > 0 );
1687 
1688  const char DEL_CHAR = 0x7F;
1689  const char* p;
1690  switch (*(end - 1)) {
1691  case DEL_CHAR: {
1692  if (end >= str + 4 && *(end - 2) == '`') {
1693  p = end - 2;
1694  for (;;) {
1695  p = string_memrchr(str, DEL_CHAR, p - str);
1696  if (p == NULL || p == str)
1697  break;
1698  if (*--p == '`') {
1699  startq = p + 2;
1700  p = str_scan_nws_r(str, p, ws_delim);
1701  if (p > str && ::memchr(delims, *(p - 1), delim_count) == NULL)
1702  continue; // no delim/end here
1703  endq = end - 2;
1704  return p;
1705  }
1706  }
1707  }
1708  break;
1709  }
1710  case '`':
1711  case '\'':
1712  case '"': {
1713  const char q = *(end - 1);
1714  if (end >= str + 6 && *(end - 2) == q && *(end - 3) == q) {
1715  // Triple char quoted
1716  p = end - 3;
1717  for (;;) {
1718  p = string_memrchr(str, q, p - str);
1719  if (p == NULL || p - 2 < str)
1720  break;
1721  if (*--p == q) {
1722  if (*--p == q) {
1723  while (p > str && *(p - 1) == q)
1724  --p; // include additional quote chars
1725  startq = p + 3;
1726  p = str_scan_nws_r(str, p, ws_delim);
1727  if (p > str && ::memchr(delims, *(p - 1), delim_count) == NULL)
1728  continue; // no delim/end here
1729  endq = end - 3;
1730  return p;
1731  }
1732  }
1733  }
1734  }
1735 
1736  // Single char quoted
1737  p = end - 1;
1738  for (;;) {
1739  p = string_memrchr(str, q, p - str);
1740  if (p == NULL)
1741  break;
1742  startq = p + 1;
1743  p = str_scan_nws_r(str, p, ws_delim);
1744  if (p > str && ::memchr(delims, *(p - 1), delim_count) == NULL)
1745  continue; // no delim/end here
1746  endq = end - 1;
1747  return p;
1748  }
1749  break;
1750  }
1751  default:
1752  break;
1753  }
1754 
1755  // Unquoted
1756  endq = end;
1757  p = str_scan_delim_r(str, end, delims, delim_count);
1758  if (p == str) {
1759  startq = str;
1760  return str;
1761  }
1762  startq = p;
1763  return startq;
1764 }
1765 
1775 inline const char* str_scan_to(uint maxlen, const char* str, const char* end, char ch) {
1776  if (str == end)
1777  return end;
1778  assert( str < end );
1779  if (maxlen > 0) {
1780  const char* maxlen_end = str + maxlen;
1781  if (maxlen_end < end)
1782  return (char*)::memchr(str, ch, maxlen_end - str);
1783  }
1784  str = (char*)::memchr(str, ch, end - str);
1785  if (str == NULL)
1786  return end;
1787  return str;
1788 }
1789 
1801 inline const char* str_scan_to(uint maxlen, const char* str, const char* end, char ch1, char ch2) {
1802  if (str == end)
1803  return end;
1804  assert( str < end );
1805  if (maxlen > 0) {
1806  const char* maxlen_end = str + maxlen;
1807  if (maxlen_end < end) {
1808  str = str_scan_delim(str, maxlen_end, ch1, ch2);
1809  if (str < maxlen_end)
1810  return str;
1811  return NULL;
1812  }
1813  }
1814  return str_scan_delim(str, end, ch1, ch2);
1815 }
1816 
1825 template<class T>
1826 inline const char* str_scan_decimal(T& num, const char* str, const char* end) {
1827  const T BASE = 10;
1828  const T limitbase = IntegerT<T>::MAX / BASE;
1829  const T limitdig = IntegerT<T>::MAX % BASE;
1830  char ch;
1831  const char* p = str;
1832  for (; p < end; ++p) {
1833  ch = *p;
1834  if (ch < '0' || ch > '9')
1835  return p;
1836  ch -= '0';
1837  if (num > limitbase || (num == limitbase && (T)ch > limitdig))
1838  return NULL;
1839  num *= BASE;
1840  num += (T)ch;
1841  }
1842  if (p == str)
1843  return NULL;
1844  return p;
1845 }
1846 
1855 template<class T>
1856 inline const char* str_scan_hex(T& num, const char* str, const char* end) {
1857  const T BASE = 16;
1858  const T limitbase = IntegerT<T>::MAX / BASE;
1859  char ch;
1860  const char* p = str;
1861  for (; p < end; ++p) {
1862  ch = *p;
1863  if (ch <= '9') {
1864  if (ch < '0')
1865  return p;
1866  ch -= '0';
1867  } else if (ch >= 'a') {
1868  if (ch > 'f')
1869  return p;
1870  ch -= ('a' - 10);
1871  } else if (ch <= 'F') {
1872  if (ch < 'A')
1873  return p;
1874  ch -= ('A' - 10);
1875  } else
1876  return p;
1877  if (num > limitbase)
1878  return NULL;
1879  num *= BASE;
1880  num += (T)ch;
1881  }
1882  if (p == str)
1883  return NULL;
1884  return p;
1885 }
1886 
1888 
1893 struct StrQuoting {
1894  static const char DEL_CHAR = 0x7F;
1895 
1897  enum Type {
1905  tERROR
1906  };
1907 
1908  /* Scan string data and determine required quoting type to make it parsable.
1909  - This scans the string for quote characters -- for best performance specify a delimiter with: get(bool&,const char*,ulong,char)
1910  - This uses Smart Quoting -- see \ref SmartQuoting
1911  .
1912  \param data %String data pointer
1913  \param data_size %String size
1914  \param delim Delimiter that may follow the quoted string
1915  \return Quoting type for string data
1916  */
1917  static Type get(const char* data, ulong data_size) {
1918  if (data_size == 0)
1919  return tSINGLE;
1920  assert( data != NULL );
1921 
1922  const uint DELIMS_COUNT = 3;
1923  const char* DELIMS = "'\"`";
1924  const char* save_ptr[DELIMS_COUNT];
1925 
1926  // Check for quote chars, save quote pointer for triple-quote checks below
1927  const char* p;
1928  for (ulong i = 0; i < DELIMS_COUNT; ++i) {
1929  p = (char*)::memchr(data, DELIMS[i], data_size);
1930  if (p == NULL)
1931  return (Type)i;
1932  save_ptr[i] = p;
1933  }
1934 
1935  // All quote chars found, look for unused triple-quotes
1936  const char* end = data + data_size - 2;
1937  for (ulong i = 0; i < DELIMS_COUNT; ++i) {
1938  const char ch = DELIMS[i];
1939  p = save_ptr[i];
1940  for (;;) {
1941  if (p < end) {
1942  if (p[1] == ch && p[2] == ch)
1943  break;
1944  if ((p = (char*)::memchr(p + 1, ch, end - p - 1)) != NULL)
1945  continue;
1946  }
1947  return (Type)(i + DELIMS_COUNT);
1948  }
1949  }
1950 
1951  // Fallback to backtick + del (rare)
1952  if (str_scan_backtickdel(data, data + data_size))
1953  return tERROR;
1954  return tBACKTICK_DEL;
1955  }
1956 
1957  /* Scan string data and determine required quoting type to make it parsable.
1958  - This scans the string for the delimiter and checks quote chars before all delimiters to determine the best quoting type to use
1959  - This uses Smart Quoting -- see \ref SmartQuoting
1960  .
1961  \param optional %Set to whether string data may be left unquoted [out]
1962  \param data %String data pointer
1963  \param data_size %String size
1964  \param delim Delimiter a parser expects after the quoted string
1965  \return Quoting type for string data, tERROR if data is unquotable (invalid text)
1966  */
1967  static Type get(bool& optional, const char* data, ulong data_size, char delim) {
1968  if (data_size == 0) {
1969  optional = true;
1970  return tSINGLE;
1971  } else if (data_size == 1) {
1972  optional = (*data != delim && *data > ' ');
1973  return (*data == '\'' ? tDOUBLE : tSINGLE);
1974  }
1975  assert( data != NULL );
1976 
1977  bool block_backtick_del = false;
1978  const char* end = data + data_size;
1979  const char* p = (char*)::memchr(data, delim, data_size);
1980  if (p == NULL) {
1981  // No delim, check first and last chars to determine quoting
1982  switch (*data) {
1983  case '\'':
1984  optional = false;
1985  if (*(end - 1) == '"')
1986  return tBACKTICK;
1987  return tDOUBLE;
1988  case '"':
1989  optional = false;
1990  if (*(end - 1) == '\'')
1991  return tBACKTICK;
1992  return tSINGLE;
1993  case '`':
1994  optional = false;
1995  if (*(end - 1) == '\'')
1996  return tDOUBLE;
1997  return tSINGLE;
1998  default:
1999  switch (*(end - 1)) {
2000  case '\'':
2001  optional = false;
2002  return tDOUBLE;
2003  case '"':
2004  case '`':
2005  optional = false;
2006  return tSINGLE;
2007  default:
2008  optional = (*data > ' ' && *(end - 1) > ' ');
2009  return tSINGLE;
2010  }
2011  }
2012  } else {
2013  enum {
2014  ALLOW = 0, // ok to use
2015  BLOCK, // ruled out, do not use
2016  AVOID // avoid, use if all others are blocked
2017  };
2018  const uint QUOTING_SIZE = 6;
2019  char quoting[QUOTING_SIZE] = { 0, 0, 0, 0, 0, 0 }; // value per quote type, '"` then same as triples
2020  char ch;
2021  optional = false;
2022 
2023  // Quotes used at end should be avoided
2024  ch = *(end - 1);
2025  switch (ch) {
2026  case '\'':
2027  quoting[(uint)tSINGLE] = (char)AVOID;
2028  quoting[(uint)tSINGLE3] = (char)AVOID;
2029  break;
2030  case '"':
2031  quoting[(uint)tDOUBLE] = (char)AVOID;
2032  quoting[(uint)tDOUBLE3] = (char)AVOID;
2033  break;
2034  case '`':
2035  quoting[(uint)tBACKTICK] = (char)AVOID;
2036  quoting[(uint)tBACKTICK3] = (char)AVOID;
2037  break;
2038  default:
2039  break;
2040  }
2041 
2042  // Quotes used at beginning should be avoided, block single quote char if ambiguous
2043  ch = *data;
2044  switch (ch) {
2045  case '\'':
2046  if (data_size > 1 && data[1] == ch)
2047  quoting[(uint)tSINGLE] = (char)BLOCK;
2048  else
2049  quoting[(uint)tSINGLE] = (char)AVOID;
2050  quoting[(uint)tSINGLE3] = (char)AVOID;
2051  break;
2052  case '"':
2053  if (data_size > 1 && data[1] == ch)
2054  quoting[(uint)tDOUBLE] = (char)BLOCK;
2055  else
2056  quoting[(uint)tDOUBLE] = (char)AVOID;
2057  quoting[(uint)tDOUBLE3] = (char)AVOID;
2058  break;
2059  case '`':
2060  if (data_size > 1 && data[1] == ch)
2061  quoting[(uint)tBACKTICK] = (char)BLOCK;
2062  else
2063  quoting[(uint)tBACKTICK] = (char)AVOID;
2064  quoting[(uint)tBACKTICK3] = (char)AVOID;
2065  break;
2066  default:
2067  break;
2068  }
2069 
2070  // Block quote types found right before a delim
2071  const char* data_plus_3 = data + 3;
2072  do {
2073  if (p > data) {
2074  ch = *(p - 1);
2075  switch (ch) {
2076  case '\'':
2077  quoting[(uint)tSINGLE] = (char)BLOCK;
2078  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2079  quoting[(uint)tSINGLE3] = (char)BLOCK;
2080  break;
2081  case '"':
2082  quoting[(uint)tDOUBLE] = 1;
2083  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2084  quoting[(uint)tDOUBLE3] = (char)BLOCK;
2085  break;
2086  case '`':
2087  quoting[(uint)tBACKTICK] = 1;
2088  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2089  quoting[(uint)tBACKTICK3] = (char)BLOCK;
2090  break;
2091  case DEL_CHAR:
2092  if (p >= data + 2 && *(p - 2) == '`')
2093  block_backtick_del = true;
2094  break;
2095  default:
2096  break;
2097  }
2098  }
2099  ++p;
2100  p = (char*)::memchr(p, delim, end - p);
2101  } while (p != NULL);
2102 
2103  // Use first allowed quoting type
2104  p = (char*)::memchr(quoting, ALLOW, QUOTING_SIZE);
2105  if (p != NULL)
2106  return (Type)(p - quoting);
2107 
2108  // Try first non-preferred quoting type (marked AVOID but not BLOCKED)
2109  p = (char*)::memchr(quoting, AVOID, QUOTING_SIZE);
2110  if (p != NULL)
2111  return (Type)(p - quoting);
2112  }
2113 
2114  // Fallback to backtick + del (rare)
2115  if (block_backtick_del)
2116  return tERROR;
2117  return tBACKTICK_DEL;
2118  }
2119 
2120  /* Scan string data and determine required quoting type to make it parsable.
2121  - This scans the string for the delimiter and checks quote chars before all delimiters to determine the best quoting type to use
2122  - This uses Smart Quoting -- see \ref SmartQuoting
2123  .
2124  \param data %String data pointer
2125  \param data_size %String size
2126  \param delim Delimiter a parser expects after the quoted string
2127  \return Quoting type for string data, tERROR if data is unquotable (invalid text)
2128  */
2129  static Type get(const char* data, ulong data_size, char delim) {
2130  bool optional;
2131  return get(optional, data, data_size, delim);
2132  }
2133 
2134  /* Scan string data and determine required quoting type to make it parsable.
2135  - This scans the string for a delimiter and checks quote chars before all delimiters to determine the best quoting type to use
2136  - This uses Smart Quoting -- see \ref SmartQuoting
2137  .
2138  \param data %String data pointer
2139  \param data_size %String size
2140  \param delim1 Delimiter a parser may expect after the quoted string
2141  \param delim2 Another delimiter a parser may expect after the quoted string
2142  \return Quoting type for string data, tERROR if data is unquotable (invalid text)
2143  */
2144  static Type get(const char* data, ulong data_size, char delim1, char delim2) {
2145  if (data_size == 0)
2146  return tSINGLE;
2147  else if (data_size == 1)
2148  return (*data == '\'' ? tDOUBLE : tSINGLE);
2149  assert( data != NULL );
2150 
2151  bool block_backtick_del = false;
2152  const char* end = data + data_size;
2153  const char* p = str_scan_delim(data, end, delim1, delim2);
2154  if (p >= end) {
2155  // No delim, check first and last chars to determine quoting
2156  switch (*data) {
2157  case '\'':
2158  if (*(end - 1) == '"')
2159  return tBACKTICK;
2160  return tDOUBLE;
2161  case '"':
2162  if (*(end - 1) == '\'')
2163  return tBACKTICK;
2164  return tSINGLE;
2165  case '`':
2166  if (*(end - 1) == '\'')
2167  return tDOUBLE;
2168  return tSINGLE;
2169  default:
2170  switch (*(end - 1)) {
2171  case '\'':
2172  return tDOUBLE;
2173  case '"':
2174  case '`':
2175  return tSINGLE;
2176  default:
2177  return tSINGLE;
2178  }
2179  }
2180  } else {
2181  enum {
2182  ALLOW = 0, // ok to use
2183  BLOCK, // ruled out, do not use
2184  AVOID // avoid, use if all others are blocked
2185  };
2186  const uint QUOTING_SIZE = 6;
2187  char quoting[QUOTING_SIZE] = { 0, 0, 0, 0, 0, 0 }; // value per quote type, '"` then same as triples
2188  char ch;
2189 
2190  // Quotes used at end should be avoided
2191  ch = *(end - 1);
2192  switch (ch) {
2193  case '\'':
2194  quoting[(uint)tSINGLE] = (char)AVOID;
2195  quoting[(uint)tSINGLE3] = (char)AVOID;
2196  break;
2197  case '"':
2198  quoting[(uint)tDOUBLE] = (char)AVOID;
2199  quoting[(uint)tDOUBLE3] = (char)AVOID;
2200  break;
2201  case '`':
2202  quoting[(uint)tBACKTICK] = (char)AVOID;
2203  quoting[(uint)tBACKTICK3] = (char)AVOID;
2204  break;
2205  default:
2206  break;
2207  }
2208 
2209  // Quotes used at beginning should be avoided, block single quote char if ambiguous
2210  ch = *data;
2211  switch (ch) {
2212  case '\'':
2213  if (data_size > 1 && data[1] == ch)
2214  quoting[(uint)tSINGLE] = (char)BLOCK;
2215  else
2216  quoting[(uint)tSINGLE] = (char)AVOID;
2217  quoting[(uint)tSINGLE3] = (char)AVOID;
2218  break;
2219  case '"':
2220  if (data_size > 1 && data[1] == ch)
2221  quoting[(uint)tDOUBLE] = (char)BLOCK;
2222  else
2223  quoting[(uint)tDOUBLE] = (char)AVOID;
2224  quoting[(uint)tDOUBLE3] = (char)AVOID;
2225  break;
2226  case '`':
2227  if (data_size > 1 && data[1] == ch)
2228  quoting[(uint)tBACKTICK] = (char)BLOCK;
2229  else
2230  quoting[(uint)tBACKTICK] = (char)AVOID;
2231  quoting[(uint)tBACKTICK3] = (char)AVOID;
2232  break;
2233  default:
2234  break;
2235  }
2236 
2237  // Block quote types found right before a delim
2238  const char* data_plus_3 = data + 3;
2239  do {
2240  if (p > data) {
2241  ch = *(p - 1);
2242  switch (ch) {
2243  case '\'':
2244  quoting[(uint)tSINGLE] = (char)BLOCK;
2245  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2246  quoting[(uint)tSINGLE3] = (char)BLOCK;
2247  break;
2248  case '"':
2249  quoting[(uint)tDOUBLE] = 1;
2250  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2251  quoting[(uint)tDOUBLE3] = (char)BLOCK;
2252  break;
2253  case '`':
2254  quoting[(uint)tBACKTICK] = 1;
2255  if (p >= data_plus_3 && *(p - 2) == ch && *(p - 3) == ch)
2256  quoting[(uint)tBACKTICK3] = (char)BLOCK;
2257  break;
2258  case DEL_CHAR:
2259  if (p >= data + 2 && *(p - 2) == '`')
2260  block_backtick_del = true;
2261  break;
2262  default:
2263  break;
2264  }
2265  }
2266  p = str_scan_delim(++p, end, delim1, delim2);
2267  } while (p < end);
2268 
2269  // Use first allowed quoting type
2270  p = (char*)::memchr(quoting, ALLOW, QUOTING_SIZE);
2271  if (p != NULL)
2272  return (Type)(p - quoting);
2273 
2274  // Try first non-preferred quoting type (marked AVOID but not BLOCKED)
2275  p = (char*)::memchr(quoting, AVOID, QUOTING_SIZE);
2276  if (p != NULL)
2277  return (Type)(p - quoting);
2278  }
2279 
2280  // Fallback to backtick + del (rare)
2281  if (block_backtick_del)
2282  return tERROR;
2283  return tBACKTICK_DEL;
2284  }
2285 };
2286 
2288 
2289 }
2290 #endif
const char * str_scan_endq_r(const char *&startq, const char *&endq, const char *str, const char *end, char delim)
Scan string pointer in reverse and extract quoted or unquoted text with a delimiter and return new en...
Definition: strscan.h:1362
const char * str_scan_delim(const char *str, const char *end, char delim1, char delim2)
Scan string pointer for next delimiter and return stop pointer.
Definition: strscan.h:1037
const char * str_scan_ndelim(const char *str, const char *end, char delim1, char delim2)
Scan string pointer for next non-delimiter and return stop pointer.
Definition: strscan.h:996
Evo String container.
Single-quotes: &#39;
Definition: strscan.h:1898
Backtick: `
Definition: strscan.h:1900
Triple backtick: ```
Definition: strscan.h:1903
Triple single-quotes: &#39;&#39;&#39;
Definition: strscan.h:1901
Evo implementation detail: String helpers.
bool str_scan_backtickdel(const char *str, const char *end)
Scan string for a Backtick + DEL pair.
Definition: strscan.h:1143
const char * str_scan_nws(const char *str, const char *end)
Scan string pointer for next non-whitespace character and return stop pointer.
Definition: strscan.h:877
Basic integer type.
Definition: type.h:980
const char * string_memrchr(const char *str, char ch, size_t size)
Evo implementation of memrchr() to search for character in reverse.
Definition: str.h:1150
const char * str_scan_to(uint maxlen, const char *str, const char *end, char ch)
Scan string pointer for char and return stop pointer.
Definition: strscan.h:1775
const char * str_scan_nws_r(const char *str, const char *end)
Scan string pointer for next non-whitespace character in reverse and return new end after stop pointe...
Definition: strscan.h:896
const char * str_scan_endq(const char *&startq, const char *&endq, const char *str, const char *end)
Scan string pointer and extract quoted or unquoted text.
Definition: strscan.h:1172
static const int BITS
Type size in bits.
Definition: type.h:986
Double-quotes: "
Definition: strscan.h:1899
Triple double-quotes: """
Definition: strscan.h:1902
Evo C++ Library namespace.
Definition: alg.h:11
const char * str_scan_decimal(T &num, const char *str, const char *end)
Scan string pointer for decimal number and return stop pointer.
Definition: strscan.h:1826
Backtick followed by DEL char (7F) – last resort (rare)
Definition: strscan.h:1904
const char * str_scan_ndelim_r(const char *str, const char *end, char delim1, char delim2)
Scan string pointer for next non-delimiter in reverse and return new end after stop pointer...
Definition: strscan.h:1017
const char * str_scan_hex(T &num, const char *str, const char *end)
Scan string pointer for hex number and return stop pointer.
Definition: strscan.h:1856
const char * str_scan_delim_r(const char *str, const char *end, char delim1, char delim2)
Scan string pointer for next delimiter in reverse and return new end after stop pointer.
Definition: strscan.h:1058
Helpers for determining quoting type to use with string data.
Definition: strscan.h:1893
Evo basic types and traits.
Type
Quoting type.
Definition: strscan.h:1897