stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
parser.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023 Amebis
4*/
5
6#pragma once
7
8#include "interval.hpp"
9#include "memory.hpp"
10#include "sal.hpp"
11#include "sgml.hpp"
12#include "string.hpp"
13#include <assert.h>
14#include <stdarg.h>
15#include <stdint.h>
16#include <limits>
17#include <list>
18#include <memory>
19#include <set>
20#include <string>
21#ifdef _WIN32
22#include <winsock2.h>
23#else
24#include <inaddr.h>
25#include <in6addr.h>
26#endif
27
28#ifdef _MSC_VER
29#pragma warning(push)
30#pragma warning(disable: 4100)
31#endif
32
33namespace stdex
34{
35 namespace parser
36 {
40 constexpr int match_default = 0;
41 constexpr int match_case_insensitive = 0x1;
42 constexpr int match_multiline = 0x2;
43
47 template <class T>
49 {
50 public:
51 basic_tester(_In_ const std::locale& locale = std::locale()) : m_locale(locale) {}
52 virtual ~basic_tester() {}
53
54 bool search(
55 _In_reads_or_z_(end) const T* text,
56 _In_ size_t start = 0,
57 _In_ size_t end = (size_t)-1,
58 _In_ int flags = match_default)
59 {
60 for (size_t i = start; i < end && text[i]; i++)
61 if (match(text, i, end, flags))
62 return true;
63 return false;
64 }
65
66 virtual bool match(
67 _In_reads_or_z_(end) const T* text,
68 _In_ size_t start = 0,
69 _In_ size_t end = (size_t)-1,
70 _In_ int flags = match_default) = 0;
71
72 template<class _Traits, class _Ax>
73 inline bool match(
74 const std::basic_string<T, _Traits, _Ax>& text,
75 _In_ size_t start = 0,
76 _In_ size_t end = (size_t)-1,
77 _In_ int flags = match_default)
78 {
79 return match(text.c_str(), start, std::min<size_t>(end, text.size()), flags);
80 }
81
82 virtual void invalidate()
83 {
84 interval.start = 1;
85 interval.end = 0;
86 }
87
88 protected:
90 const wchar_t* next_sgml_cp(_In_ const char* text, _In_ size_t start, _In_ size_t end, _Out_ size_t& chr_end, _Out_ wchar_t(&buf)[3])
91 {
92 if (text[start] == '&') {
93 // Potential entity start
94 const auto& ctype = std::use_facet<std::ctype<T>>(m_locale);
95 for (chr_end = start + 1;; chr_end++) {
96 if (chr_end >= end || text[chr_end] == 0) {
97 // Unterminated entity
98 break;
99 }
100 if (text[chr_end] == ';') {
101 // Entity end
102 size_t n = chr_end - start - 1;
103 if (n >= 2 && text[start + 1] == '#') {
104 // Numerical entity
105 char32_t unicode;
106 if (text[start + 2] == 'x' || text[start + 2] == 'X')
107 unicode = strtou32(text + start + 3, n - 2, nullptr, 16);
108 else
109 unicode = strtou32(text + start + 2, n - 1, nullptr, 10);
110#ifdef _WIN32
111 if (unicode < 0x10000) {
112 buf[0] = (wchar_t)unicode;
113 buf[1] = 0;
114 }
115 else {
116 ucs4_to_surrogate_pair(buf, unicode);
117 buf[2] = 0;
118 }
119#else
120 buf[0] = (wchar_t)unicode;
121 buf[1] = 0;
122#endif
123 chr_end++;
124 return buf;
125 }
126 const wchar_t* entity_w = sgml2uni(text + start + 1, n);
127 if (entity_w) {
128 chr_end++;
129 return entity_w;
130 }
131 // Unknown entity.
132 break;
133 }
134 else if (text[chr_end] == '&' || ctype.is(ctype.space, text[chr_end])) {
135 // This char cannot possibly be a part of entity.
136 break;
137 }
138 }
139 }
140 buf[0] = text[start];
141 buf[1] = 0;
142 chr_end = start + 1;
143 return buf;
144 }
146
147 public:
149
150 protected:
151 const std::locale& m_locale;
152 };
153
156#ifdef _UNICODE
157 using ttester = wtester;
158#else
159 using ttester = tester;
160#endif
162
166 template <class T>
167 class basic_noop : public basic_tester<T>
168 {
169 public:
170 virtual bool match(
171 _In_reads_or_z_(end) const T* text,
172 _In_ size_t start = 0,
173 _In_ size_t end = (size_t)-1,
174 _In_ int flags = match_default)
175 {
176 assert(text || start >= end);
177 if (start < end && text[start]) {
178 interval.start = interval.end = start;
179 return true;
180 }
181 interval.start = (interval.end = start) + 1;
182 return false;
183 }
184 };
185
186 using noop = basic_noop<char>;
188#ifdef _UNICODE
189 using tnoop = wnoop;
190#else
191 using tnoop = noop;
192#endif
194
198 template <class T>
199 class basic_any_cu : public basic_tester<T>
200 {
201 public:
202 virtual bool match(
203 _In_reads_or_z_(end) const T* text,
204 _In_ size_t start = 0,
205 _In_ size_t end = (size_t)-1,
206 _In_ int flags = match_default)
207 {
208 assert(text || start >= end);
209 if (start < end && text[start]) {
210 interval.end = (interval.start = start) + 1;
211 return true;
212 }
213 interval.start = (interval.end = start) + 1;
214 return false;
215 }
216 };
217
220#ifdef _UNICODE
221 using tany_cu = wany_cu;
222#else
223 using tany_cu = any_cu;
224#endif
225
229 class sgml_any_cp : public basic_any_cu<char>
230 {
231 public:
232 virtual bool match(
233 _In_reads_or_z_(end) const char* text,
234 _In_ size_t start = 0,
235 _In_ size_t end = (size_t)-1,
236 _In_ int flags = match_default)
237 {
238 assert(text || start >= end);
239 if (start < end && text[start]) {
240 if (text[start] == '&') {
241 // SGML entity
242 const auto& ctype = std::use_facet<std::ctype<char>>(m_locale);
243 for (interval.end = start + 1; interval.end < end && text[interval.end]; interval.end++)
244 if (text[interval.end] == ';') {
245 interval.end++;
246 interval.start = start;
247 return true;
248 }
249 else if (text[interval.end] == '&' || ctype.is(ctype.space, text[interval.end]))
250 break;
251 // Unterminated entity
252 }
253 interval.end = (interval.start = start) + 1;
254 return true;
255 }
256 interval.start = (interval.end = start) + 1;
257 return false;
258 }
259 };
260
264 template <class T>
265 class basic_cu : public basic_tester<T>
266 {
267 public:
268 basic_cu(T chr, bool invert = false, _In_ const std::locale& locale = std::locale()) :
269 basic_tester<T>(locale),
270 m_chr(chr),
271 m_invert(invert)
272 {}
273
274 virtual bool match(
275 _In_reads_or_z_(end) const T* text,
276 _In_ size_t start = 0,
277 _In_ size_t end = (size_t)-1,
278 _In_ int flags = match_default)
279 {
280 assert(text || start >= end);
281 if (start < end && text[start]) {
282 bool r;
283 if (flags & match_case_insensitive) {
284 const auto& ctype = std::use_facet<std::ctype<T>>(m_locale);
285 r = ctype.tolower(text[start]) == ctype.tolower(m_chr);
286 }
287 else
288 r = text[start] == m_chr;
289 if (r && !m_invert || !r && m_invert) {
290 interval.end = (interval.start = start) + 1;
291 return true;
292 }
293 }
294 interval.start = (interval.end = start) + 1;
295 return false;
296 }
297
298 protected:
299 T m_chr;
300 bool m_invert;
301 };
302
303 using cu = basic_cu<char>;
304 using wcu = basic_cu<wchar_t>;
305#ifdef _UNICODE
306 using tcu = wcu;
307#else
308 using tcu = cu;
309#endif
310
314 class sgml_cp : public sgml_tester
315 {
316 public:
317 sgml_cp(const char* chr, size_t count = (size_t)-1, bool invert = false, _In_ const std::locale& locale = std::locale()) :
318 sgml_tester(locale),
319 m_invert(invert)
320 {
321 assert(chr || !count);
322 wchar_t buf[3];
323 size_t chr_end;
324 m_chr.assign(count ? next_sgml_cp(chr, 0, count, chr_end, buf) : L"");
325 }
326
327 virtual bool match(
328 _In_reads_or_z_(end) const char* text,
329 _In_ size_t start = 0,
330 _In_ size_t end = (size_t)-1,
331 _In_ int flags = match_default)
332 {
333 assert(text || start >= end);
334 if (start < end && text[start]) {
335 wchar_t buf[3];
336 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
337 bool r = ((flags & match_case_insensitive) ?
338 stdex::strnicmp(chr, (size_t)-1, m_chr.c_str(), m_chr.size(), m_locale) :
339 stdex::strncmp(chr, (size_t)-1, m_chr.c_str(), m_chr.size())) == 0;
340 if (r && !m_invert || !r && m_invert) {
341 interval.start = start;
342 return true;
343 }
344 }
345 interval.start = (interval.end = start) + 1;
346 return false;
347 }
348
349 protected:
350 std::wstring m_chr;
351 bool m_invert;
352 };
353
357 template <class T>
358 class basic_space_cu : public basic_tester<T>
359 {
360 public:
361 basic_space_cu(bool invert = false, _In_ const std::locale& locale = std::locale()) :
362 basic_tester<T>(locale),
363 m_invert(invert)
364 {}
365
366 virtual bool match(
367 _In_reads_or_z_(end) const T* text,
368 _In_ size_t start = 0,
369 _In_ size_t end = (size_t)-1,
370 _In_ int flags = match_default)
371 {
372 assert(text || start >= end);
373 if (start < end && text[start]) {
374 bool r =
375 ((flags & match_multiline) || !islbreak(text[start])) &&
376 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::space, text[start]);
377 if (r && !m_invert || !r && m_invert) {
378 interval.end = (interval.start = start) + 1;
379 return true;
380 }
381 }
382 interval.start = (interval.end = start) + 1;
383 return false;
384 }
385
386 protected:
387 bool m_invert;
388 };
389
392#ifdef _UNICODE
393 using tspace_cu = wspace_cu;
394#else
395 using tspace_cu = space_cu;
396#endif
397
401 class sgml_space_cp : public basic_space_cu<char>
402 {
403 public:
404 sgml_space_cp(_In_ bool invert = false, _In_ const std::locale& locale = std::locale()) :
405 basic_space_cu<char>(invert, locale)
406 {}
407
408 virtual bool match(
409 _In_reads_or_z_(end) const char* text,
410 _In_ size_t start = 0,
411 _In_ size_t end = (size_t)-1,
412 _In_ int flags = match_default)
413 {
414 assert(text || start >= end);
415 if (start < end && text[start]) {
416 wchar_t buf[3];
417 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
418 const wchar_t* chr_end = chr + stdex::strlen(chr);
419 bool r =
420 ((flags & match_multiline) || !islbreak(chr, (size_t)-1)) &&
421 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::space, chr, chr_end) == chr_end;
422 if (r && !m_invert || !r && m_invert) {
423 interval.start = start;
424 return true;
425 }
426 }
427
428 interval.start = (interval.end = start) + 1;
429 return false;
430 }
431 };
432
436 template <class T>
437 class basic_punct_cu : public basic_tester<T>
438 {
439 public:
440 basic_punct_cu(bool invert = false, _In_ const std::locale& locale = std::locale()) :
441 basic_tester<T>(locale),
442 m_invert(invert)
443 {}
444
445 virtual bool match(
446 _In_reads_or_z_(end) const T* text,
447 _In_ size_t start = 0,
448 _In_ size_t end = (size_t)-1,
449 _In_ int flags = match_default)
450 {
451 assert(text || start >= end);
452 if (start < end && text[start]) {
453 bool r = std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::punct, text[start]);
454 if (r && !m_invert || !r && m_invert) {
455 interval.end = (interval.start = start) + 1;
456 return true;
457 }
458 }
459 interval.start = (interval.end = start) + 1;
460 return false;
461 }
462
463 protected:
464 bool m_invert;
465 };
466
469#ifdef _UNICODE
470 using tpunct_cu = wpunct_cu;
471#else
472 using tpunct_cu = punct_cu;
473#endif
474
478 class sgml_punct_cp : public basic_punct_cu<char>
479 {
480 public:
481 virtual bool match(
482 _In_reads_or_z_(end) const char* text,
483 _In_ size_t start = 0,
484 _In_ size_t end = (size_t)-1,
485 _In_ int flags = match_default)
486 {
487 assert(text || start >= end);
488 if (start < end && text[start]) {
489 wchar_t buf[3];
490 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
491 const wchar_t* chr_end = chr + stdex::strlen(chr);
492 bool r = std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::punct, chr, chr_end) == chr_end;
493 if (r && !m_invert || !r && m_invert) {
494 interval.start = start;
495 return true;
496 }
497 }
498 interval.start = (interval.end = start) + 1;
499 return false;
500 }
501 };
502
506 template <class T>
508 {
509 public:
510 basic_space_or_punct_cu(bool invert = false, _In_ const std::locale& locale = std::locale()) :
511 basic_tester<T>(locale),
512 m_invert(invert)
513 {}
514
515 virtual bool match(
516 _In_reads_or_z_(end) const T* text,
517 _In_ size_t start = 0,
518 _In_ size_t end = (size_t)-1,
519 _In_ int flags = match_default)
520 {
521 assert(text || start >= end);
522 if (start < end && text[start]) {
523 bool r =
524 ((flags & match_multiline) || !islbreak(text[start])) &&
525 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::space | std::ctype_base::punct, text[start]);
526 if (r && !m_invert || !r && m_invert) {
527 interval.end = (interval.start = start) + 1;
528 return true;
529 }
530 }
531 interval.start = (interval.end = start) + 1;
532 return false;
533 }
534
535 protected:
536 bool m_invert;
537 };
538
541#ifdef _UNICODE
543#else
545#endif
546
551 {
552 public:
553 virtual bool match(
554 _In_reads_or_z_(end) const char* text,
555 _In_ size_t start = 0,
556 _In_ size_t end = (size_t)-1,
557 _In_ int flags = match_default)
558 {
559 assert(text || start >= end);
560 if (start < end && text[start]) {
561 wchar_t buf[3];
562 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
563 const wchar_t* chr_end = chr + stdex::strlen(chr);
564 bool r =
565 ((flags & match_multiline) || !islbreak(chr, (size_t)-1)) &&
566 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::space | std::ctype_base::punct, chr, chr_end) == chr_end;
567 if (r && !m_invert || !r && m_invert) {
568 interval.start = start;
569 return true;
570 }
571 }
572 interval.start = (interval.end = start) + 1;
573 return false;
574 }
575 };
576
580 template <class T>
581 class basic_bol : public basic_tester<T>
582 {
583 public:
584 basic_bol(bool invert = false) : m_invert(invert) {}
585
586 virtual bool match(
587 _In_reads_or_z_(end) const T* text,
588 _In_ size_t start = 0,
589 _In_ size_t end = (size_t)-1,
590 _In_ int flags = match_default)
591 {
592 assert(text || start >= end);
593 bool r = start == 0 || start <= end && islbreak(text[start - 1]);
594 if (r && !m_invert || !r && m_invert) {
595 interval.end = interval.start = start;
596 return true;
597 }
598 interval.start = (interval.end = start) + 1;
599 return false;
600 }
601
602 protected:
603 bool m_invert;
604 };
605
606 using bol = basic_bol<char>;
607 using wbol = basic_bol<wchar_t>;
608#ifdef _UNICODE
609 using tbol = wbol;
610#else
611 using tbol = bol;
612#endif
614
618 template <class T>
619 class basic_eol : public basic_tester<T>
620 {
621 public:
622 basic_eol(bool invert = false) : m_invert(invert) {}
623
624 virtual bool match(
625 _In_reads_or_z_(end) const T* text,
626 _In_ size_t start = 0,
627 _In_ size_t end = (size_t)-1,
628 _In_ int flags = match_default)
629 {
630 assert(text || start >= end);
631 bool r = islbreak(text[start]);
632 if (r && !m_invert || !r && m_invert) {
633 interval.end = interval.start = start;
634 return true;
635 }
636 interval.start = (interval.end = start) + 1;
637 return false;
638 }
639
640 protected:
641 bool m_invert;
642 };
643
644 using eol = basic_eol<char>;
645 using weol = basic_eol<wchar_t>;
646#ifdef _UNICODE
647 using teol = weol;
648#else
649 using teol = eol;
650#endif
652
653 template <class T>
654 class basic_set : public basic_tester<T>
655 {
656 public:
657 basic_set(bool invert = false, _In_ const std::locale& locale = std::locale()) :
658 basic_tester<T>(locale),
659 hit_offset((size_t)-1),
660 m_invert(invert)
661 {}
662
663 virtual bool match(
664 _In_reads_or_z_(end) const T* text,
665 _In_ size_t start = 0,
666 _In_ size_t end = (size_t)-1,
667 _In_ int flags = match_default) = 0;
668
669 virtual void invalidate()
670 {
671 hit_offset = (size_t)-1;
673 }
674
675 public:
676 size_t hit_offset;
677
678 protected:
679 bool m_invert;
680 };
681
685 template <class T>
686 class basic_cu_set : public basic_set<T>
687 {
688 public:
690 _In_reads_or_z_(count) const T* set,
691 _In_ size_t count = (size_t)-1,
692 _In_ bool invert = false,
693 _In_ const std::locale& locale = std::locale()) :
694 basic_set<T>(invert, locale)
695 {
696 if (set)
697 m_set.assign(set, set + stdex::strnlen(set, count));
698 }
699
700 virtual bool match(
701 _In_reads_or_z_(end) const T* text,
702 _In_ size_t start = 0,
703 _In_ size_t end = (size_t)-1,
704 _In_ int flags = match_default)
705 {
706 assert(text || start >= end);
707 if (start < end && text[start]) {
708 const T* set = m_set.c_str();
709 const T* r = (flags & match_case_insensitive) ?
710 stdex::strnichr(set, text[start], m_set.size(), m_locale) :
711 stdex::strnchr(set, text[start], m_set.size());
712 if (r && !m_invert || !r && m_invert) {
713 hit_offset = r ? r - set : (size_t)-1;
714 interval.end = (interval.start = start) + 1;
715 return true;
716 }
717 }
718 hit_offset = (size_t)-1;
719 interval.start = (interval.end = start) + 1;
720 return false;
721 }
722
723 protected:
724 std::basic_string<T> m_set;
725 };
726
729#ifdef _UNICODE
730 using tcu_set = wcu_set;
731#else
732 using tcu_set = cu_set;
733#endif
734
738 class sgml_cp_set : public basic_set<char>
739 {
740 public:
741 sgml_cp_set(const char* set, size_t count = (size_t)-1, bool invert = false, _In_ const std::locale& locale = std::locale()) :
742 basic_set<char>(invert, locale)
743 {
744 if (set)
745 m_set = sgml2str(set, count);
746 }
747
748 virtual bool match(
749 _In_reads_or_z_(end) const char* text,
750 _In_ size_t start = 0,
751 _In_ size_t end = (size_t)-1,
752 _In_ int flags = match_default)
753 {
754 assert(text || start >= end);
755 if (start < end && text[start]) {
756 wchar_t buf[3];
757 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
758 const wchar_t* set = m_set.c_str();
759 const wchar_t* r = (flags & match_case_insensitive) ?
760 stdex::strnistr(set, chr, m_set.size(), m_locale) :
761 stdex::strnstr(set, chr, m_set.size());
762 if (r && !m_invert || !r && m_invert) {
763 hit_offset = r ? r - set : (size_t)-1;
764 interval.start = start;
765 return true;
766 }
767 }
768 hit_offset = (size_t)-1;
769 interval.start = (interval.end = start) + 1;
770 return false;
771 }
772
773 protected:
774 std::wstring m_set;
775 };
776
780 template <class T>
781 class basic_string : public basic_tester<T>
782 {
783 public:
785 _In_reads_or_z_(count) const T* str,
786 _In_ size_t count = (size_t)-1,
787 _In_ const std::locale& locale = std::locale()) :
788 basic_tester<T>(locale),
789 m_str(str, str + stdex::strnlen(str, count))
790 {}
791
792 virtual bool match(
793 _In_reads_or_z_(end) const T* text,
794 _In_ size_t start = 0,
795 _In_ size_t end = (size_t)-1,
796 _In_ int flags = match_default)
797 {
798 assert(text || start >= end);
799 size_t
800 m = m_str.size(),
801 n = std::min<size_t>(end - start, m);
802 bool r = ((flags & match_case_insensitive) ?
803 stdex::strnicmp(text + start, n, m_str.c_str(), m, m_locale) :
804 stdex::strncmp(text + start, n, m_str.c_str(), m)) == 0;
805 if (r) {
806 interval.end = (interval.start = start) + n;
807 return true;
808 }
809 interval.start = (interval.end = start) + 1;
810 return false;
811 }
812
813 protected:
814 std::basic_string<T> m_str;
815 };
816
819#ifdef _UNICODE
820 using tstring = wstring;
821#else
822 using tstring = string;
823#endif
824
829 {
830 public:
831 sgml_string(const char* str, size_t count = (size_t)-1, _In_ const std::locale& locale = std::locale()) :
832 sgml_tester(locale),
833 m_str(sgml2str(str, count))
834 {}
835
836 virtual bool match(
837 _In_reads_or_z_(end) const char* text,
838 _In_ size_t start = 0,
839 _In_ size_t end = (size_t)-1,
840 _In_ int flags = match_default)
841 {
842 assert(text || start >= end);
843 const wchar_t* str = m_str.c_str();
844 const bool case_insensitive = flags & match_case_insensitive ? true : false;
845 const auto& ctype = std::use_facet<std::ctype<wchar_t>>(m_locale);
846 for (interval.end = start;;) {
847 if (!*str) {
848 interval.start = start;
849 return true;
850 }
851 if (interval.end >= end || !text[interval.end]) {
852 interval.start = (interval.end = start) + 1;
853 return false;
854 }
855 wchar_t buf[3];
856 const wchar_t* chr = next_sgml_cp(text, interval.end, end, interval.end, buf);
857 for (; *chr; ++str, ++chr) {
858 if (!*str ||
859 (case_insensitive ? ctype.tolower(*str) != ctype.tolower(*chr) : *str != *chr))
860 {
861 interval.start = (interval.end = start) + 1;
862 return false;
863 }
864 }
865 }
866 }
867
868 protected:
869 std::wstring m_str;
870 };
871
875 template <class T>
877 {
878 public:
879 basic_iterations(const std::shared_ptr<basic_tester<T>>& el, size_t min_iterations = 0, size_t max_iterations = (size_t)-1, bool greedy = true) :
880 m_el(el),
881 m_min_iterations(min_iterations),
882 m_max_iterations(max_iterations),
883 m_greedy(greedy)
884 {}
885
886 virtual bool match(
887 _In_reads_or_z_(end) const T* text,
888 _In_ size_t start = 0,
889 _In_ size_t end = (size_t)-1,
890 _In_ int flags = match_default)
891 {
892 assert(text || start >= end);
893 interval.start = interval.end = start;
894 for (size_t i = 0; ; i++) {
895 if (!m_greedy && i >= m_min_iterations || i >= m_max_iterations)
896 return true;
897 if (!m_el->match(text, interval.end, end, flags)) {
898 if (i >= m_min_iterations)
899 return true;
900 break;
901 }
902 if (m_el->interval.end == interval.end) {
903 // Element did match, but the matching interval was empty. Quit instead of spinning.
904 return true;
905 }
906 interval.end = m_el->interval.end;
907 }
908 interval.start = (interval.end = start) + 1;
909 return false;
910 }
911
912 protected:
913 std::shared_ptr<basic_tester<T>> m_el;
916 bool m_greedy;
917 };
918
921#ifdef _UNICODE
922 using titerations = witerations;
923#else
924 using titerations = iterations;
925#endif
927
931 template <class T>
933 {
934 protected:
935 tester_collection(_In_ const std::locale& locale) : basic_tester<T>(locale) {}
936
937 public:
939 _In_count_(count) const std::shared_ptr<basic_tester<T>>* el,
940 _In_ size_t count,
941 _In_ const std::locale& locale = std::locale()) :
942 basic_tester<T>(locale)
943 {
944 assert(el || !count);
945 m_collection.reserve(count);
946 for (size_t i = 0; i < count; i++)
947 m_collection.push_back(el[i]);
948 }
949
951 _Inout_ std::vector<std::shared_ptr<basic_tester<T>>>&& collection,
952 _In_ const std::locale& locale = std::locale()) :
953 basic_tester<T>(locale),
954 m_collection(std::move(collection))
955 {}
956
957 virtual void invalidate()
958 {
959 for (auto& el: m_collection)
960 el->invalidate();
962 }
963
964 protected:
965 std::vector<std::shared_ptr<basic_tester<T>>> m_collection;
966 };
967
971 template <class T>
973 {
974 public:
976 _In_count_(count) const std::shared_ptr<basic_tester<T>>* el = nullptr,
977 _In_ size_t count = 0,
978 _In_ const std::locale& locale = std::locale()) :
979 tester_collection<T>(el, count, locale)
980 {}
981
983 _Inout_ std::vector<std::shared_ptr<basic_tester<T>>>&& collection,
984 _In_ const std::locale& locale = std::locale()) :
985 tester_collection<T>(std::move(collection), locale)
986 {}
987
988 virtual bool match(
989 _In_reads_or_z_(end) const T* text,
990 _In_ size_t start = 0,
991 _In_ size_t end = (size_t)-1,
992 _In_ int flags = match_default)
993 {
994 assert(text || start >= end);
995 interval.end = start;
996 for (auto i = m_collection.begin(); i != m_collection.end(); ++i) {
997 if (!(*i)->match(text, interval.end, end, flags)) {
998 for (++i; i != m_collection.end(); ++i)
999 (*i)->invalidate();
1000 interval.start = (interval.end = start) + 1;
1001 return false;
1002 }
1003 interval.end = (*i)->interval.end;
1004 }
1005 interval.start = start;
1006 return true;
1007 }
1008 };
1009
1012#ifdef _UNICODE
1013 using tsequence = wsequence;
1014#else
1015 using tsequence = sequence;
1016#endif
1018
1022 template <class T>
1024 {
1025 protected:
1026 basic_branch(_In_ const std::locale& locale) :
1027 tester_collection<T>(locale),
1028 hit_offset((size_t)-1)
1029 {}
1030
1031 public:
1033 _In_count_(count) const std::shared_ptr<basic_tester<T>>* el = nullptr,
1034 _In_ size_t count = 0,
1035 _In_ const std::locale& locale = std::locale()) :
1036 tester_collection<T>(el, count, locale),
1037 hit_offset((size_t)-1)
1038 {}
1039
1041 _Inout_ std::vector<std::shared_ptr<basic_tester<T>>>&& collection,
1042 _In_ const std::locale& locale = std::locale()) :
1043 tester_collection<T>(std::move(collection), locale),
1044 hit_offset((size_t)-1)
1045 {}
1046
1047 virtual bool match(
1048 _In_reads_or_z_(end) const T* text,
1049 _In_ size_t start = 0,
1050 _In_ size_t end = (size_t)-1,
1051 _In_ int flags = match_default)
1052 {
1053 assert(text || start >= end);
1054 hit_offset = 0;
1055 for (auto i = m_collection.begin(); i != m_collection.end(); ++i, ++hit_offset) {
1056 if ((*i)->match(text, start, end, flags)) {
1057 interval = (*i)->interval;
1058 for (++i; i != m_collection.end(); ++i)
1059 (*i)->invalidate();
1060 return true;
1061 }
1062 }
1063 hit_offset = (size_t)-1;
1064 interval.start = (interval.end = start) + 1;
1065 return false;
1066 }
1067
1068 virtual void invalidate()
1069 {
1070 hit_offset = (size_t)-1;
1072 }
1073
1074 public:
1075 size_t hit_offset;
1076 };
1077
1078 using branch = basic_branch<char>;
1080#ifdef _UNICODE
1081 using tbranch = wbranch;
1082#else
1083 using tbranch = branch;
1084#endif
1086
1090 template <class T, class T_tester = basic_string<T>>
1092 {
1093 public:
1094 inline basic_string_branch(
1095 _In_reads_(count) const T* str_z = nullptr,
1096 _In_ size_t count = 0,
1097 _In_ const std::locale& locale = std::locale()) :
1098 basic_branch<T>(locale)
1099 {
1100 build(str_z, count);
1101 }
1102
1103 inline basic_string_branch(_In_z_ const T* str, ...) :
1104 basic_branch<T>(std::locale())
1105 {
1106 va_list params;
1107 va_start(params, str);
1108 build(str, params);
1109 va_end(params);
1110 }
1111
1112 inline basic_string_branch(_In_ const std::locale& locale, _In_z_ const T* str, ...) :
1113 basic_branch<T>(locale)
1114 {
1115 va_list params;
1116 va_start(params, str);
1117 build(str, params);
1118 va_end(params);
1119 }
1120
1121 protected:
1122 void build(_In_reads_(count) const T* str_z, _In_ size_t count)
1123 {
1124 assert(str_z || !count);
1125 if (count) {
1126 size_t offset, n;
1127 for (
1128 offset = n = 0;
1129 offset < count && str_z[offset];
1130 offset += stdex::strnlen(str_z + offset, count - offset) + 1, ++n);
1131 m_collection.reserve(n);
1132 for (
1133 offset = 0;
1134 offset < count && str_z[offset];
1135 offset += stdex::strnlen(str_z + offset, count - offset) + 1)
1136 m_collection.push_back(std::move(std::make_shared<T_tester>(str_z + offset, count - offset, m_locale)));
1137 }
1138 }
1139
1140 void build(_In_z_ const T* str, _In_ va_list params)
1141 {
1142 const T* p;
1143 for (
1144 m_collection.push_back(std::move(std::make_shared<T_tester>(str, (size_t)-1, m_locale)));
1145 (p = va_arg(params, const T*)) != nullptr;
1146 m_collection.push_back(std::move(std::make_shared<T_tester>(p, (size_t)-1, m_locale))));
1147 }
1148 };
1149
1152#ifdef _UNICODE
1154#else
1156#endif
1158
1162 template <class T>
1164 {
1165 public:
1167 _In_count_(count) const std::shared_ptr<basic_tester<T>>* el = nullptr,
1168 _In_ size_t count = 0,
1169 _In_ const std::locale& locale = std::locale()) :
1170 tester_collection<T>(el, count, locale)
1171 {}
1172
1174 _Inout_ std::vector<std::shared_ptr<basic_tester<T>>>&& collection,
1175 _In_ const std::locale& locale = std::locale()) :
1176 tester_collection<T>(std::move(collection), locale)
1177 {}
1178
1179 virtual bool match(
1180 _In_reads_or_z_(end) const T* text,
1181 _In_ size_t start = 0,
1182 _In_ size_t end = (size_t)-1,
1183 _In_ int flags = match_default)
1184 {
1185 assert(text || start >= end);
1186 for (auto& el: m_collection)
1187 el->invalidate();
1188 if (match_recursively(text, start, end, flags)) {
1189 interval.start = start;
1190 return true;
1191 }
1192 interval.start = (interval.end = start) + 1;
1193 return false;
1194 }
1195
1196 protected:
1197 bool match_recursively(
1198 _In_reads_or_z_(end) const T* text,
1199 _In_ size_t start = 0,
1200 _In_ size_t end = (size_t)-1,
1201 _In_ int flags = match_default)
1202 {
1203 bool all_matched = true;
1204 for (auto& el: m_collection) {
1205 if (!el->interval) {
1206 // Element was not matched in permutatuion yet.
1207 all_matched = false;
1208 if (el->match(text, start, end, flags)) {
1209 // Element matched for the first time.
1210 if (match_recursively(text, el->interval.end, end, flags)) {
1211 // Rest of the elements matched too.
1212 return true;
1213 }
1214 el->invalidate();
1215 }
1216 }
1217 }
1218 if (all_matched) {
1219 interval.end = start;
1220 return true;
1221 }
1222 return false;
1223 }
1224 };
1225
1228#ifdef _UNICODE
1229 using tpermutation = wpermutation;
1230#else
1231 using tpermutation = permutation;
1232#endif
1234
1238 template <class T>
1239 class basic_integer : public basic_tester<T>
1240 {
1241 public:
1242 basic_integer(_In_ const std::locale& locale = std::locale()) :
1243 basic_tester<T>(locale),
1244 value(0)
1245 {}
1246
1247 virtual void invalidate()
1248 {
1249 value = 0;
1251 }
1252
1253 public:
1254 size_t value;
1255 };
1256
1260 template <class T>
1262 {
1263 public:
1265 _In_ const std::shared_ptr<basic_tester<T>>& digit_0,
1266 _In_ const std::shared_ptr<basic_tester<T>>& digit_1,
1267 _In_ const std::shared_ptr<basic_tester<T>>& digit_2,
1268 _In_ const std::shared_ptr<basic_tester<T>>& digit_3,
1269 _In_ const std::shared_ptr<basic_tester<T>>& digit_4,
1270 _In_ const std::shared_ptr<basic_tester<T>>& digit_5,
1271 _In_ const std::shared_ptr<basic_tester<T>>& digit_6,
1272 _In_ const std::shared_ptr<basic_tester<T>>& digit_7,
1273 _In_ const std::shared_ptr<basic_tester<T>>& digit_8,
1274 _In_ const std::shared_ptr<basic_tester<T>>& digit_9,
1275 _In_ const std::locale& locale = std::locale()) :
1276 basic_integer<T>(locale),
1277 m_digit_0(digit_0),
1278 m_digit_1(digit_1),
1279 m_digit_2(digit_2),
1280 m_digit_3(digit_3),
1281 m_digit_4(digit_4),
1282 m_digit_5(digit_5),
1283 m_digit_6(digit_6),
1284 m_digit_7(digit_7),
1285 m_digit_8(digit_8),
1286 m_digit_9(digit_9)
1287 {}
1288
1289 virtual bool match(
1290 _In_reads_or_z_(end) const T* text,
1291 _In_ size_t start = 0,
1292 _In_ size_t end = (size_t)-1,
1293 _In_ int flags = match_default)
1294 {
1295 assert(text || start >= end);
1296 for (interval.end = start, value = 0; interval.end < end && text[interval.end];) {
1297 size_t dig;
1298 if (m_digit_0->match(text, interval.end, end, flags)) { dig = 0; interval.end = m_digit_0->interval.end; }
1299 else if (m_digit_1->match(text, interval.end, end, flags)) { dig = 1; interval.end = m_digit_1->interval.end; }
1300 else if (m_digit_2->match(text, interval.end, end, flags)) { dig = 2; interval.end = m_digit_2->interval.end; }
1301 else if (m_digit_3->match(text, interval.end, end, flags)) { dig = 3; interval.end = m_digit_3->interval.end; }
1302 else if (m_digit_4->match(text, interval.end, end, flags)) { dig = 4; interval.end = m_digit_4->interval.end; }
1303 else if (m_digit_5->match(text, interval.end, end, flags)) { dig = 5; interval.end = m_digit_5->interval.end; }
1304 else if (m_digit_6->match(text, interval.end, end, flags)) { dig = 6; interval.end = m_digit_6->interval.end; }
1305 else if (m_digit_7->match(text, interval.end, end, flags)) { dig = 7; interval.end = m_digit_7->interval.end; }
1306 else if (m_digit_8->match(text, interval.end, end, flags)) { dig = 8; interval.end = m_digit_8->interval.end; }
1307 else if (m_digit_9->match(text, interval.end, end, flags)) { dig = 9; interval.end = m_digit_9->interval.end; }
1308 else break;
1309 value = value * 10 + dig;
1310 }
1311 if (start < interval.end) {
1312 interval.start = start;
1313 return true;
1314 }
1315 interval.start = (interval.end = start) + 1;
1316 return false;
1317 }
1318
1319 protected:
1320 std::shared_ptr<basic_tester<T>>
1321 m_digit_0,
1322 m_digit_1,
1323 m_digit_2,
1324 m_digit_3,
1325 m_digit_4,
1326 m_digit_5,
1327 m_digit_6,
1328 m_digit_7,
1329 m_digit_8,
1330 m_digit_9;
1331 };
1332
1335#ifdef _UNICODE
1336 using tinteger10 = winteger10;
1337#else
1338 using tinteger10 = integer10;
1339#endif
1341
1345 template <class T>
1347 {
1348 public:
1350 _In_ const std::shared_ptr<basic_integer10<T>>& digits,
1351 _In_ const std::shared_ptr<basic_set<T>>& separator,
1352 _In_ const std::locale& locale = std::locale()) :
1353 basic_integer<T>(locale),
1354 digit_count(0),
1355 has_separators(false),
1356 m_digits(digits),
1357 m_separator(separator)
1358 {}
1359
1360 virtual bool match(
1361 _In_reads_or_z_(end) const T* text,
1362 _In_ size_t start = 0,
1363 _In_ size_t end = (size_t)-1,
1364 _In_ int flags = match_default)
1365 {
1366 assert(text || start >= end);
1367 if (m_digits->match(text, start, end, flags)) {
1368 // Leading part match.
1369 value = m_digits->value;
1370 digit_count = m_digits->interval.size();
1371 has_separators = false;
1372 interval.start = start;
1373 interval.end = m_digits->interval.end;
1374 if (m_digits->interval.size() <= 3) {
1375 // Maybe separated with thousand separators?
1376 size_t hit_offset = (size_t)-1;
1377 while (m_separator->match(text, interval.end, end, flags) &&
1378 (hit_offset == (size_t)-1 || hit_offset == m_separator->hit_offset) && // All separators must be the same, no mixing.
1379 m_digits->match(text, m_separator->interval.end, end, flags) &&
1380 m_digits->interval.size() == 3)
1381 {
1382 // Thousand separator and three-digit integer followed.
1383 value = value * 1000 + m_digits->value;
1384 digit_count += 3;
1385 has_separators = true;
1386 interval.end = m_digits->interval.end;
1387 hit_offset = m_separator->hit_offset;
1388 }
1389 }
1390
1391 return true;
1392 }
1393 value = 0;
1394 interval.start = (interval.end = start) + 1;
1395 return false;
1396 }
1397
1398 virtual void invalidate()
1399 {
1400 digit_count = 0;
1401 has_separators = false;
1403 }
1404
1405 public:
1408
1409 protected:
1410 std::shared_ptr<basic_integer10<T>> m_digits;
1411 std::shared_ptr<basic_set<T>> m_separator;
1412 };
1413
1416#ifdef _UNICODE
1417 using tinteger10ts = winteger10ts;
1418#else
1419 using tinteger10ts = integer10ts;
1420#endif
1422
1426 template <class T>
1428 {
1429 public:
1431 _In_ const std::shared_ptr<basic_tester<T>>& digit_0,
1432 _In_ const std::shared_ptr<basic_tester<T>>& digit_1,
1433 _In_ const std::shared_ptr<basic_tester<T>>& digit_2,
1434 _In_ const std::shared_ptr<basic_tester<T>>& digit_3,
1435 _In_ const std::shared_ptr<basic_tester<T>>& digit_4,
1436 _In_ const std::shared_ptr<basic_tester<T>>& digit_5,
1437 _In_ const std::shared_ptr<basic_tester<T>>& digit_6,
1438 _In_ const std::shared_ptr<basic_tester<T>>& digit_7,
1439 _In_ const std::shared_ptr<basic_tester<T>>& digit_8,
1440 _In_ const std::shared_ptr<basic_tester<T>>& digit_9,
1441 _In_ const std::shared_ptr<basic_tester<T>>& digit_10,
1442 _In_ const std::shared_ptr<basic_tester<T>>& digit_11,
1443 _In_ const std::shared_ptr<basic_tester<T>>& digit_12,
1444 _In_ const std::shared_ptr<basic_tester<T>>& digit_13,
1445 _In_ const std::shared_ptr<basic_tester<T>>& digit_14,
1446 _In_ const std::shared_ptr<basic_tester<T>>& digit_15,
1447 _In_ const std::locale& locale = std::locale()) :
1448 basic_integer<T>(locale),
1449 m_digit_0(digit_0),
1450 m_digit_1(digit_1),
1451 m_digit_2(digit_2),
1452 m_digit_3(digit_3),
1453 m_digit_4(digit_4),
1454 m_digit_5(digit_5),
1455 m_digit_6(digit_6),
1456 m_digit_7(digit_7),
1457 m_digit_8(digit_8),
1458 m_digit_9(digit_9),
1459 m_digit_10(digit_10),
1460 m_digit_11(digit_11),
1461 m_digit_12(digit_12),
1462 m_digit_13(digit_13),
1463 m_digit_14(digit_14),
1464 m_digit_15(digit_15)
1465 {}
1466
1467 virtual bool match(
1468 _In_reads_or_z_(end) const T* text,
1469 _In_ size_t start = 0,
1470 _In_ size_t end = (size_t)-1,
1471 _In_ int flags = match_default)
1472 {
1473 assert(text || start >= end);
1474 for (interval.end = start, value = 0; interval.end < end && text[interval.end];) {
1475 size_t dig;
1476 if (m_digit_0->match(text, interval.end, end, flags)) { dig = 0; interval.end = m_digit_0->interval.end; }
1477 else if (m_digit_1->match(text, interval.end, end, flags)) { dig = 1; interval.end = m_digit_1->interval.end; }
1478 else if (m_digit_2->match(text, interval.end, end, flags)) { dig = 2; interval.end = m_digit_2->interval.end; }
1479 else if (m_digit_3->match(text, interval.end, end, flags)) { dig = 3; interval.end = m_digit_3->interval.end; }
1480 else if (m_digit_4->match(text, interval.end, end, flags)) { dig = 4; interval.end = m_digit_4->interval.end; }
1481 else if (m_digit_5->match(text, interval.end, end, flags)) { dig = 5; interval.end = m_digit_5->interval.end; }
1482 else if (m_digit_6->match(text, interval.end, end, flags)) { dig = 6; interval.end = m_digit_6->interval.end; }
1483 else if (m_digit_7->match(text, interval.end, end, flags)) { dig = 7; interval.end = m_digit_7->interval.end; }
1484 else if (m_digit_8->match(text, interval.end, end, flags)) { dig = 8; interval.end = m_digit_8->interval.end; }
1485 else if (m_digit_9->match(text, interval.end, end, flags)) { dig = 9; interval.end = m_digit_9->interval.end; }
1486 else if (m_digit_10->match(text, interval.end, end, flags)) { dig = 10; interval.end = m_digit_10->interval.end; }
1487 else if (m_digit_11->match(text, interval.end, end, flags)) { dig = 11; interval.end = m_digit_11->interval.end; }
1488 else if (m_digit_12->match(text, interval.end, end, flags)) { dig = 12; interval.end = m_digit_12->interval.end; }
1489 else if (m_digit_13->match(text, interval.end, end, flags)) { dig = 13; interval.end = m_digit_13->interval.end; }
1490 else if (m_digit_14->match(text, interval.end, end, flags)) { dig = 14; interval.end = m_digit_14->interval.end; }
1491 else if (m_digit_15->match(text, interval.end, end, flags)) { dig = 15; interval.end = m_digit_15->interval.end; }
1492 else break;
1493 value = value * 16 + dig;
1494 }
1495 if (start < interval.end) {
1496 interval.start = start;
1497 return true;
1498 }
1499 interval.start = (interval.end = start) + 1;
1500 return false;
1501 }
1502
1503 protected:
1504 std::shared_ptr<basic_tester<T>>
1505 m_digit_0,
1506 m_digit_1,
1507 m_digit_2,
1508 m_digit_3,
1509 m_digit_4,
1510 m_digit_5,
1511 m_digit_6,
1512 m_digit_7,
1513 m_digit_8,
1514 m_digit_9,
1515 m_digit_10,
1516 m_digit_11,
1517 m_digit_12,
1518 m_digit_13,
1519 m_digit_14,
1520 m_digit_15;
1521 };
1522
1525#ifdef _UNICODE
1526 using tinteger16 = winteger16;
1527#else
1528 using tinteger16 = integer16;
1529#endif
1531
1535 template <class T>
1537 {
1538 public:
1540 _In_ const std::shared_ptr<basic_tester<T>>& digit_1,
1541 _In_ const std::shared_ptr<basic_tester<T>>& digit_5,
1542 _In_ const std::shared_ptr<basic_tester<T>>& digit_10,
1543 _In_ const std::shared_ptr<basic_tester<T>>& digit_50,
1544 _In_ const std::shared_ptr<basic_tester<T>>& digit_100,
1545 _In_ const std::shared_ptr<basic_tester<T>>& digit_500,
1546 _In_ const std::shared_ptr<basic_tester<T>>& digit_1000,
1547 _In_ const std::shared_ptr<basic_tester<T>>& digit_5000,
1548 _In_ const std::shared_ptr<basic_tester<T>>& digit_10000,
1549 _In_ const std::locale& locale = std::locale()) :
1550 basic_integer<T>(locale),
1551 m_digit_1(digit_1),
1552 m_digit_5(digit_5),
1553 m_digit_10(digit_10),
1554 m_digit_50(digit_50),
1555 m_digit_100(digit_100),
1556 m_digit_500(digit_500),
1557 m_digit_1000(digit_1000),
1558 m_digit_5000(digit_5000),
1559 m_digit_10000(digit_10000)
1560 {}
1561
1562 virtual bool match(
1563 _In_reads_or_z_(end) const T* text,
1564 _In_ size_t start = 0,
1565 _In_ size_t end = (size_t)-1,
1566 _In_ int flags = match_default)
1567 {
1568 assert(text || start >= end);
1569 size_t
1570 dig[5] = { (size_t)-1, (size_t)-1, (size_t)-1, (size_t)-1, (size_t)-1 },
1571 end2;
1572
1573 for (interval.end = start, value = 0; interval.end < end && text[interval.end]; dig[3] = dig[2], dig[2] = dig[1], dig[1] = dig[0], interval.end = end2) {
1574 if (m_digit_1 && m_digit_1->match(text, interval.end, end, flags)) { dig[0] = 1; end2 = m_digit_1->interval.end; }
1575 else if (m_digit_5 && m_digit_5->match(text, interval.end, end, flags)) { dig[0] = 5; end2 = m_digit_5->interval.end; }
1576 else if (m_digit_10 && m_digit_10->match(text, interval.end, end, flags)) { dig[0] = 10; end2 = m_digit_10->interval.end; }
1577 else if (m_digit_50 && m_digit_50->match(text, interval.end, end, flags)) { dig[0] = 50; end2 = m_digit_50->interval.end; }
1578 else if (m_digit_100 && m_digit_100->match(text, interval.end, end, flags)) { dig[0] = 100; end2 = m_digit_100->interval.end; }
1579 else if (m_digit_500 && m_digit_500->match(text, interval.end, end, flags)) { dig[0] = 500; end2 = m_digit_500->interval.end; }
1580 else if (m_digit_1000 && m_digit_1000->match(text, interval.end, end, flags)) { dig[0] = 1000; end2 = m_digit_1000->interval.end; }
1581 else if (m_digit_5000 && m_digit_5000->match(text, interval.end, end, flags)) { dig[0] = 5000; end2 = m_digit_5000->interval.end; }
1582 else if (m_digit_10000 && m_digit_10000->match(text, interval.end, end, flags)) { dig[0] = 10000; end2 = m_digit_10000->interval.end; }
1583 else break;
1584
1585 // Store first digit.
1586 if (dig[4] == (size_t)-1) dig[4] = dig[0];
1587
1588 if (dig[3] == dig[2] && dig[2] == dig[1] && dig[1] == dig[0] && dig[0] != dig[4]) {
1589 // Same digit repeated four times. No-go, unless first digit. E.g. XIIII vs. XIV. MMMMMCD allowed, IIII also...
1590 break;
1591 }
1592 if (dig[0] <= dig[1]) {
1593 // Digit is less or equal previous one: add.
1594 value += dig[0];
1595 }
1596 else if (
1597 dig[1] == 1 && (dig[0] == 5 || dig[0] == 10) ||
1598 dig[1] == 10 && (dig[0] == 50 || dig[0] == 100) ||
1599 dig[1] == 100 && (dig[0] == 500 || dig[0] == 1000) ||
1600 dig[1] == 1000 && (dig[0] == 5000 || dig[0] == 10000))
1601 {
1602 // Digit is up to two orders bigger than previous one: subtract. But...
1603 if (dig[2] < dig[0]) {
1604 // Digit is also bigger than pre-previous one. E.g. VIX (V < X => invalid)
1605 break;
1606 }
1607 value -= dig[1]; // Cancel addition in the previous step.
1608 dig[0] -= dig[1]; // Combine last two digits.
1609 dig[1] = dig[2]; // The true previous digit is now pre-previous one. :)
1610 dig[2] = dig[3]; // The true pre-previous digit is now pre-pre-previous one. :)
1611 value += dig[0]; // Add combined value.
1612 }
1613 else {
1614 // New digit is too big than the previous one. E.g. VX (V < X => invalid)
1615 break;
1616 }
1617 }
1618 if (value) {
1619 interval.start = start;
1620 return true;
1621 }
1622 interval.start = (interval.end = start) + 1;
1623 return false;
1624 }
1625
1626 protected:
1627 std::shared_ptr<basic_tester<T>>
1628 m_digit_1,
1629 m_digit_5,
1630 m_digit_10,
1631 m_digit_50,
1632 m_digit_100,
1633 m_digit_500,
1634 m_digit_1000,
1635 m_digit_5000,
1636 m_digit_10000;
1637 };
1638
1641#ifdef _UNICODE
1643#else
1645#endif
1647
1651 template <class T>
1653 {
1654 public:
1656 _In_ const std::shared_ptr<basic_tester<T>>& _numerator,
1657 _In_ const std::shared_ptr<basic_tester<T>>& _fraction_line,
1658 _In_ const std::shared_ptr<basic_tester<T>>& _denominator,
1659 _In_ const std::locale& locale = std::locale()) :
1660 basic_tester<T>(locale),
1661 numerator(_numerator),
1662 fraction_line(_fraction_line),
1663 denominator(_denominator)
1664 {}
1665
1666 virtual bool match(
1667 _In_reads_or_z_(end) const T* text,
1668 _In_ size_t start = 0,
1669 _In_ size_t end = (size_t)-1,
1670 _In_ int flags = match_default)
1671 {
1672 assert(text || start >= end);
1673 if (numerator->match(text, start, end, flags) &&
1674 fraction_line->match(text, numerator->interval.end, end, flags) &&
1675 denominator->match(text, fraction_line->interval.end, end, flags))
1676 {
1677 interval.start = start;
1678 interval.end = denominator->interval.end;
1679 return true;
1680 }
1681 numerator->invalidate();
1682 fraction_line->invalidate();
1683 denominator->invalidate();
1684 interval.start = (interval.end = start) + 1;
1685 return false;
1686 }
1687
1688 virtual void invalidate()
1689 {
1690 numerator->invalidate();
1691 fraction_line->invalidate();
1692 denominator->invalidate();
1694 }
1695
1696 public:
1697 std::shared_ptr<basic_tester<T>> numerator;
1698 std::shared_ptr<basic_tester<T>> fraction_line;
1699 std::shared_ptr<basic_tester<T>> denominator;
1700 };
1701
1704#ifdef _UNICODE
1705 using tfraction = wfraction;
1706#else
1707 using tfraction = fraction;
1708#endif
1710
1714 template <class T>
1715 class basic_score : public basic_tester<T>
1716 {
1717 public:
1719 _In_ const std::shared_ptr<basic_tester<T>>& _home,
1720 _In_ const std::shared_ptr<basic_tester<T>>& _separator,
1721 _In_ const std::shared_ptr<basic_tester<T>>& _guest,
1722 _In_ const std::shared_ptr<basic_tester<T>>& space,
1723 _In_ const std::locale& locale = std::locale()) :
1724 basic_tester<T>(locale),
1725 home(_home),
1726 separator(_separator),
1727 guest(_guest),
1728 m_space(space)
1729 {}
1730
1731 virtual bool match(
1732 _In_reads_or_z_(end) const T* text,
1733 _In_ size_t start = 0,
1734 _In_ size_t end = (size_t)-1,
1735 _In_ int flags = match_default)
1736 {
1737 assert(text || start >= end);
1738 interval.end = start;
1739
1740 if (home->match(text, interval.end, end, flags))
1741 interval.end = home->interval.end;
1742 else
1743 goto end;
1744
1745 const int space_match_flags = flags & ~match_multiline; // Spaces in score must never be broken in new line.
1746 for (; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
1747
1748 if (separator->match(text, interval.end, end, flags))
1749 interval.end = separator->interval.end;
1750 else
1751 goto end;
1752
1753 for (; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
1754
1755 if (guest->match(text, interval.end, end, flags))
1756 interval.end = guest->interval.end;
1757 else
1758 goto end;
1759
1760 interval.start = start;
1761 return true;
1762
1763 end:
1764 home->invalidate();
1765 separator->invalidate();
1766 guest->invalidate();
1767 interval.start = (interval.end = start) + 1;
1768 return false;
1769 }
1770
1771 virtual void invalidate()
1772 {
1773 home->invalidate();
1774 separator->invalidate();
1775 guest->invalidate();
1777 }
1778
1779 public:
1780 std::shared_ptr<basic_tester<T>> home;
1781 std::shared_ptr<basic_tester<T>> separator;
1782 std::shared_ptr<basic_tester<T>> guest;
1783
1784 protected:
1785 std::shared_ptr<basic_tester<T>> m_space;
1786 };
1787
1788 using score = basic_score<char>;
1790#ifdef _UNICODE
1791 using tscore = wscore;
1792#else
1793 using tscore = score;
1794#endif
1796
1800 template <class T>
1802 {
1803 public:
1805 _In_ const std::shared_ptr<basic_tester<T>>& _positive_sign,
1806 _In_ const std::shared_ptr<basic_tester<T>>& _negative_sign,
1807 _In_ const std::shared_ptr<basic_tester<T>>& _special_sign,
1808 _In_ const std::shared_ptr<basic_tester<T>>& _number,
1809 _In_ const std::locale& locale = std::locale()) :
1810 basic_tester<T>(locale),
1811 positive_sign(_positive_sign),
1812 negative_sign(_negative_sign),
1813 special_sign(_special_sign),
1814 number(_number)
1815 {}
1816
1817 virtual bool match(
1818 _In_reads_or_z_(end) const T* text,
1819 _In_ size_t start = 0,
1820 _In_ size_t end = (size_t)-1,
1821 _In_ int flags = match_default)
1822 {
1823 assert(text || start >= end);
1824 interval.end = start;
1825 if (positive_sign && positive_sign->match(text, interval.end, end, flags)) {
1826 interval.end = positive_sign->interval.end;
1827 if (negative_sign) negative_sign->invalidate();
1828 if (special_sign) special_sign->invalidate();
1829 }
1830 else if (negative_sign && negative_sign->match(text, interval.end, end, flags)) {
1831 interval.end = negative_sign->interval.end;
1832 if (positive_sign) positive_sign->invalidate();
1833 if (special_sign) special_sign->invalidate();
1834 }
1835 else if (special_sign && special_sign->match(text, interval.end, end, flags)) {
1836 interval.end = special_sign->interval.end;
1837 if (positive_sign) positive_sign->invalidate();
1838 if (negative_sign) negative_sign->invalidate();
1839 }
1840 else {
1841 if (positive_sign) positive_sign->invalidate();
1842 if (negative_sign) negative_sign->invalidate();
1843 if (special_sign) special_sign->invalidate();
1844 }
1845 if (number->match(text, interval.end, end, flags)) {
1846 interval.start = start;
1847 interval.end = number->interval.end;
1848 return true;
1849 }
1850 if (positive_sign) positive_sign->invalidate();
1851 if (negative_sign) negative_sign->invalidate();
1852 if (special_sign) special_sign->invalidate();
1853 number->invalidate();
1854 interval.start = (interval.end = start) + 1;
1855 return false;
1856 }
1857
1858 virtual void invalidate()
1859 {
1860 if (positive_sign) positive_sign->invalidate();
1861 if (negative_sign) negative_sign->invalidate();
1862 if (special_sign) special_sign->invalidate();
1863 number->invalidate();
1865 }
1866
1867 public:
1868 std::shared_ptr<basic_tester<T>> positive_sign;
1869 std::shared_ptr<basic_tester<T>> negative_sign;
1870 std::shared_ptr<basic_tester<T>> special_sign;
1871 std::shared_ptr<basic_tester<T>> number;
1872 };
1873
1876#ifdef _UNICODE
1878#else
1880#endif
1882
1886 template <class T>
1888 {
1889 public:
1891 _In_ const std::shared_ptr<basic_tester<T>>& _positive_sign,
1892 _In_ const std::shared_ptr<basic_tester<T>>& _negative_sign,
1893 _In_ const std::shared_ptr<basic_tester<T>>& _special_sign,
1894 _In_ const std::shared_ptr<basic_tester<T>>& _integer,
1895 _In_ const std::shared_ptr<basic_tester<T>>& space,
1896 _In_ const std::shared_ptr<basic_tester<T>>& _fraction,
1897 _In_ const std::locale& locale = std::locale()) :
1898 basic_tester<T>(locale),
1899 positive_sign(_positive_sign),
1900 negative_sign(_negative_sign),
1901 special_sign(_special_sign),
1902 integer(_integer),
1903 fraction(_fraction),
1904 m_space(space)
1905 {}
1906
1907 virtual bool match(
1908 _In_reads_or_z_(end) const T* text,
1909 _In_ size_t start = 0,
1910 _In_ size_t end = (size_t)-1,
1911 _In_ int flags = match_default)
1912 {
1913 assert(text || start >= end);
1914 interval.end = start;
1915
1916 if (positive_sign && positive_sign->match(text, interval.end, end, flags)) {
1917 interval.end = positive_sign->interval.end;
1918 if (negative_sign) negative_sign->invalidate();
1919 if (special_sign) special_sign->invalidate();
1920 }
1921 else if (negative_sign && negative_sign->match(text, interval.end, end, flags)) {
1922 interval.end = negative_sign->interval.end;
1923 if (positive_sign) positive_sign->invalidate();
1924 if (special_sign) special_sign->invalidate();
1925 }
1926 else if (special_sign && special_sign->match(text, interval.end, end, flags)) {
1927 interval.end = special_sign->interval.end;
1928 if (positive_sign) positive_sign->invalidate();
1929 if (negative_sign) negative_sign->invalidate();
1930 }
1931 else {
1932 if (positive_sign) positive_sign->invalidate();
1933 if (negative_sign) negative_sign->invalidate();
1934 if (special_sign) special_sign->invalidate();
1935 }
1936
1937 // Check for <integer> <fraction>
1938 const int space_match_flags = flags & ~match_multiline; // Spaces in fractions must never be broken in new line.
1939 if (integer->match(text, interval.end, end, flags) &&
1940 m_space->match(text, integer->interval.end, end, space_match_flags))
1941 {
1942 for (interval.end = m_space->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
1943 if (fraction->match(text, interval.end, end, flags)) {
1944 interval.start = start;
1946 return true;
1947 }
1948 fraction->invalidate();
1949 interval.start = start;
1950 interval.end = integer->interval.end;
1951 return true;
1952 }
1953
1954 // Check for <fraction>
1955 if (fraction->match(text, interval.end, end, flags)) {
1956 integer->invalidate();
1957 interval.start = start;
1959 return true;
1960 }
1961
1962 // Check for <integer>
1963 if (integer->match(text, interval.end, end, flags)) {
1964 fraction->invalidate();
1965 interval.start = start;
1966 interval.end = integer->interval.end;
1967 return true;
1968 }
1969
1970 if (positive_sign) positive_sign->invalidate();
1971 if (negative_sign) negative_sign->invalidate();
1972 if (special_sign) special_sign->invalidate();
1973 integer->invalidate();
1974 fraction->invalidate();
1975 interval.start = (interval.end = start) + 1;
1976 return false;
1977 }
1978
1979 virtual void invalidate()
1980 {
1981 if (positive_sign) positive_sign->invalidate();
1982 if (negative_sign) negative_sign->invalidate();
1983 if (special_sign) special_sign->invalidate();
1984 integer->invalidate();
1985 fraction->invalidate();
1987 }
1988
1989 public:
1990 std::shared_ptr<basic_tester<T>> positive_sign;
1991 std::shared_ptr<basic_tester<T>> negative_sign;
1992 std::shared_ptr<basic_tester<T>> special_sign;
1993 std::shared_ptr<basic_tester<T>> integer;
1994 std::shared_ptr<basic_tester<T>> fraction;
1995
1996 protected:
1997 std::shared_ptr<basic_tester<T>> m_space;
1998 };
1999
2002#ifdef _UNICODE
2004#else
2006#endif
2008
2012 template <class T>
2014 {
2015 public:
2017 _In_ const std::shared_ptr<basic_tester<T>>& _positive_sign,
2018 _In_ const std::shared_ptr<basic_tester<T>>& _negative_sign,
2019 _In_ const std::shared_ptr<basic_tester<T>>& _special_sign,
2020 _In_ const std::shared_ptr<basic_integer<T>>& _integer,
2021 _In_ const std::shared_ptr<basic_tester<T>>& _decimal_separator,
2022 _In_ const std::shared_ptr<basic_integer<T>>& _decimal,
2023 _In_ const std::shared_ptr<basic_tester<T>>& _exponent_symbol,
2024 _In_ const std::shared_ptr<basic_tester<T>>& _positive_exp_sign,
2025 _In_ const std::shared_ptr<basic_tester<T>>& _negative_exp_sign,
2026 _In_ const std::shared_ptr<basic_integer<T>>& _exponent,
2027 _In_ const std::locale& locale = std::locale()) :
2028 basic_tester<T>(locale),
2029 positive_sign(_positive_sign),
2030 negative_sign(_negative_sign),
2031 special_sign(_special_sign),
2032 integer(_integer),
2033 decimal_separator(_decimal_separator),
2034 decimal(_decimal),
2035 exponent_symbol(_exponent_symbol),
2036 positive_exp_sign(_positive_exp_sign),
2037 negative_exp_sign(_negative_exp_sign),
2038 exponent(_exponent),
2039 value(std::numeric_limits<double>::quiet_NaN())
2040 {}
2041
2042 virtual bool match(
2043 _In_reads_or_z_(end) const T* text,
2044 _In_ size_t start = 0,
2045 _In_ size_t end = (size_t)-1,
2046 _In_ int flags = match_default)
2047 {
2048 assert(text || start >= end);
2049 interval.end = start;
2050
2051 if (positive_sign && positive_sign->match(text, interval.end, end, flags)) {
2052 interval.end = positive_sign->interval.end;
2053 if (negative_sign) negative_sign->invalidate();
2054 if (special_sign) special_sign->invalidate();
2055 }
2056 else if (negative_sign && negative_sign->match(text, interval.end, end, flags)) {
2057 interval.end = negative_sign->interval.end;
2058 if (positive_sign) positive_sign->invalidate();
2059 if (special_sign) special_sign->invalidate();
2060 }
2061 else if (special_sign && special_sign->match(text, interval.end, end, flags)) {
2062 interval.end = special_sign->interval.end;
2063 if (positive_sign) positive_sign->invalidate();
2064 if (negative_sign) negative_sign->invalidate();
2065 }
2066 else {
2067 if (positive_sign) positive_sign->invalidate();
2068 if (negative_sign) negative_sign->invalidate();
2069 if (special_sign) special_sign->invalidate();
2070 }
2071
2072 if (integer->match(text, interval.end, end, flags))
2073 interval.end = integer->interval.end;
2074
2075 if (decimal_separator->match(text, interval.end, end, flags) &&
2076 decimal->match(text, decimal_separator->interval.end, end, flags))
2077 interval.end = decimal->interval.end;
2078 else {
2079 decimal_separator->invalidate();
2080 decimal->invalidate();
2081 }
2082
2083 if (!integer->interval.empty() &&
2084 decimal->interval.empty())
2085 {
2086 // No integer part, no decimal part.
2087 if (positive_sign) positive_sign->invalidate();
2088 if (negative_sign) negative_sign->invalidate();
2089 if (special_sign) special_sign->invalidate();
2090 integer->invalidate();
2091 decimal_separator->invalidate();
2092 decimal->invalidate();
2093 if (exponent_symbol) exponent_symbol->invalidate();
2094 if (positive_exp_sign) positive_exp_sign->invalidate();
2095 if (negative_exp_sign) negative_exp_sign->invalidate();
2096 if (exponent) exponent->invalidate();
2097 interval.start = (interval.end = start) + 1;
2098 return false;
2099 }
2100
2101 if (exponent_symbol && exponent_symbol->match(text, interval.end, end, flags) &&
2102 (positive_exp_sign && positive_exp_sign->match(text, exponent_symbol->interval.end, end, flags) &&
2103 exponent && exponent->match(text, positive_exp_sign->interval.end, end, flags) ||
2104 exponent && exponent->match(text, exponent_symbol->interval.end, end, flags)))
2105 {
2106 interval.end = exponent->interval.end;
2107 if (negative_exp_sign) negative_exp_sign->invalidate();
2108 }
2109 else if (exponent_symbol && exponent_symbol->match(text, interval.end, end, flags) &&
2110 negative_exp_sign && negative_exp_sign->match(text, exponent_symbol->interval.end, end, flags) &&
2111 exponent && exponent->match(text, negative_exp_sign->interval.end, end, flags))
2112 {
2113 interval.end = exponent->interval.end;
2114 if (positive_exp_sign) positive_exp_sign->invalidate();
2115 }
2116 else {
2117 if (exponent_symbol) exponent_symbol->invalidate();
2118 if (positive_exp_sign) positive_exp_sign->invalidate();
2119 if (negative_exp_sign) negative_exp_sign->invalidate();
2120 if (exponent) exponent->invalidate();
2121 }
2122
2123 value = (double)integer->value;
2124 if (decimal->interval)
2125 value += (double)decimal->value * pow(10.0, -(double)decimal->interval.size());
2126 if (negative_sign && negative_sign->interval)
2127 value = -value;
2128 if (exponent && exponent->interval) {
2129 double e = (double)exponent->value;
2130 if (negative_exp_sign && negative_exp_sign->interval)
2131 e = -e;
2132 value *= pow(10.0, e);
2133 }
2134
2135 interval.start = start;
2136 return true;
2137 }
2138
2139 virtual void invalidate()
2140 {
2141 if (positive_sign) positive_sign->invalidate();
2142 if (negative_sign) negative_sign->invalidate();
2143 if (special_sign) special_sign->invalidate();
2144 integer->invalidate();
2145 decimal_separator->invalidate();
2146 decimal->invalidate();
2147 if (exponent_symbol) exponent_symbol->invalidate();
2148 if (positive_exp_sign) positive_exp_sign->invalidate();
2149 if (negative_exp_sign) negative_exp_sign->invalidate();
2150 if (exponent) exponent->invalidate();
2151 value = std::numeric_limits<double>::quiet_NaN();
2153 }
2154
2155 public:
2156 std::shared_ptr<basic_tester<T>> positive_sign;
2157 std::shared_ptr<basic_tester<T>> negative_sign;
2158 std::shared_ptr<basic_tester<T>> special_sign;
2159 std::shared_ptr<basic_integer<T>> integer;
2160 std::shared_ptr<basic_tester<T>> decimal_separator;
2161 std::shared_ptr<basic_integer<T>> decimal;
2162 std::shared_ptr<basic_tester<T>> exponent_symbol;
2163 std::shared_ptr<basic_tester<T>> positive_exp_sign;
2164 std::shared_ptr<basic_tester<T>> negative_exp_sign;
2165 std::shared_ptr<basic_integer<T>> exponent;
2166 double value;
2167 };
2168
2171#ifdef _UNICODE
2173#else
2175#endif
2177
2181 template <class T>
2183 {
2184 public:
2186 _In_ const std::shared_ptr<basic_tester<T>>& _positive_sign,
2187 _In_ const std::shared_ptr<basic_tester<T>>& _negative_sign,
2188 _In_ const std::shared_ptr<basic_tester<T>>& _special_sign,
2189 _In_ const std::shared_ptr<basic_tester<T>>& _currency,
2190 _In_ const std::shared_ptr<basic_tester<T>>& _integer,
2191 _In_ const std::shared_ptr<basic_tester<T>>& _decimal_separator,
2192 _In_ const std::shared_ptr<basic_tester<T>>& _decimal,
2193 _In_ const std::locale& locale = std::locale()) :
2194 basic_tester<T>(locale),
2195 positive_sign(_positive_sign),
2196 negative_sign(_negative_sign),
2197 special_sign(_special_sign),
2198 currency(_currency),
2199 integer(_integer),
2200 decimal_separator(_decimal_separator),
2201 decimal(_decimal)
2202 {}
2203
2204 virtual bool match(
2205 _In_reads_or_z_(end) const T* text,
2206 _In_ size_t start = 0,
2207 _In_ size_t end = (size_t)-1,
2208 _In_ int flags = match_default)
2209 {
2210 assert(text || start >= end);
2211 interval.end = start;
2212
2213 if (positive_sign->match(text, interval.end, end, flags)) {
2214 interval.end = positive_sign->interval.end;
2215 if (negative_sign) negative_sign->invalidate();
2216 if (special_sign) special_sign->invalidate();
2217 }
2218 else if (negative_sign->match(text, interval.end, end, flags)) {
2219 interval.end = negative_sign->interval.end;
2220 if (positive_sign) positive_sign->invalidate();
2221 if (special_sign) special_sign->invalidate();
2222 }
2223 else if (special_sign->match(text, interval.end, end, flags)) {
2224 interval.end = special_sign->interval.end;
2225 if (positive_sign) positive_sign->invalidate();
2226 if (negative_sign) negative_sign->invalidate();
2227 }
2228 else {
2229 if (positive_sign) positive_sign->invalidate();
2230 if (negative_sign) negative_sign->invalidate();
2231 if (special_sign) special_sign->invalidate();
2232 }
2233
2234 if (currency->match(text, interval.end, end, flags))
2235 interval.end = currency->interval.end;
2236 else {
2237 if (positive_sign) positive_sign->invalidate();
2238 if (negative_sign) negative_sign->invalidate();
2239 if (special_sign) special_sign->invalidate();
2240 integer->invalidate();
2241 decimal_separator->invalidate();
2242 decimal->invalidate();
2243 interval.start = (interval.end = start) + 1;
2244 return false;
2245 }
2246
2247 if (integer->match(text, interval.end, end, flags))
2248 interval.end = integer->interval.end;
2249 if (decimal_separator->match(text, interval.end, end, flags) &&
2250 decimal->match(text, decimal_separator->interval.end, end, flags))
2251 interval.end = decimal->interval.end;
2252 else {
2253 decimal_separator->invalidate();
2254 decimal->invalidate();
2255 }
2256
2257 if (integer->interval.empty() &&
2258 decimal->interval.empty())
2259 {
2260 // No integer part, no decimal part.
2261 if (positive_sign) positive_sign->invalidate();
2262 if (negative_sign) negative_sign->invalidate();
2263 if (special_sign) special_sign->invalidate();
2264 currency->invalidate();
2265 integer->invalidate();
2266 decimal_separator->invalidate();
2267 decimal->invalidate();
2268 interval.start = (interval.end = start) + 1;
2269 return false;
2270 }
2271
2272 interval.start = start;
2273 return true;
2274 }
2275
2276 virtual void invalidate()
2277 {
2278 if (positive_sign) positive_sign->invalidate();
2279 if (negative_sign) negative_sign->invalidate();
2280 if (special_sign) special_sign->invalidate();
2281 currency->invalidate();
2282 integer->invalidate();
2283 decimal_separator->invalidate();
2284 decimal->invalidate();
2286 }
2287
2288 public:
2289 std::shared_ptr<basic_tester<T>> positive_sign;
2290 std::shared_ptr<basic_tester<T>> negative_sign;
2291 std::shared_ptr<basic_tester<T>> special_sign;
2292 std::shared_ptr<basic_tester<T>> currency;
2293 std::shared_ptr<basic_tester<T>> integer;
2294 std::shared_ptr<basic_tester<T>> decimal_separator;
2295 std::shared_ptr<basic_tester<T>> decimal;
2296 };
2297
2300#ifdef _UNICODE
2302#else
2304#endif
2306
2310 template <class T>
2312 {
2313 public:
2315 _In_ const std::shared_ptr<basic_tester<T>>& digit_0,
2316 _In_ const std::shared_ptr<basic_tester<T>>& digit_1,
2317 _In_ const std::shared_ptr<basic_tester<T>>& digit_2,
2318 _In_ const std::shared_ptr<basic_tester<T>>& digit_3,
2319 _In_ const std::shared_ptr<basic_tester<T>>& digit_4,
2320 _In_ const std::shared_ptr<basic_tester<T>>& digit_5,
2321 _In_ const std::shared_ptr<basic_tester<T>>& digit_6,
2322 _In_ const std::shared_ptr<basic_tester<T>>& digit_7,
2323 _In_ const std::shared_ptr<basic_tester<T>>& digit_8,
2324 _In_ const std::shared_ptr<basic_tester<T>>& digit_9,
2325 _In_ const std::shared_ptr<basic_tester<T>>& separator,
2326 _In_ const std::locale& locale = std::locale()) :
2327 basic_tester<T>(locale),
2328 m_digit_0(digit_0),
2329 m_digit_1(digit_1),
2330 m_digit_2(digit_2),
2331 m_digit_3(digit_3),
2332 m_digit_4(digit_4),
2333 m_digit_5(digit_5),
2334 m_digit_6(digit_6),
2335 m_digit_7(digit_7),
2336 m_digit_8(digit_8),
2337 m_digit_9(digit_9),
2338 m_separator(separator)
2339 {
2340 value.s_addr = 0;
2341 }
2342
2343 virtual bool match(
2344 _In_reads_or_z_(end) const T* text,
2345 _In_ size_t start = 0,
2346 _In_ size_t end = (size_t)-1,
2347 _In_ int flags = match_default)
2348 {
2349 assert(text || start >= end);
2350 interval.end = start;
2351 value.s_addr = 0;
2352
2353 size_t i;
2354 for (i = 0; i < 4; i++) {
2355 if (i) {
2356 if (m_separator->match(text, interval.end, end, flags))
2357 interval.end = m_separator->interval.end;
2358 else
2359 goto error;
2360 }
2361
2362 components[i].start = interval.end;
2363 bool is_empty = true;
2364 size_t x;
2365 for (x = 0; interval.end < end && text[interval.end];) {
2366 size_t dig, digit_end;
2367 if (m_digit_0->match(text, interval.end, end, flags)) { dig = 0; digit_end = m_digit_0->interval.end; }
2368 else if (m_digit_1->match(text, interval.end, end, flags)) { dig = 1; digit_end = m_digit_1->interval.end; }
2369 else if (m_digit_2->match(text, interval.end, end, flags)) { dig = 2; digit_end = m_digit_2->interval.end; }
2370 else if (m_digit_3->match(text, interval.end, end, flags)) { dig = 3; digit_end = m_digit_3->interval.end; }
2371 else if (m_digit_4->match(text, interval.end, end, flags)) { dig = 4; digit_end = m_digit_4->interval.end; }
2372 else if (m_digit_5->match(text, interval.end, end, flags)) { dig = 5; digit_end = m_digit_5->interval.end; }
2373 else if (m_digit_6->match(text, interval.end, end, flags)) { dig = 6; digit_end = m_digit_6->interval.end; }
2374 else if (m_digit_7->match(text, interval.end, end, flags)) { dig = 7; digit_end = m_digit_7->interval.end; }
2375 else if (m_digit_8->match(text, interval.end, end, flags)) { dig = 8; digit_end = m_digit_8->interval.end; }
2376 else if (m_digit_9->match(text, interval.end, end, flags)) { dig = 9; digit_end = m_digit_9->interval.end; }
2377 else break;
2378 size_t x_n = x * 10 + dig;
2379 if (x_n <= 255) {
2380 x = x_n;
2381 interval.end = digit_end;
2382 is_empty = false;
2383 }
2384 else
2385 break;
2386 }
2387 if (is_empty)
2388 goto error;
2389 components[i].end = interval.end;
2390 value.s_addr = (value.s_addr << 8) | (uint8_t)x;
2391 }
2392 if (i < 4)
2393 goto error;
2394
2395 interval.start = start;
2396 return true;
2397
2398 error:
2399 components[0].start = 1;
2400 components[0].end = 0;
2401 components[1].start = 1;
2402 components[1].end = 0;
2403 components[2].start = 1;
2404 components[2].end = 0;
2405 components[3].start = 1;
2406 components[3].end = 0;
2407 value = 0;
2408 interval.start = (interval.end = start) + 1;
2409 return false;
2410 }
2411
2412 virtual void invalidate()
2413 {
2414 components[0].start = 1;
2415 components[0].end = 0;
2416 components[1].start = 1;
2417 components[1].end = 0;
2418 components[2].start = 1;
2419 components[2].end = 0;
2420 components[3].start = 1;
2421 components[3].end = 0;
2422 value = 0;
2424 }
2425
2426 public:
2428 struct in_addr value;
2429
2430 protected:
2431 std::shared_ptr<basic_tester<T>>
2432 m_digit_0,
2433 m_digit_1,
2434 m_digit_2,
2435 m_digit_3,
2436 m_digit_4,
2437 m_digit_5,
2438 m_digit_6,
2439 m_digit_7,
2440 m_digit_8,
2441 m_digit_9;
2442 std::shared_ptr<basic_tester<T>> m_separator;
2443 };
2444
2447#ifdef _UNICODE
2449#else
2451#endif
2453
2457 template <class T>
2459 {
2460 public:
2461 basic_ipv6_scope_id_char(_In_ const std::locale& locale = std::locale()) : basic_tester<T>(locale)
2462 {}
2463
2464 virtual bool match(
2465 _In_reads_or_z_(end) const T* text,
2466 _In_ size_t start = 0,
2467 _In_ size_t end = (size_t)-1,
2468 _In_ int flags = match_default)
2469 {
2470 assert(text || start >= end);
2471 if (start < end && text[start]) {
2472 if (text[start] == '-' ||
2473 text[start] == '_' ||
2474 text[start] == ':' ||
2475 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::alnum, text[start]))
2476 {
2477 interval.end = (interval.start = start) + 1;
2478 return true;
2479 }
2480 }
2481 interval.start = (interval.end = start) + 1;
2482 return false;
2483 }
2484 };
2485
2488#ifdef _UNICODE
2490#else
2492#endif
2493
2498 {
2499 public:
2500 sgml_ipv6_scope_id_char(_In_ const std::locale& locale = std::locale()) : sgml_tester(locale)
2501 {}
2502
2503 virtual bool match(
2504 _In_reads_or_z_(end) const char* text,
2505 _In_ size_t start = 0,
2506 _In_ size_t end = (size_t)-1,
2507 _In_ int flags = match_default)
2508 {
2509 assert(text || start >= end);
2510 if (start < end && text[start]) {
2511 wchar_t buf[3];
2512 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
2513 const wchar_t* chr_end = chr + stdex::strlen(chr);
2514 if ((chr[0] == L'-' ||
2515 chr[0] == L'_' ||
2516 chr[0] == L':') && chr[1] == 0 ||
2517 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::alnum, chr, chr_end) == chr_end)
2518 {
2519 interval.start = start;
2520 return true;
2521 }
2522 }
2523 interval.start = (interval.end = start) + 1;
2524 return false;
2525 }
2526 };
2527
2531 template <class T>
2533 {
2534 public:
2536 _In_ const std::shared_ptr<basic_tester<T>>& digit_0,
2537 _In_ const std::shared_ptr<basic_tester<T>>& digit_1,
2538 _In_ const std::shared_ptr<basic_tester<T>>& digit_2,
2539 _In_ const std::shared_ptr<basic_tester<T>>& digit_3,
2540 _In_ const std::shared_ptr<basic_tester<T>>& digit_4,
2541 _In_ const std::shared_ptr<basic_tester<T>>& digit_5,
2542 _In_ const std::shared_ptr<basic_tester<T>>& digit_6,
2543 _In_ const std::shared_ptr<basic_tester<T>>& digit_7,
2544 _In_ const std::shared_ptr<basic_tester<T>>& digit_8,
2545 _In_ const std::shared_ptr<basic_tester<T>>& digit_9,
2546 _In_ const std::shared_ptr<basic_tester<T>>& digit_10,
2547 _In_ const std::shared_ptr<basic_tester<T>>& digit_11,
2548 _In_ const std::shared_ptr<basic_tester<T>>& digit_12,
2549 _In_ const std::shared_ptr<basic_tester<T>>& digit_13,
2550 _In_ const std::shared_ptr<basic_tester<T>>& digit_14,
2551 _In_ const std::shared_ptr<basic_tester<T>>& digit_15,
2552 _In_ const std::shared_ptr<basic_tester<T>>& separator,
2553 _In_ const std::shared_ptr<basic_tester<T>>& scope_id_separator = nullptr,
2554 _In_ const std::shared_ptr<basic_tester<T>>& _scope_id = nullptr,
2555 _In_ const std::locale& locale = std::locale()) :
2556 basic_tester<T>(locale),
2557 m_digit_0(digit_0),
2558 m_digit_1(digit_1),
2559 m_digit_2(digit_2),
2560 m_digit_3(digit_3),
2561 m_digit_4(digit_4),
2562 m_digit_5(digit_5),
2563 m_digit_6(digit_6),
2564 m_digit_7(digit_7),
2565 m_digit_8(digit_8),
2566 m_digit_9(digit_9),
2567 m_digit_10(digit_10),
2568 m_digit_11(digit_11),
2569 m_digit_12(digit_12),
2570 m_digit_13(digit_13),
2571 m_digit_14(digit_14),
2572 m_digit_15(digit_15),
2573 m_separator(separator),
2574 m_scope_id_separator(scope_id_separator),
2575 scope_id(_scope_id)
2576 {
2577 memset(value, 0, sizeof(value));
2578 }
2579
2580 virtual bool match(
2581 _In_reads_or_z_(end) const T* text,
2582 _In_ size_t start = 0,
2583 _In_ size_t end = (size_t)-1,
2584 _In_ int flags = match_default)
2585 {
2586 assert(text || start >= end);
2587 interval.end = start;
2588 memset(value, 0, sizeof(value));
2589
2590 size_t i, compaction_i = (size_t)-1, compaction_start = start;
2591 for (i = 0; i < 8; i++) {
2592 bool is_empty = true;
2593
2594 if (m_separator->match(text, interval.end, end, flags)) {
2595 if (m_separator->match(text, m_separator->interval.end, end, flags)) {
2596 // :: found
2597 if (compaction_i == (size_t)-1) {
2598 // Zero compaction start
2599 compaction_i = i;
2600 compaction_start = m_separator->interval.start;
2601 interval.end = m_separator->interval.end;
2602 }
2603 else {
2604 // More than one zero compaction
2605 break;
2606 }
2607 }
2608 else if (i) {
2609 // Inner : found
2610 interval.end = m_separator->interval.end;
2611 }
2612 else {
2613 // Leading : found
2614 goto error;
2615 }
2616 }
2617 else if (i) {
2618 // : missing
2619 break;
2620 }
2621
2622 components[i].start = interval.end;
2623 size_t x;
2624 for (x = 0; interval.end < end && text[interval.end];) {
2625 size_t dig, digit_end;
2626 if (m_digit_0->match(text, interval.end, end, flags)) { dig = 0; digit_end = m_digit_0->interval.end; }
2627 else if (m_digit_1->match(text, interval.end, end, flags)) { dig = 1; digit_end = m_digit_1->interval.end; }
2628 else if (m_digit_2->match(text, interval.end, end, flags)) { dig = 2; digit_end = m_digit_2->interval.end; }
2629 else if (m_digit_3->match(text, interval.end, end, flags)) { dig = 3; digit_end = m_digit_3->interval.end; }
2630 else if (m_digit_4->match(text, interval.end, end, flags)) { dig = 4; digit_end = m_digit_4->interval.end; }
2631 else if (m_digit_5->match(text, interval.end, end, flags)) { dig = 5; digit_end = m_digit_5->interval.end; }
2632 else if (m_digit_6->match(text, interval.end, end, flags)) { dig = 6; digit_end = m_digit_6->interval.end; }
2633 else if (m_digit_7->match(text, interval.end, end, flags)) { dig = 7; digit_end = m_digit_7->interval.end; }
2634 else if (m_digit_8->match(text, interval.end, end, flags)) { dig = 8; digit_end = m_digit_8->interval.end; }
2635 else if (m_digit_9->match(text, interval.end, end, flags)) { dig = 9; digit_end = m_digit_9->interval.end; }
2636 else if (m_digit_10->match(text, interval.end, end, flags)) { dig = 10; digit_end = m_digit_10->interval.end; }
2637 else if (m_digit_11->match(text, interval.end, end, flags)) { dig = 11; digit_end = m_digit_11->interval.end; }
2638 else if (m_digit_12->match(text, interval.end, end, flags)) { dig = 12; digit_end = m_digit_12->interval.end; }
2639 else if (m_digit_13->match(text, interval.end, end, flags)) { dig = 13; digit_end = m_digit_13->interval.end; }
2640 else if (m_digit_14->match(text, interval.end, end, flags)) { dig = 14; digit_end = m_digit_14->interval.end; }
2641 else if (m_digit_15->match(text, interval.end, end, flags)) { dig = 15; digit_end = m_digit_15->interval.end; }
2642 else break;
2643 size_t x_n = x * 16 + dig;
2644 if (x_n <= 0xffff) {
2645 x = x_n;
2646 interval.end = digit_end;
2647 is_empty = false;
2648 }
2649 else
2650 break;
2651 }
2652 if (is_empty) {
2653 if (compaction_i != (size_t)-1) {
2654 // Zero compaction active: no sweat.
2655 break;
2656 }
2657 goto error;
2658 }
2659 components[i].end = interval.end;
2660 value.s6_words[i] = (uint16_t)x;
2661 }
2662
2663 if (compaction_i != (size_t)-1) {
2664 // Align components right due to zero compaction.
2665 size_t j, k;
2666 for (j = 8, k = i; k > compaction_i;) {
2667 value.s6_words[--j] = value.s6_words[--k];
2668 components[j] = components[k];
2669 }
2670 for (; j > compaction_i;) {
2671 value.s6_words[--j] = 0;
2672 components[j].start =
2673 components[j].end = compaction_start;
2674 }
2675 }
2676 else if (i < 8)
2677 goto error;
2678
2679 if (m_scope_id_separator && m_scope_id_separator->match(text, interval.end, end, flags) &&
2680 scope_id && scope_id->match(text, m_scope_id_separator->interval.end, end, flags))
2681 interval.end = scope_id->interval.end;
2682 else if (scope_id)
2683 scope_id->invalidate();
2684
2685 interval.start = start;
2686 return true;
2687
2688 error:
2689 components[0].start = 1;
2690 components[0].end = 0;
2691 components[1].start = 1;
2692 components[1].end = 0;
2693 components[2].start = 1;
2694 components[2].end = 0;
2695 components[3].start = 1;
2696 components[3].end = 0;
2697 components[4].start = 1;
2698 components[4].end = 0;
2699 components[5].start = 1;
2700 components[5].end = 0;
2701 components[6].start = 1;
2702 components[6].end = 0;
2703 components[7].start = 1;
2704 components[7].end = 0;
2705 memset(value, 0, sizeof(value));
2706 if (scope_id) scope_id->invalidate();
2707 interval.start = (interval.end = start) + 1;
2708 return false;
2709 }
2710
2711 virtual void invalidate()
2712 {
2713 components[0].start = 1;
2714 components[0].end = 0;
2715 components[1].start = 1;
2716 components[1].end = 0;
2717 components[2].start = 1;
2718 components[2].end = 0;
2719 components[3].start = 1;
2720 components[3].end = 0;
2721 components[4].start = 1;
2722 components[4].end = 0;
2723 components[5].start = 1;
2724 components[5].end = 0;
2725 components[6].start = 1;
2726 components[6].end = 0;
2727 components[7].start = 1;
2728 components[7].end = 0;
2729 memset(value, 0, sizeof(value));
2730 if (scope_id) scope_id->invalidate();
2732 }
2733
2734 public:
2736 struct in6_addr value;
2737 std::shared_ptr<basic_tester<T>> scope_id;
2738
2739 protected:
2740 std::shared_ptr<basic_tester<T>>
2741 m_digit_0,
2742 m_digit_1,
2743 m_digit_2,
2744 m_digit_3,
2745 m_digit_4,
2746 m_digit_5,
2747 m_digit_6,
2748 m_digit_7,
2749 m_digit_8,
2750 m_digit_9,
2751 m_digit_10,
2752 m_digit_11,
2753 m_digit_12,
2754 m_digit_13,
2755 m_digit_14,
2756 m_digit_15;
2757 std::shared_ptr<basic_tester<T>> m_separator, m_scope_id_separator;
2758 };
2759
2762#ifdef _UNICODE
2764#else
2766#endif
2768
2772 template <class T>
2774 {
2775 public:
2777 _In_ bool allow_idn,
2778 _In_ const std::locale& locale = std::locale()) :
2779 basic_tester<T>(locale),
2780 m_allow_idn(allow_idn),
2781 allow_on_edge(true)
2782 {}
2783
2784 virtual bool match(
2785 _In_reads_or_z_(end) const T* text,
2786 _In_ size_t start = 0,
2787 _In_ size_t end = (size_t)-1,
2788 _In_ int flags = match_default)
2789 {
2790 assert(text || start >= end);
2791 if (start < end && text[start]) {
2792 if (('A' <= text[start] && text[start] <= 'Z') ||
2793 ('a' <= text[start] && text[start] <= 'z') ||
2794 ('0' <= text[start] && text[start] <= '9'))
2795 allow_on_edge = true;
2796 else if (text[start] == '-')
2797 allow_on_edge = false;
2798 else if (m_allow_idn && std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::alnum, text[start]))
2799 allow_on_edge = true;
2800 else {
2801 interval.start = (interval.end = start) + 1;
2802 return false;
2803 }
2804 interval.end = (interval.start = start) + 1;
2805 return true;
2806 }
2807 interval.start = (interval.end = start) + 1;
2808 return false;
2809 }
2810
2811 public:
2813
2814 protected:
2815 bool m_allow_idn;
2816 };
2817
2820#ifdef _UNICODE
2822#else
2824#endif
2825
2830 {
2831 public:
2833 _In_ bool allow_idn,
2834 _In_ const std::locale& locale = std::locale()) :
2835 basic_dns_domain_char<char>(allow_idn, locale)
2836 {}
2837
2838 virtual bool match(
2839 _In_reads_or_z_(end) const char* text,
2840 _In_ size_t start = 0,
2841 _In_ size_t end = (size_t)-1,
2842 _In_ int flags = match_default)
2843 {
2844 assert(text || start >= end);
2845 if (start < end && text[start]) {
2846 wchar_t buf[3];
2847 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
2848 const wchar_t* chr_end = chr + stdex::strlen(chr);
2849 if ((('A' <= chr[0] && chr[0] <= 'Z') ||
2850 ('a' <= chr[0] && chr[0] <= 'z') ||
2851 ('0' <= chr[0] && chr[0] <= '9')) && chr[1] == 0)
2852 allow_on_edge = true;
2853 else if (chr[0] == '-' && chr[1] == 0)
2854 allow_on_edge = false;
2855 else if (m_allow_idn && std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::alnum, chr, chr_end) == chr_end)
2856 allow_on_edge = true;
2857 else {
2858 interval.start = (interval.end = start) + 1;
2859 return false;
2860 }
2861 interval.start = start;
2862 return true;
2863 }
2864 interval.start = (interval.end = start) + 1;
2865 return false;
2866 }
2867 };
2868
2872 template <class T>
2874 {
2875 public:
2877 _In_ bool allow_absolute,
2878 _In_ const std::shared_ptr<basic_dns_domain_char<T>>& domain_char,
2879 _In_ const std::shared_ptr<basic_tester<T>>& separator,
2880 _In_ const std::locale& locale = std::locale()) :
2881 basic_tester<T>(locale),
2882 m_allow_absolute(allow_absolute),
2883 m_domain_char(domain_char),
2884 m_separator(separator)
2885 {}
2886
2887 virtual bool match(
2888 _In_reads_or_z_(end) const T* text,
2889 _In_ size_t start = 0,
2890 _In_ size_t end = (size_t)-1,
2891 _In_ int flags = match_default)
2892 {
2893 assert(text || start >= end);
2894 size_t i = start, count;
2895 for (count = 0; i < end && text[i] && count < 127; count++) {
2896 if (m_domain_char->match(text, i, end, flags) &&
2897 m_domain_char->allow_on_edge)
2898 {
2899 // Domain start
2900 interval.end = i = m_domain_char->interval.end;
2901 while (i < end && text[i]) {
2902 if (m_domain_char->allow_on_edge &&
2903 m_separator->match(text, i, end, flags))
2904 {
2905 // Domain end
2906 if (m_allow_absolute)
2907 interval.end = i = m_separator->interval.end;
2908 else {
2909 interval.end = i;
2910 i = m_separator->interval.end;
2911 }
2912 break;
2913 }
2914 if (m_domain_char->match(text, i, end, flags)) {
2915 if (m_domain_char->allow_on_edge)
2916 interval.end = i = m_domain_char->interval.end;
2917 else
2918 i = m_domain_char->interval.end;
2919 }
2920 else {
2921 interval.start = start;
2922 return true;
2923 }
2924 }
2925 }
2926 else
2927 break;
2928 }
2929 if (count) {
2930 interval.start = start;
2931 return true;
2932 }
2933 interval.start = (interval.end = start) + 1;
2934 return false;
2935 }
2936
2937 protected:
2939 std::shared_ptr<basic_dns_domain_char<T>> m_domain_char;
2940 std::shared_ptr<basic_tester<T>> m_separator;
2941 };
2942
2945#ifdef _UNICODE
2946 using tdns_name = wdns_name;
2947#else
2948 using tdns_name = dns_name;
2949#endif
2951
2955 template <class T>
2957 {
2958 public:
2959 basic_url_username_char(_In_ const std::locale& locale = std::locale()) : basic_tester<T>(locale) {}
2960
2961 virtual bool match(
2962 _In_reads_or_z_(end) const T* text,
2963 _In_ size_t start = 0,
2964 _In_ size_t end = (size_t)-1,
2965 _In_ int flags = match_default)
2966 {
2967 assert(text || start >= end);
2968 if (start < end && text[start]) {
2969 if (text[start] == '-' ||
2970 text[start] == '.' ||
2971 text[start] == '_' ||
2972 text[start] == '~' ||
2973 text[start] == '%' ||
2974 text[start] == '!' ||
2975 text[start] == '$' ||
2976 text[start] == '&' ||
2977 text[start] == '\'' ||
2978 //text[start] == '(' ||
2979 //text[start] == ')' ||
2980 text[start] == '*' ||
2981 text[start] == '+' ||
2982 text[start] == ',' ||
2983 text[start] == ';' ||
2984 text[start] == '=' ||
2985 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::alnum, text[start]))
2986 {
2987 interval.end = (interval.start = start) + 1;
2988 return true;
2989 }
2990 }
2991 interval.start = (interval.end = start) + 1;
2992 return false;
2993 }
2994 };
2995
2998#ifdef _UNICODE
3000#else
3002#endif
3003
3008 {
3009 public:
3010 sgml_url_username_char(_In_ const std::locale& locale = std::locale()) : basic_url_username_char<char>(locale) {}
3011
3012 virtual bool match(
3013 _In_reads_or_z_(end) const char* text,
3014 _In_ size_t start = 0,
3015 _In_ size_t end = (size_t)-1,
3016 _In_ int flags = match_default)
3017 {
3018 assert(text || start >= end);
3019 if (start < end && text[start]) {
3020 wchar_t buf[3];
3021 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
3022 const wchar_t* chr_end = chr + stdex::strlen(chr);
3023 if ((chr[0] == L'-' ||
3024 chr[0] == L'.' ||
3025 chr[0] == L'_' ||
3026 chr[0] == L'~' ||
3027 chr[0] == L'%' ||
3028 chr[0] == L'!' ||
3029 chr[0] == L'$' ||
3030 chr[0] == L'&' ||
3031 chr[0] == L'\'' ||
3032 //chr[0] == L'(' ||
3033 //chr[0] == L')' ||
3034 chr[0] == L'*' ||
3035 chr[0] == L'+' ||
3036 chr[0] == L',' ||
3037 chr[0] == L';' ||
3038 chr[0] == L'=') && chr[1] == 0 ||
3039 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::alnum, chr, chr_end) == chr_end)
3040 {
3041 interval.start = start;
3042 return true;
3043 }
3044 }
3045
3046 interval.start = (interval.end = start) + 1;
3047 return false;
3048 }
3049 };
3050
3054 template <class T>
3056 {
3057 public:
3058 basic_url_password_char(_In_ const std::locale& locale = std::locale()) : basic_tester<T>(locale) {}
3059
3060 virtual bool match(
3061 _In_reads_or_z_(end) const T* text,
3062 _In_ size_t start = 0,
3063 _In_ size_t end = (size_t)-1,
3064 _In_ int flags = match_default)
3065 {
3066 assert(text || start >= end);
3067 if (start < end && text[start]) {
3068 if (text[start] == '-' ||
3069 text[start] == '.' ||
3070 text[start] == '_' ||
3071 text[start] == '~' ||
3072 text[start] == '%' ||
3073 text[start] == '!' ||
3074 text[start] == '$' ||
3075 text[start] == '&' ||
3076 text[start] == '\'' ||
3077 text[start] == '(' ||
3078 text[start] == ')' ||
3079 text[start] == '*' ||
3080 text[start] == '+' ||
3081 text[start] == ',' ||
3082 text[start] == ';' ||
3083 text[start] == '=' ||
3084 text[start] == ':' ||
3085 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::alnum, text[start]))
3086 {
3087 interval.end = (interval.start = start) + 1;
3088 return true;
3089 }
3090 }
3091 interval.start = (interval.end = start) + 1;
3092 return false;
3093 }
3094 };
3095
3098#ifdef _UNICODE
3100#else
3102#endif
3103
3108 {
3109 public:
3110 sgml_url_password_char(_In_ const std::locale& locale = std::locale()) : basic_url_password_char<char>(locale) {}
3111
3112 virtual bool match(
3113 _In_reads_or_z_(end) const char* text,
3114 _In_ size_t start = 0,
3115 _In_ size_t end = (size_t)-1,
3116 _In_ int flags = match_default)
3117 {
3118 assert(text || start >= end);
3119 if (start < end && text[start]) {
3120 wchar_t buf[3];
3121 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
3122 const wchar_t* chr_end = chr + stdex::strlen(chr);
3123 if ((chr[0] == L'-' ||
3124 chr[0] == L'.' ||
3125 chr[0] == L'_' ||
3126 chr[0] == L'~' ||
3127 chr[0] == L'%' ||
3128 chr[0] == L'!' ||
3129 chr[0] == L'$' ||
3130 chr[0] == L'&' ||
3131 chr[0] == L'\'' ||
3132 chr[0] == L'(' ||
3133 chr[0] == L')' ||
3134 chr[0] == L'*' ||
3135 chr[0] == L'+' ||
3136 chr[0] == L',' ||
3137 chr[0] == L';' ||
3138 chr[0] == L'=' ||
3139 chr[0] == L':') && chr[1] == 0 ||
3140 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::alnum, chr, chr_end) == chr_end)
3141 {
3142 interval.start = start;
3143 return true;
3144 }
3145 }
3146 interval.start = (interval.end = start) + 1;
3147 return false;
3148 }
3149 };
3150
3154 template <class T>
3156 {
3157 public:
3158 basic_url_path_char(_In_ const std::locale& locale = std::locale()) : basic_tester<T>(locale) {}
3159
3160 virtual bool match(
3161 _In_reads_or_z_(end) const T* text,
3162 _In_ size_t start = 0,
3163 _In_ size_t end = (size_t)-1,
3164 _In_ int flags = match_default)
3165 {
3166 assert(text || start >= end);
3167 if (start < end && text[start]) {
3168 if (text[start] == '/' ||
3169 text[start] == '-' ||
3170 text[start] == '.' ||
3171 text[start] == '_' ||
3172 text[start] == '~' ||
3173 text[start] == '%' ||
3174 text[start] == '!' ||
3175 text[start] == '$' ||
3176 text[start] == '&' ||
3177 text[start] == '\'' ||
3178 text[start] == '(' ||
3179 text[start] == ')' ||
3180 text[start] == '*' ||
3181 text[start] == '+' ||
3182 text[start] == ',' ||
3183 text[start] == ';' ||
3184 text[start] == '=' ||
3185 text[start] == ':' ||
3186 text[start] == '@' ||
3187 text[start] == '?' ||
3188 text[start] == '#' ||
3189 std::use_facet<std::ctype<T>>(m_locale).is(std::ctype_base::alnum, text[start]))
3190 {
3191 interval.end = (interval.start = start) + 1;
3192 return true;
3193 }
3194 }
3195 interval.start = (interval.end = start) + 1;
3196 return false;
3197 }
3198 };
3199
3202#ifdef _UNICODE
3204#else
3206#endif
3207
3212 {
3213 public:
3214 sgml_url_path_char(_In_ const std::locale& locale = std::locale()) : basic_url_path_char<char>(locale) {}
3215
3216 virtual bool match(
3217 _In_reads_or_z_(end) const char* text,
3218 _In_ size_t start = 0,
3219 _In_ size_t end = (size_t)-1,
3220 _In_ int flags = match_default)
3221 {
3222 assert(text || start >= end);
3223 if (start < end && text[start]) {
3224 wchar_t buf[3];
3225 const wchar_t* chr = next_sgml_cp(text, start, end, interval.end, buf);
3226 const wchar_t* chr_end = chr + stdex::strlen(chr);
3227 if ((chr[0] == L'/' ||
3228 chr[0] == L'-' ||
3229 chr[0] == L'.' ||
3230 chr[0] == L'_' ||
3231 chr[0] == L'~' ||
3232 chr[0] == L'%' ||
3233 chr[0] == L'!' ||
3234 chr[0] == L'$' ||
3235 chr[0] == L'&' ||
3236 chr[0] == L'\'' ||
3237 chr[0] == L'(' ||
3238 chr[0] == L')' ||
3239 chr[0] == L'*' ||
3240 chr[0] == L'+' ||
3241 chr[0] == L',' ||
3242 chr[0] == L';' ||
3243 chr[0] == L'=' ||
3244 chr[0] == L':' ||
3245 chr[0] == L'@' ||
3246 chr[0] == L'?' ||
3247 chr[0] == L'#') && chr[1] == 0 ||
3248 std::use_facet<std::ctype<wchar_t>>(m_locale).scan_not(std::ctype_base::alnum, chr, chr_end) == chr_end)
3249 {
3250 interval.start = start;
3251 return true;
3252 }
3253 }
3254 interval.start = (interval.end = start) + 1;
3255 return false;
3256 }
3257 };
3258
3262 template <class T>
3264 {
3265 public:
3267 _In_ const std::shared_ptr<basic_tester<T>>& path_char,
3268 _In_ const std::shared_ptr<basic_tester<T>>& query_start,
3269 _In_ const std::shared_ptr<basic_tester<T>>& bookmark_start,
3270 _In_ const std::locale& locale = std::locale()) :
3271 basic_tester<T>(locale),
3272 m_path_char(path_char),
3273 m_query_start(query_start),
3274 m_bookmark_start(bookmark_start)
3275 {}
3276
3277 virtual bool match(
3278 _In_reads_or_z_(end) const T* text,
3279 _In_ size_t start = 0,
3280 _In_ size_t end = (size_t)-1,
3281 _In_ int flags = match_default)
3282 {
3283 assert(text || start >= end);
3284
3285 interval.end = start;
3286 path.start = start;
3287 query.start = 1;
3288 query.end = 0;
3289 bookmark.start = 1;
3290 bookmark.end = 0;
3291
3292 for (;;) {
3293 if (interval.end >= end || !text[interval.end])
3294 break;
3295 if (m_query_start->match(text, interval.end, end, flags)) {
3296 path.end = interval.end;
3297 query.start = interval.end = m_query_start->interval.end;
3298 for (;;) {
3299 if (interval.end >= end || !text[interval.end]) {
3300 query.end = interval.end;
3301 break;
3302 }
3303 if (m_bookmark_start->match(text, interval.end, end, flags)) {
3304 query.end = interval.end;
3305 bookmark.start = interval.end = m_bookmark_start->interval.end;
3306 for (;;) {
3307 if (interval.end >= end || !text[interval.end]) {
3308 bookmark.end = interval.end;
3309 break;
3310 }
3311 if (m_path_char->match(text, interval.end, end, flags))
3312 interval.end = m_path_char->interval.end;
3313 else {
3314 bookmark.end = interval.end;
3315 break;
3316 }
3317 }
3318 interval.start = start;
3319 return true;
3320 }
3321 if (m_path_char->match(text, interval.end, end, flags))
3322 interval.end = m_path_char->interval.end;
3323 else {
3324 query.end = interval.end;
3325 break;
3326 }
3327 }
3328 interval.start = start;
3329 return true;
3330 }
3331 if (m_bookmark_start->match(text, interval.end, end, flags)) {
3332 path.end = interval.end;
3333 bookmark.start = interval.end = m_bookmark_start->interval.end;
3334 for (;;) {
3335 if (interval.end >= end || !text[interval.end]) {
3336 bookmark.end = interval.end;
3337 break;
3338 }
3339 if (m_path_char->match(text, interval.end, end, flags))
3340 interval.end = m_path_char->interval.end;
3341 else {
3342 bookmark.end = interval.end;
3343 break;
3344 }
3345 }
3346 interval.start = start;
3347 return true;
3348 }
3349 if (m_path_char->match(text, interval.end, end, flags))
3350 interval.end = m_path_char->interval.end;
3351 else
3352 break;
3353 }
3354
3355 if (start < interval.end) {
3356 path.end = interval.end;
3357 interval.start = start;
3358 return true;
3359 }
3360
3361 path.start = 1;
3362 path.end = 0;
3363 bookmark.start = 1;
3364 bookmark.end = 0;
3365 interval.start = (interval.end = start) + 1;
3366 return false;
3367 }
3368
3369 virtual void invalidate()
3370 {
3371 path.start = 1;
3372 path.end = 0;
3373 query.start = 1;
3374 query.end = 0;
3375 bookmark.start = 1;
3376 bookmark.end = 0;
3378 }
3379
3380 public:
3383 stdex::interval<size_t> bookmark;
3384
3385 protected:
3386 std::shared_ptr<basic_tester<T>> m_path_char;
3387 std::shared_ptr<basic_tester<T>> m_query_start;
3388 std::shared_ptr<basic_tester<T>> m_bookmark_start;
3389 };
3390
3393#ifdef _UNICODE
3394 using turl_path = wurl_path;
3395#else
3396 using turl_path = url_path;
3397#endif
3399
3403 template <class T>
3404 class basic_url : public basic_tester<T>
3405 {
3406 public:
3407 basic_url(
3408 _In_ const std::shared_ptr<basic_tester<T>>& _http_scheme,
3409 _In_ const std::shared_ptr<basic_tester<T>>& _ftp_scheme,
3410 _In_ const std::shared_ptr<basic_tester<T>>& _mailto_scheme,
3411 _In_ const std::shared_ptr<basic_tester<T>>& _file_scheme,
3412 _In_ const std::shared_ptr<basic_tester<T>>& colon,
3413 _In_ const std::shared_ptr<basic_tester<T>>& slash,
3414 _In_ const std::shared_ptr<basic_tester<T>>& _username,
3415 _In_ const std::shared_ptr<basic_tester<T>>& _password,
3416 _In_ const std::shared_ptr<basic_tester<T>>& at,
3417 _In_ const std::shared_ptr<basic_tester<T>>& ip_lbracket,
3418 _In_ const std::shared_ptr<basic_tester<T>>& ip_rbracket,
3419 _In_ const std::shared_ptr<basic_tester<T>>& _ipv4_host,
3420 _In_ const std::shared_ptr<basic_tester<T>>& _ipv6_host,
3421 _In_ const std::shared_ptr<basic_tester<T>>& _dns_host,
3422 _In_ const std::shared_ptr<basic_tester<T>>& _port,
3423 _In_ const std::shared_ptr<basic_tester<T>>& _path,
3424 _In_ const std::locale& locale = std::locale()) :
3425 basic_tester<T>(locale),
3426 http_scheme(_http_scheme),
3427 ftp_scheme(_ftp_scheme),
3428 mailto_scheme(_mailto_scheme),
3429 file_scheme(_file_scheme),
3430 m_colon(colon),
3431 m_slash(slash),
3432 username(_username),
3433 password(_password),
3434 m_at(at),
3435 m_ip_lbracket(ip_lbracket),
3436 m_ip_rbracket(ip_rbracket),
3437 ipv4_host(_ipv4_host),
3438 ipv6_host(_ipv6_host),
3439 dns_host(_dns_host),
3440 port(_port),
3441 path(_path)
3442 {}
3443
3444 virtual bool match(
3445 _In_reads_or_z_(end) const T* text,
3446 _In_ size_t start = 0,
3447 _In_ size_t end = (size_t)-1,
3448 _In_ int flags = match_default)
3449 {
3450 assert(text || start >= end);
3451
3452 interval.end = start;
3453
3454 if (http_scheme->match(text, interval.end, end, flags) &&
3455 m_colon->match(text, http_scheme->interval.end, end, flags) &&
3456 m_slash->match(text, m_colon->interval.end, end, flags) &&
3457 m_slash->match(text, m_slash->interval.end, end, flags))
3458 {
3459 // http://
3460 interval.end = m_slash->interval.end;
3461 ftp_scheme->invalidate();
3462 mailto_scheme->invalidate();
3463 file_scheme->invalidate();
3464 }
3465 else if (ftp_scheme->match(text, interval.end, end, flags) &&
3466 m_colon->match(text, ftp_scheme->interval.end, end, flags) &&
3467 m_slash->match(text, m_colon->interval.end, end, flags) &&
3468 m_slash->match(text, m_slash->interval.end, end, flags))
3469 {
3470 // ftp://
3471 interval.end = m_slash->interval.end;
3472 http_scheme->invalidate();
3473 mailto_scheme->invalidate();
3474 file_scheme->invalidate();
3475 }
3476 else if (mailto_scheme->match(text, interval.end, end, flags) &&
3477 m_colon->match(text, mailto_scheme->interval.end, end, flags))
3478 {
3479 // mailto:
3480 interval.end = m_colon->interval.end;
3481 http_scheme->invalidate();
3482 ftp_scheme->invalidate();
3483 file_scheme->invalidate();
3484 }
3485 else if (file_scheme->match(text, interval.end, end, flags) &&
3486 m_colon->match(text, file_scheme->interval.end, end, flags) &&
3487 m_slash->match(text, m_colon->interval.end, end, flags) &&
3488 m_slash->match(text, m_slash->interval.end, end, flags))
3489 {
3490 // file://
3491 interval.end = m_slash->interval.end;
3492 http_scheme->invalidate();
3493 ftp_scheme->invalidate();
3494 mailto_scheme->invalidate();
3495 }
3496 else {
3497 // Default to http:
3498 http_scheme->invalidate();
3499 ftp_scheme->invalidate();
3500 mailto_scheme->invalidate();
3501 file_scheme->invalidate();
3502 }
3503
3504 if (ftp_scheme->interval) {
3505 if (username->match(text, interval.end, end, flags)) {
3506 if (m_colon->match(text, username->interval.end, end, flags) &&
3507 password->match(text, m_colon->interval.end, end, flags) &&
3508 m_at->match(text, password->interval.end, end, flags))
3509 {
3510 // Username and password
3511 interval.end = m_at->interval.end;
3512 }
3513 else if (m_at->match(text, interval.end, end, flags)) {
3514 // Username only
3515 interval.end = m_at->interval.end;
3516 password->invalidate();
3517 }
3518 else {
3519 username->invalidate();
3520 password->invalidate();
3521 }
3522 }
3523 else {
3524 username->invalidate();
3525 password->invalidate();
3526 }
3527
3528 if (ipv4_host->match(text, interval.end, end, flags)) {
3529 // Host is IPv4
3530 interval.end = ipv4_host->interval.end;
3531 ipv6_host->invalidate();
3532 dns_host->invalidate();
3533 }
3534 else if (
3535 m_ip_lbracket->match(text, interval.end, end, flags) &&
3536 ipv6_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3537 m_ip_rbracket->match(text, ipv6_host->interval.end, end, flags))
3538 {
3539 // Host is IPv6
3540 interval.end = m_ip_rbracket->interval.end;
3541 ipv4_host->invalidate();
3542 dns_host->invalidate();
3543 }
3544 else if (dns_host->match(text, interval.end, end, flags)) {
3545 // Host is hostname
3546 interval.end = dns_host->interval.end;
3547 ipv4_host->invalidate();
3548 ipv6_host->invalidate();
3549 }
3550 else {
3551 invalidate();
3552 return false;
3553 }
3554
3555 if (m_colon->match(text, interval.end, end, flags) &&
3556 port->match(text, m_colon->interval.end, end, flags))
3557 {
3558 // Port
3559 interval.end = port->interval.end;
3560 }
3561 else
3562 port->invalidate();
3563
3564 if (path->match(text, interval.end, end, flags)) {
3565 // Path
3566 interval.end = path->interval.end;
3567 }
3568
3569 interval.start = start;
3570 return true;
3571 }
3572
3573 if (mailto_scheme->interval) {
3574 if (username->match(text, interval.end, end, flags) &&
3575 m_at->match(text, username->interval.end, end, flags))
3576 {
3577 // Username
3578 interval.end = m_at->interval.end;
3579 }
3580 else {
3581 invalidate();
3582 return false;
3583 }
3584
3585 if (m_ip_lbracket->match(text, interval.end, end, flags) &&
3586 ipv4_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3587 m_ip_rbracket->match(text, ipv4_host->interval.end, end, flags))
3588 {
3589 // Host is IPv4
3590 interval.end = m_ip_rbracket->interval.end;
3591 ipv6_host->invalidate();
3592 dns_host->invalidate();
3593 }
3594 else if (
3595 m_ip_lbracket->match(text, interval.end, end, flags) &&
3596 ipv6_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3597 m_ip_rbracket->match(text, ipv6_host->interval.end, end, flags))
3598 {
3599 // Host is IPv6
3600 interval.end = m_ip_rbracket->interval.end;
3601 ipv4_host->invalidate();
3602 dns_host->invalidate();
3603 }
3604 else if (dns_host->match(text, interval.end, end, flags)) {
3605 // Host is hostname
3606 interval.end = dns_host->interval.end;
3607 ipv4_host->invalidate();
3608 ipv6_host->invalidate();
3609 }
3610 else {
3611 invalidate();
3612 return false;
3613 }
3614
3615 password->invalidate();
3616 port->invalidate();
3617 path->invalidate();
3618 interval.start = start;
3619 return true;
3620 }
3621
3622 if (file_scheme->interval) {
3623 if (path->match(text, interval.end, end, flags)) {
3624 // Path
3625 interval.end = path->interval.end;
3626 }
3627
3628 username->invalidate();
3629 password->invalidate();
3630 ipv4_host->invalidate();
3631 ipv6_host->invalidate();
3632 dns_host->invalidate();
3633 port->invalidate();
3634 interval.start = start;
3635 return true;
3636 }
3637
3638 // "http://" found or defaulted to
3639
3640 // If "http://" explicit, test for username&password.
3641 if (http_scheme->interval &&
3642 username->match(text, interval.end, end, flags))
3643 {
3644 if (m_colon->match(text, username->interval.end, end, flags) &&
3645 password->match(text, m_colon->interval.end, end, flags) &&
3646 m_at->match(text, password->interval.end, end, flags))
3647 {
3648 // Username and password
3649 interval.end = m_at->interval.end;
3650 }
3651 else if (m_at->match(text, username->interval.end, end, flags)) {
3652 // Username only
3653 interval.end = m_at->interval.end;
3654 password->invalidate();
3655 }
3656 else {
3657 username->invalidate();
3658 password->invalidate();
3659 }
3660 }
3661 else {
3662 username->invalidate();
3663 password->invalidate();
3664 }
3665
3666 if (ipv4_host->match(text, interval.end, end, flags)) {
3667 // Host is IPv4
3668 interval.end = ipv4_host->interval.end;
3669 ipv6_host->invalidate();
3670 dns_host->invalidate();
3671 }
3672 else if (
3673 m_ip_lbracket->match(text, interval.end, end, flags) &&
3674 ipv6_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3675 m_ip_rbracket->match(text, ipv6_host->interval.end, end, flags))
3676 {
3677 // Host is IPv6
3678 interval.end = m_ip_rbracket->interval.end;
3679 ipv4_host->invalidate();
3680 dns_host->invalidate();
3681 }
3682 else if (dns_host->match(text, interval.end, end, flags)) {
3683 // Host is hostname
3684 interval.end = dns_host->interval.end;
3685 ipv4_host->invalidate();
3686 ipv6_host->invalidate();
3687 }
3688 else {
3689 invalidate();
3690 return false;
3691 }
3692
3693 if (m_colon->match(text, interval.end, end, flags) &&
3694 port->match(text, m_colon->interval.end, end, flags))
3695 {
3696 // Port
3697 interval.end = port->interval.end;
3698 }
3699 else
3700 port->invalidate();
3701
3702 if (path->match(text, interval.end, end, flags)) {
3703 // Path
3704 interval.end = path->interval.end;
3705 }
3706
3707 interval.start = start;
3708 return true;
3709 }
3710
3711 virtual void invalidate()
3712 {
3713 http_scheme->invalidate();
3714 ftp_scheme->invalidate();
3715 mailto_scheme->invalidate();
3716 file_scheme->invalidate();
3717 username->invalidate();
3718 password->invalidate();
3719 ipv4_host->invalidate();
3720 ipv6_host->invalidate();
3721 dns_host->invalidate();
3722 port->invalidate();
3723 path->invalidate();
3725 }
3726
3727 public:
3728 std::shared_ptr<basic_tester<T>> http_scheme;
3729 std::shared_ptr<basic_tester<T>> ftp_scheme;
3730 std::shared_ptr<basic_tester<T>> mailto_scheme;
3731 std::shared_ptr<basic_tester<T>> file_scheme;
3732 std::shared_ptr<basic_tester<T>> username;
3733 std::shared_ptr<basic_tester<T>> password;
3734 std::shared_ptr<basic_tester<T>> ipv4_host;
3735 std::shared_ptr<basic_tester<T>> ipv6_host;
3736 std::shared_ptr<basic_tester<T>> dns_host;
3737 std::shared_ptr<basic_tester<T>> port;
3738 std::shared_ptr<basic_tester<T>> path;
3739
3740 protected:
3741 std::shared_ptr<basic_tester<T>> m_colon;
3742 std::shared_ptr<basic_tester<T>> m_slash;
3743 std::shared_ptr<basic_tester<T>> m_at;
3744 std::shared_ptr<basic_tester<T>> m_ip_lbracket;
3745 std::shared_ptr<basic_tester<T>> m_ip_rbracket;
3746 };
3747
3748 using url = basic_url<char>;
3749 using wurl = basic_url<wchar_t>;
3750#ifdef _UNICODE
3751 using turl = wurl;
3752#else
3753 using turl = url;
3754#endif
3755 using sgml_url = basic_url<char>;
3756
3760 template <class T>
3762 {
3763 public:
3765 _In_ const std::shared_ptr<basic_tester<T>>& _username,
3766 _In_ const std::shared_ptr<basic_tester<T>>& at,
3767 _In_ const std::shared_ptr<basic_tester<T>>& ip_lbracket,
3768 _In_ const std::shared_ptr<basic_tester<T>>& ip_rbracket,
3769 _In_ const std::shared_ptr<basic_tester<T>>& _ipv4_host,
3770 _In_ const std::shared_ptr<basic_tester<T>>& _ipv6_host,
3771 _In_ const std::shared_ptr<basic_tester<T>>& _dns_host,
3772 _In_ const std::locale& locale = std::locale()) :
3773 basic_tester<T>(locale),
3774 username(_username),
3775 m_at(at),
3776 m_ip_lbracket(ip_lbracket),
3777 m_ip_rbracket(ip_rbracket),
3778 ipv4_host(_ipv4_host),
3779 ipv6_host(_ipv6_host),
3780 dns_host(_dns_host)
3781 {}
3782
3783 virtual bool match(
3784 _In_reads_or_z_(end) const T* text,
3785 _In_ size_t start = 0,
3786 _In_ size_t end = (size_t)-1,
3787 _In_ int flags = match_default)
3788 {
3789 assert(text || start >= end);
3790
3791 if (username->match(text, start, end, flags) &&
3792 m_at->match(text, username->interval.end, end, flags))
3793 {
3794 // Username@
3795 if (m_ip_lbracket->match(text, m_at->interval.end, end, flags) &&
3796 ipv4_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3797 m_ip_rbracket->match(text, ipv4_host->interval.end, end, flags))
3798 {
3799 // Host is IPv4
3800 interval.end = m_ip_rbracket->interval.end;
3801 ipv6_host->invalidate();
3802 dns_host->invalidate();
3803 }
3804 else if (
3805 m_ip_lbracket->match(text, m_at->interval.end, end, flags) &&
3806 ipv6_host->match(text, m_ip_lbracket->interval.end, end, flags) &&
3807 m_ip_rbracket->match(text, ipv6_host->interval.end, end, flags))
3808 {
3809 // Host is IPv6
3810 interval.end = m_ip_rbracket->interval.end;
3811 ipv4_host->invalidate();
3812 dns_host->invalidate();
3813 }
3814 else if (dns_host->match(text, m_at->interval.end, end, flags)) {
3815 // Host is hostname
3816 interval.end = dns_host->interval.end;
3817 ipv4_host->invalidate();
3818 ipv6_host->invalidate();
3819 }
3820 else
3821 goto error;
3822 interval.start = start;
3823 return true;
3824 }
3825
3826 error:
3827 username->invalidate();
3828 ipv4_host->invalidate();
3829 ipv6_host->invalidate();
3830 dns_host->invalidate();
3831 interval.start = (interval.end = start) + 1;
3832 return false;
3833 }
3834
3835 virtual void invalidate()
3836 {
3837 username->invalidate();
3838 ipv4_host->invalidate();
3839 ipv6_host->invalidate();
3840 dns_host->invalidate();
3842 }
3843
3844 public:
3845 std::shared_ptr<basic_tester<T>> username;
3846 std::shared_ptr<basic_tester<T>> ipv4_host;
3847 std::shared_ptr<basic_tester<T>> ipv6_host;
3848 std::shared_ptr<basic_tester<T>> dns_host;
3849
3850 protected:
3851 std::shared_ptr<basic_tester<T>> m_at;
3852 std::shared_ptr<basic_tester<T>> m_ip_lbracket;
3853 std::shared_ptr<basic_tester<T>> m_ip_rbracket;
3854 };
3855
3858#ifdef _UNICODE
3860#else
3862#endif
3864
3868 template <class T>
3870 {
3871 public:
3873 _In_ const std::shared_ptr<basic_tester<T>>& _emoticon,
3874 _In_ const std::shared_ptr<basic_tester<T>>& _apex,
3875 _In_ const std::shared_ptr<basic_tester<T>>& _eyes,
3876 _In_ const std::shared_ptr<basic_tester<T>>& _nose,
3877 _In_ const std::shared_ptr<basic_set<T>>& _mouth,
3878 _In_ const std::locale& locale = std::locale()) :
3879 basic_tester<T>(locale),
3880 emoticon(_emoticon),
3881 apex(_apex),
3882 eyes(_eyes),
3883 nose(_nose),
3884 mouth(_mouth)
3885 {}
3886
3887 virtual bool match(
3888 _In_reads_or_z_(end) const T* text,
3889 _In_ size_t start = 0,
3890 _In_ size_t end = (size_t)-1,
3891 _In_ int flags = match_default)
3892 {
3893 assert(text || start >= end);
3894
3895 if (emoticon && emoticon->match(text, start, end, flags)) {
3896 if (apex) apex->invalidate();
3897 eyes->invalidate();
3898 if (nose) nose->invalidate();
3899 mouth->invalidate();
3900 interval.start = start;
3902 return true;
3903 }
3904
3905 interval.end = start;
3906
3907 if (apex && apex->match(text, interval.end, end, flags))
3908 interval.end = apex->interval.end;
3909
3910 if (eyes->match(text, interval.end, end, flags)) {
3911 if (nose && nose->match(text, eyes->interval.end, end, flags) &&
3912 mouth->match(text, nose->interval.end, end, flags))
3913 {
3914 size_t
3915 start_mouth = mouth->interval.start,
3916 hit_offset = mouth->hit_offset;
3917 // Mouth may repeat :-)))))))
3918 for (interval.end = mouth->interval.end; mouth->match(text, interval.end, end, flags) && mouth->hit_offset == hit_offset; interval.end = mouth->interval.end);
3919 mouth->interval.start = start_mouth;
3920 mouth->interval.end = interval.end;
3921 interval.start = start;
3922 return true;
3923 }
3924 if (mouth->match(text, eyes->interval.end, end, flags)) {
3925 size_t
3926 start_mouth = mouth->interval.start,
3927 hit_offset = mouth->hit_offset;
3928 // Mouth may repeat :-)))))))
3929 for (interval.end = mouth->interval.end; mouth->match(text, interval.end, end, flags) && mouth->hit_offset == hit_offset; interval.end = mouth->interval.end);
3930 if (nose) nose->invalidate();
3931 mouth->interval.start = start_mouth;
3932 mouth->interval.end = interval.end;
3933 interval.start = start;
3934 return true;
3935 }
3936 }
3937
3938 if (emoticon) emoticon->invalidate();
3939 if (apex) apex->invalidate();
3940 eyes->invalidate();
3941 if (nose) nose->invalidate();
3942 mouth->invalidate();
3943 interval.start = (interval.end = start) + 1;
3944 return false;
3945 }
3946
3947 virtual void invalidate()
3948 {
3949 if (emoticon) emoticon->invalidate();
3950 if (apex) apex->invalidate();
3951 eyes->invalidate();
3952 if (nose) nose->invalidate();
3953 mouth->invalidate();
3955 }
3956
3957 public:
3958 std::shared_ptr<basic_tester<T>> emoticon;
3959 std::shared_ptr<basic_tester<T>> apex;
3960 std::shared_ptr<basic_tester<T>> eyes;
3961 std::shared_ptr<basic_tester<T>> nose;
3962 std::shared_ptr<basic_set<T>> mouth;
3963 };
3964
3967#ifdef _UNICODE
3968 using temoticon = wemoticon;
3969#else
3970 using temoticon = emoticon;
3971#endif
3973
3977 template <class T>
3978 class basic_date : public basic_tester<T>
3979 {
3980 public:
3981 enum class format {
3982 dmy = 0x1,
3983 mdy = 0x2,
3984 ymd = 0x4,
3985 ym = 0x8,
3986 my = 0x10,
3987 dm = 0x20,
3988 md = 0x40,
3989 };
3990
3991 basic_date(
3992 _In_ int format_mask,
3993 _In_ const std::shared_ptr<basic_integer<T>>& _day,
3994 _In_ const std::shared_ptr<basic_integer<T>>& _month,
3995 _In_ const std::shared_ptr<basic_integer<T>>& _year,
3996 _In_ const std::shared_ptr<basic_set<T>>& separator,
3997 _In_ const std::shared_ptr<basic_tester<T>>& space,
3998 _In_ const std::locale& locale = std::locale()) :
3999 basic_tester<T>(locale),
4000 format(0),
4001 m_format_mask(format_mask),
4002 day(_day),
4003 month(_month),
4004 year(_year),
4005 m_separator(separator),
4006 m_space(space)
4007 {}
4008
4009 virtual bool match(
4010 _In_reads_or_z_(end) const T* text,
4011 _In_ size_t start = 0,
4012 _In_ size_t end = (size_t)-1,
4013 _In_ int flags = match_default)
4014 {
4015 assert(text || start >= end);
4016
4017 const int space_match_flags = flags & ~match_multiline; // Spaces in dates must never be broken in new line.
4018 if ((m_format_mask & format::dmy) != 0) {
4019 if (day->match(text, start, end, flags)) {
4020 for (interval.end = day->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4021 if (m_separator->match(text, interval.end, end, flags)) {
4022 size_t hit_offset = m_separator->hit_offset;
4023 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4024 if (month->match(text, interval.end, end, flags)) {
4025 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4026 if (m_separator->match(text, interval.end, end, flags) &&
4027 m_separator->hit_offset == hit_offset) // Both separators must match.
4028 {
4029 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4030 if (year->match(text, interval.end, end, flags) &&
4031 is_valid(day->value, month->value))
4032 {
4033 interval.start = start;
4034 interval.end = year->interval.end;
4035 format = format::dmy;
4036 return true;
4037 }
4038 }
4039 }
4040 }
4041 }
4042 }
4043
4044 if ((m_format_mask & format::mdy) != 0) {
4045 if (month->match(text, start, end, flags)) {
4046 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4047 if (m_separator->match(text, interval.end, end, flags)) {
4048 size_t hit_offset = m_separator->hit_offset;
4049 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4050 if (day->match(text, interval.end, end, flags)) {
4051 for (interval.end = day->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4052 if (m_separator->match(text, interval.end, end, flags) &&
4053 m_separator->hit_offset == hit_offset) // Both separators must match.
4054 {
4055 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4056 if (year->match(text, interval.end, end, flags) &&
4057 is_valid(day->value, month->value))
4058 {
4059 interval.start = start;
4060 interval.end = year->interval.end;
4061 format = format::mdy;
4062 return true;
4063 }
4064 }
4065 }
4066 }
4067 }
4068 }
4069
4070 if ((m_format_mask & format::ymd) != 0) {
4071 if (year->match(text, start, end, flags)) {
4072 for (interval.end = year->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4073 if (m_separator->match(text, interval.end, end, flags)) {
4074 size_t hit_offset = m_separator->hit_offset;
4075 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4076 if (month->match(text, interval.end, end, flags)) {
4077 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4078 if (m_separator->match(text, interval.end, end, flags) &&
4079 m_separator->hit_offset == hit_offset) // Both separators must match.
4080 {
4081 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4082 if (day->match(text, interval.end, end, flags) &&
4083 is_valid(day->value, month->value))
4084 {
4085 interval.start = start;
4086 interval.end = day->interval.end;
4087 format = format::ymd;
4088 return true;
4089 }
4090 }
4091 }
4092 }
4093 }
4094 }
4095
4096 if ((m_format_mask & format::ym) != 0) {
4097 if (year->match(text, start, end, flags)) {
4098 for (interval.end = year->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4099 if (m_separator->match(text, interval.end, end, flags)) {
4100 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4101 if (month->match(text, interval.end, end, flags) &&
4102 is_valid((size_t)-1, month->value))
4103 {
4104 if (day) day->invalidate();
4105 interval.start = start;
4106 interval.end = month->interval.end;
4107 format = format::ym;
4108 return true;
4109 }
4110 }
4111 }
4112 }
4113
4114 if ((m_format_mask & format::my) != 0) {
4115 if (month->match(text, start, end, flags)) {
4116 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4117 if (m_separator->match(text, interval.end, end, flags)) {
4118 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4119 if (year->match(text, interval.end, end, flags) &&
4120 is_valid((size_t)-1, month->value))
4121 {
4122 if (day) day->invalidate();
4123 interval.start = start;
4124 interval.end = year->interval.end;
4125 format = format::my;
4126 return true;
4127 }
4128 }
4129 }
4130 }
4131
4132 if ((m_format_mask & format::dm) != 0) {
4133 if (day->match(text, start, end, flags)) {
4134 for (interval.end = day->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4135 if (m_separator->match(text, interval.end, end, flags)) {
4136 size_t hit_offset = m_separator->hit_offset;
4137 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4138 if (month->match(text, interval.end, end, flags) &&
4139 is_valid(day->value, month->value))
4140 {
4141 if (year) year->invalidate();
4142 interval.start = start;
4143 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4144 if (m_separator->match(text, interval.end, end, flags) &&
4145 m_separator->hit_offset == hit_offset) // Both separators must match.
4146 interval.end = m_separator->interval.end;
4147 else
4148 interval.end = month->interval.end;
4149 format = format::dm;
4150 return true;
4151 }
4152 }
4153 }
4154 }
4155
4156 if ((m_format_mask & format::md) != 0) {
4157 if (month->match(text, start, end, flags)) {
4158 for (interval.end = month->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4159 if (m_separator->match(text, interval.end, end, flags)) {
4160 size_t hit_offset = m_separator->hit_offset;
4161 for (interval.end = m_separator->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4162 if (day->match(text, interval.end, end, flags) &&
4163 is_valid(day->value, month->value))
4164 {
4165 if (year) year->invalidate();
4166 interval.start = start;
4167 for (interval.end = day->interval.end; m_space->match(text, interval.end, end, space_match_flags); interval.end = m_space->interval.end);
4168 if (m_separator->match(text, interval.end, end, flags) &&
4169 m_separator->hit_offset == hit_offset) // Both separators must match.
4170 interval.end = m_separator->interval.end;
4171 else
4172 interval.end = day->interval.end;
4173 format = format::md;
4174 return true;
4175 }
4176 }
4177 }
4178 }
4179
4180 if (day) day->invalidate();
4181 if (month) month->invalidate();
4182 if (year) year->invalidate();
4183 format = 0;
4184 interval.start = (interval.end = start) + 1;
4185 return false;
4186 }
4187
4188 virtual void invalidate()
4189 {
4190 if (day) day->invalidate();
4191 if (month) month->invalidate();
4192 if (year) year->invalidate();
4193 format = 0;
4195 }
4196
4197 protected:
4198 static inline bool is_valid(size_t day, size_t month)
4199 {
4200 if (month == (size_t)-1) {
4201 // Default to January. This allows validating day only, as January has all 31 days.
4202 month = 1;
4203 }
4204 if (day == (size_t)-1) {
4205 // Default to 1st day in month. This allows validating month only, as each month has 1st day.
4206 day = 1;
4207 }
4208
4209 switch (month) {
4210 case 1:
4211 case 3:
4212 case 5:
4213 case 7:
4214 case 8:
4215 case 10:
4216 case 12:
4217 return 1 <= day && day <= 31;
4218 case 2:
4219 return 1 <= day && day <= 29;
4220 case 4:
4221 case 6:
4222 case 9:
4223 case 11:
4224 return 1 <= day && day <= 30;
4225 default:
4226 return false;
4227 }
4228 }
4229
4230 public:
4231 format format;
4232 std::shared_ptr<basic_integer<T>> day;
4233 std::shared_ptr<basic_integer<T>> month;
4234 std::shared_ptr<basic_integer<T>> year;
4235
4236 protected:
4237 int m_format_mask;
4238 std::shared_ptr<basic_set<T>> m_separator;
4239 std::shared_ptr<basic_tester<T>> m_space;
4240 };
4241
4242 using date = basic_date<char>;
4243 using wdate = basic_date<wchar_t>;
4244#ifdef _UNICODE
4245 using tdate = wdate;
4246#else
4247 using tdate = date;
4248#endif
4250
4254 template <class T>
4255 class basic_time : public basic_tester<T>
4256 {
4257 public:
4258 basic_time(
4259 _In_ const std::shared_ptr<basic_integer10<T>>& _hour,
4260 _In_ const std::shared_ptr<basic_integer10<T>>& _minute,
4261 _In_ const std::shared_ptr<basic_integer10<T>>& _second,
4262 _In_ const std::shared_ptr<basic_integer10<T>>& _millisecond,
4263 _In_ const std::shared_ptr<basic_set<T>>& separator,
4264 _In_ const std::shared_ptr<basic_tester<T>>& millisecond_separator,
4265 _In_ const std::locale& locale = std::locale()) :
4266 basic_tester<T>(locale),
4267 hour(_hour),
4268 minute(_minute),
4269 second(_second),
4270 millisecond(_millisecond),
4271 m_separator(separator),
4272 m_millisecond_separator(millisecond_separator)
4273 {}
4274
4275 virtual bool match(
4276 _In_reads_or_z_(end) const T* text,
4277 _In_ size_t start = 0,
4278 _In_ size_t end = (size_t)-1,
4279 _In_ int flags = match_default)
4280 {
4281 assert(text || start >= end);
4282
4283 if (hour->match(text, start, end, flags) &&
4284 m_separator->match(text, hour->interval.end, end, flags) &&
4285 minute->match(text, m_separator->interval.end, end, flags) &&
4286 minute->value < 60)
4287 {
4288 // hh::mm
4289 size_t hit_offset = m_separator->hit_offset;
4290 if (m_separator->match(text, minute->interval.end, end, flags) &&
4291 m_separator->hit_offset == hit_offset && // Both separators must match.
4292 second && second->match(text, m_separator->interval.end, end, flags) &&
4293 second->value < 60)
4294 {
4295 // hh::mm:ss
4296 if (m_millisecond_separator && m_millisecond_separator->match(text, second->interval.end, end, flags) &&
4297 millisecond && millisecond->match(text, m_millisecond_separator->interval.end, end, flags) &&
4298 millisecond->value < 1000)
4299 {
4300 // hh::mm:ss.mmmm
4301 interval.end = millisecond->interval.end;
4302 }
4303 else {
4304 if (millisecond) millisecond->invalidate();
4305 interval.end = second->interval.end;
4306 }
4307 }
4308 else {
4309 if (second) second->invalidate();
4310 if (millisecond) millisecond->invalidate();
4311 interval.end = minute->interval.end;
4312 }
4313 interval.start = start;
4314 return true;
4315 }
4316
4317 hour->invalidate();
4318 minute->invalidate();
4319 if (second) second->invalidate();
4320 if (millisecond) millisecond->invalidate();
4321 interval.start = (interval.end = start) + 1;
4322 return false;
4323 }
4324
4325 virtual void invalidate()
4326 {
4327 hour->invalidate();
4328 minute->invalidate();
4329 if (second) second->invalidate();
4330 if (millisecond) millisecond->invalidate();
4332 }
4333
4334 public:
4335 std::shared_ptr<basic_integer10<T>> hour;
4336 std::shared_ptr<basic_integer10<T>> minute;
4337 std::shared_ptr<basic_integer10<T>> second;
4338 std::shared_ptr<basic_integer10<T>> millisecond;
4339
4340 protected:
4341 std::shared_ptr<basic_set<T>> m_separator;
4342 std::shared_ptr<basic_tester<T>> m_millisecond_separator;
4343 };
4344
4345 using time = basic_time<char>;
4346 using wtime = basic_time<wchar_t>;
4347#ifdef _UNICODE
4348 using ttime = wtime;
4349#else
4350 using ttime = time;
4351#endif
4353
4357 template <class T>
4358 class basic_angle : public basic_tester<T>
4359 {
4360 public:
4362 _In_ const std::shared_ptr<basic_integer10<T>>& _degree,
4363 _In_ const std::shared_ptr<basic_tester<T>>& _degree_separator,
4364 _In_ const std::shared_ptr<basic_integer10<T>>& _minute,
4365 _In_ const std::shared_ptr<basic_tester<T>>& _minute_separator,
4366 _In_ const std::shared_ptr<basic_integer10<T>>& _second,
4367 _In_ const std::shared_ptr<basic_tester<T>>& _second_separator,
4368 _In_ const std::shared_ptr<basic_tester<T>>& _decimal,
4369 _In_ const std::locale& locale = std::locale()) :
4370 basic_tester<T>(locale),
4371 degree(_degree),
4372 degree_separator(_degree_separator),
4373 minute(_minute),
4374 minute_separator(_minute_separator),
4375 second(_second),
4376 second_separator(_second_separator),
4377 decimal(_decimal)
4378 {}
4379
4380 virtual bool match(
4381 _In_reads_or_z_(end) const T* text,
4382 _In_ size_t start = 0,
4383 _In_ size_t end = (size_t)-1,
4384 _In_ int flags = match_default)
4385 {
4386 assert(text || start >= end);
4387
4388 interval.end = start;
4389
4390 if (degree->match(text, interval.end, end, flags) &&
4391 degree_separator->match(text, degree->interval.end, end, flags))
4392 {
4393 // Degrees
4394 interval.end = degree_separator->interval.end;
4395 }
4396 else {
4397 degree->invalidate();
4398 degree_separator->invalidate();
4399 }
4400
4401 if (minute->match(text, interval.end, end, flags) &&
4402 minute->value < 60 &&
4403 minute_separator->match(text, minute->interval.end, end, flags))
4404 {
4405 // Minutes
4406 interval.end = minute_separator->interval.end;
4407 }
4408 else {
4409 minute->invalidate();
4410 minute_separator->invalidate();
4411 }
4412
4413 if (second && second->match(text, interval.end, end, flags) &&
4414 second->value < 60)
4415 {
4416 // Seconds
4417 interval.end = second->interval.end;
4418 if (second_separator && second_separator->match(text, interval.end, end, flags))
4419 interval.end = second_separator->interval.end;
4420 else
4421 if (second_separator) second_separator->invalidate();
4422 }
4423 else {
4424 if (second) second->invalidate();
4425 if (second_separator) second_separator->invalidate();
4426 }
4427
4428 if (degree->interval.start < degree->interval.end ||
4429 minute->interval.start < minute->interval.end ||
4430 second && second->interval.start < second->interval.end)
4431 {
4432 if (decimal && decimal->match(text, interval.end, end, flags)) {
4433 // Decimals
4434 interval.end = decimal->interval.end;
4435 }
4436 else if (decimal)
4437 decimal->invalidate();
4438 interval.start = start;
4439 return true;
4440 }
4441 if (decimal) decimal->invalidate();
4442 interval.start = (interval.end = start) + 1;
4443 return false;
4444 }
4445
4446 virtual void invalidate()
4447 {
4448 degree->invalidate();
4449 degree_separator->invalidate();
4450 minute->invalidate();
4451 minute_separator->invalidate();
4452 if (second) second->invalidate();
4453 if (second_separator) second_separator->invalidate();
4454 if (decimal) decimal->invalidate();
4456 }
4457
4458 public:
4459 std::shared_ptr<basic_integer10<T>> degree;
4460 std::shared_ptr<basic_tester<T>> degree_separator;
4461 std::shared_ptr<basic_integer10<T>> minute;
4462 std::shared_ptr<basic_tester<T>> minute_separator;
4463 std::shared_ptr<basic_integer10<T>> second;
4464 std::shared_ptr<basic_tester<T>> second_separator;
4465 std::shared_ptr<basic_tester<T>> decimal;
4466 };
4467
4468 using angle = basic_angle<char>;
4470#ifdef _UNICODE
4471 using RRegElKot = wangle;
4472#else
4473 using RRegElKot = angle;
4474#endif
4476
4480 template <class T>
4482 {
4483 public:
4485 _In_ const std::shared_ptr<basic_tester<T>>& digit,
4486 _In_ const std::shared_ptr<basic_tester<T>>& plus_sign,
4487 _In_ const std::shared_ptr<basic_set<T>>& lparenthesis,
4488 _In_ const std::shared_ptr<basic_set<T>>& rparenthesis,
4489 _In_ const std::shared_ptr<basic_tester<T>>& separator,
4490 _In_ const std::shared_ptr<basic_tester<T>>& space,
4491 _In_ const std::locale& locale = std::locale()) :
4492 basic_tester<T>(locale),
4493 m_digit(digit),
4494 m_plus_sign(plus_sign),
4495 m_lparenthesis(lparenthesis),
4496 m_rparenthesis(rparenthesis),
4497 m_separator(separator),
4498 m_space(space)
4499 {}
4500
4501 virtual bool match(
4502 _In_reads_or_z_(end) const T* text,
4503 _In_ size_t start = 0,
4504 _In_ size_t end = (size_t)-1,
4505 _In_ int flags = match_default)
4506 {
4507 assert(text || start >= end);
4508
4509 size_t safe_digit_end = start, safe_value_size = 0;
4510 bool has_digits = false, after_digit = false, in_parentheses = false, after_parentheses = false;
4511 const int space_match_flags = flags & ~match_multiline; // Spaces in phone numbers must never be broken in new line.
4512
4513 interval.end = start;
4514 value.clear();
4515 m_lparenthesis->invalidate();
4516 m_rparenthesis->invalidate();
4517
4518 if (m_plus_sign && m_plus_sign->match(text, interval.end, end, flags)) {
4519 value.append(text + m_plus_sign->interval.start, text + m_plus_sign->interval.end);
4520 safe_value_size = value.size();
4521 interval.end = m_plus_sign->interval.end;
4522 }
4523
4524 for (;;) {
4525 assert(text || interval.end >= end);
4526 if (interval.end >= end || !text[interval.end])
4527 break;
4528 if (m_digit->match(text, interval.end, end, flags)) {
4529 // Digit
4530 value.append(text + m_digit->interval.start, text + m_digit->interval.end);
4531 interval.end = m_digit->interval.end;
4532 if (!in_parentheses) {
4533 safe_digit_end = interval.end;
4534 safe_value_size = value.size();
4535 has_digits = true;
4536 }
4537 after_digit = true;
4538 after_parentheses = false;
4539 }
4540 else if (
4541 m_lparenthesis && !m_lparenthesis->interval && // No left parenthesis yet
4542 m_rparenthesis && !m_rparenthesis->interval && // Right parenthesis after left
4543 m_lparenthesis->match(text, interval.end, end, flags))
4544 {
4545 // Left parenthesis
4546 value.Prilepi(text + m_lparenthesis->interval.start, m_lparenthesis->interval.size());
4547 interval.end = m_lparenthesis->interval.end;
4548 in_parentheses = true;
4549 after_digit = false;
4550 after_parentheses = false;
4551 }
4552 else if (
4553 in_parentheses && // After left parenthesis
4554 m_rparenthesis && !m_rparenthesis->interval && // No right parenthesis yet
4555 m_rparenthesis->match(text, interval.end, end, flags) &&
4556 m_lparenthesis->hit_offset == m_rparenthesis->hit_offset) // Left and right parentheses must match
4557 {
4558 // Right parenthesis
4559 value.append(text + m_rparenthesis->interval.start, text + m_rparenthesis->interval.end);
4560 interval.end = m_rparenthesis->interval.end;
4561 safe_digit_end = interval.end;
4562 safe_value_size = value.size();
4563 in_parentheses = false;
4564 after_digit = false;
4565 after_parentheses = true;
4566 }
4567 else if (
4568 after_digit &&
4569 !in_parentheses && // No separators inside parentheses
4570 !after_parentheses && // No separators following right parenthesis
4571 m_separator && m_separator->match(text, interval.end, end, flags))
4572 {
4573 // Separator
4574 interval.end = m_separator->interval.end;
4575 after_digit = false;
4576 after_parentheses = false;
4577 }
4578 else if (
4579 (after_digit || after_parentheses) &&
4580 m_space && m_space->match(text, interval.end, end, space_match_flags))
4581 {
4582 // Space
4583 interval.end = m_space->interval.end;
4584 after_digit = false;
4585 after_parentheses = false;
4586 }
4587 else
4588 break;
4589 }
4590 if (has_digits) {
4591 value.erase(safe_value_size);
4592 interval.start = start;
4593 interval.end = safe_digit_end;
4594 return true;
4595 }
4596 value.clear();
4597 interval.start = (interval.end = start) + 1;
4598 return false;
4599 }
4600
4601 virtual void invalidate()
4602 {
4603 value.clear();
4605 }
4606
4607 public:
4608 std::basic_string<T> value;
4609
4610 protected:
4611 std::shared_ptr<basic_tester<T>> m_digit;
4612 std::shared_ptr<basic_tester<T>> m_plus_sign;
4613 std::shared_ptr<basic_set<T>> m_lparenthesis;
4614 std::shared_ptr<basic_set<T>> m_rparenthesis;
4615 std::shared_ptr<basic_tester<T>> m_separator;
4616 std::shared_ptr<basic_tester<T>> m_space;
4617 };
4618
4621#ifdef _UNICODE
4623#else
4625#endif
4627
4631 template <class T>
4633 {
4634 public:
4636 _In_ const std::shared_ptr<basic_tester<T>>& element,
4637 _In_ const std::shared_ptr<basic_tester<T>>& digit,
4638 _In_ const std::shared_ptr<basic_tester<T>>& sign,
4639 _In_ const std::locale& locale = std::locale()) :
4640 basic_tester<T>(locale),
4641 m_element(element),
4642 m_digit(digit),
4643 m_sign(sign),
4644 has_digits(false),
4645 has_charge(false)
4646 {}
4647
4648 virtual bool match(
4649 _In_reads_or_z_(end) const T* text,
4650 _In_ size_t start = 0,
4651 _In_ size_t end = (size_t)-1,
4652 _In_ int flags = match_default)
4653 {
4654 assert(text || start >= end);
4655
4656 has_digits = false;
4657 has_charge = false;
4658 interval.end = start;
4659
4660 const int element_match_flags = flags & ~match_case_insensitive; // Chemical elements are always case-sensitive.
4661 for (;;) {
4662 if (m_element->match(text, interval.end, end, element_match_flags)) {
4663 interval.end = m_element->interval.end;
4664 while (m_digit->match(text, interval.end, end, flags)) {
4665 interval.end = m_digit->interval.end;
4666 has_digits = true;
4667 }
4668 }
4669 else if (start < interval.end) {
4670 if (m_sign->match(text, interval.end, end, flags)) {
4671 interval.end = m_sign->interval.end;
4672 has_charge = true;
4673 }
4674 interval.start = start;
4675 return true;
4676 }
4677 else {
4678 interval.start = (interval.end = start) + 1;
4679 return false;
4680 }
4681 }
4682 }
4683
4684 virtual void invalidate()
4685 {
4686 has_digits = false;
4687 has_charge = false;
4689 }
4690
4691 public:
4692 bool has_digits;
4693 bool has_charge;
4694
4695 protected:
4696 std::shared_ptr<basic_tester<T>> m_element;
4697 std::shared_ptr<basic_tester<T>> m_digit;
4698 std::shared_ptr<basic_tester<T>> m_sign;
4699 };
4700
4703#ifdef _UNICODE
4705#else
4707#endif
4709
4714 {
4715 public:
4716 virtual bool match(
4717 _In_reads_or_z_(end) const char* text,
4718 _In_ size_t start = 0,
4719 _In_ size_t end = (size_t)-1,
4720 _In_ int flags = match_default)
4721 {
4722 assert(text || start >= end);
4723 interval.end = start;
4724
4725 assert(text || interval.end >= end);
4726 if (interval.end < end && text[interval.end]) {
4727 if (text[interval.end] == '\r') {
4728 interval.end++;
4729 if (interval.end < end && text[interval.end] == '\n') {
4730 interval.start = start;
4731 interval.end++;
4732 return true;
4733 }
4734 }
4735 else if (text[interval.end] == '\n') {
4736 interval.start = start;
4737 interval.end++;
4738 return true;
4739 }
4740 }
4741 interval.start = (interval.end = start) + 1;
4742 return false;
4743 }
4744 };
4745
4749 class http_space : public tester
4750 {
4751 public:
4752 virtual bool match(
4753 _In_reads_or_z_(end) const char* text,
4754 _In_ size_t start = 0,
4755 _In_ size_t end = (size_t)-1,
4756 _In_ int flags = match_default)
4757 {
4758 assert(text || start >= end);
4759 interval.end = start;
4760 if (m_line_break.match(text, interval.end, end, flags)) {
4761 interval.end = m_line_break.interval.end;
4762 if (interval.end < end && text[interval.end] && isspace(text[interval.end])) {
4763 interval.start = start;
4764 interval.end++;
4765 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
4766 return true;
4767 }
4768 }
4769 else if (interval.end < end && text[interval.end] && isspace(text[interval.end])) {
4770 interval.start = start;
4771 interval.end++;
4772 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
4773 return true;
4774 }
4775 interval.start = (interval.end = start) + 1;
4776 return false;
4777 }
4778
4779 protected:
4780 http_line_break m_line_break;
4781 };
4782
4786 class http_text_char : public tester
4787 {
4788 public:
4789 virtual bool match(
4790 _In_reads_or_z_(end) const char* text,
4791 _In_ size_t start = 0,
4792 _In_ size_t end = (size_t)-1,
4793 _In_ int flags = match_default)
4794 {
4795 assert(text || start >= end);
4796 interval.end = start;
4797
4798 assert(text || interval.end >= end);
4799 if (m_space.match(text, interval.end, end, flags)) {
4800 interval.start = start;
4801 interval.end = m_space.interval.end;
4802 return true;
4803 }
4804 else if (interval.end < end && text[interval.end] && text[interval.end] >= 0x20) {
4805 interval.start = start;
4806 interval.end++;
4807 return true;
4808 }
4809 interval.start = (interval.end = start) + 1;
4810 return false;
4811 }
4812
4813 protected:
4814 http_space m_space;
4815 };
4816
4820 class http_token : public tester
4821 {
4822 public:
4823 virtual bool match(
4824 _In_reads_or_z_(end) const char* text,
4825 _In_ size_t start = 0,
4826 _In_ size_t end = (size_t)-1,
4827 _In_ int flags = match_default)
4828 {
4829 assert(text || start >= end);
4830 interval.end = start;
4831 for (;;) {
4832 if (interval.end < end && text[interval.end]) {
4833 if ((unsigned int)text[interval.end] < 0x20 ||
4834 (unsigned int)text[interval.end] == 0x7f ||
4835 text[interval.end] == '(' ||
4836 text[interval.end] == ')' ||
4837 text[interval.end] == '<' ||
4838 text[interval.end] == '>' ||
4839 text[interval.end] == '@' ||
4840 text[interval.end] == ',' ||
4841 text[interval.end] == ';' ||
4842 text[interval.end] == ':' ||
4843 text[interval.end] == '\\' ||
4844 text[interval.end] == '\"' ||
4845 text[interval.end] == '/' ||
4846 text[interval.end] == '[' ||
4847 text[interval.end] == ']' ||
4848 text[interval.end] == '?' ||
4849 text[interval.end] == '=' ||
4850 text[interval.end] == '{' ||
4851 text[interval.end] == '}' ||
4852 isspace(text[interval.end]))
4853 break;
4854 else
4855 interval.end++;
4856 }
4857 else
4858 break;
4859 }
4860 if (start < interval.end) {
4861 interval.start = start;
4862 return true;
4863 }
4864 else {
4865 interval.start = (interval.end = start) + 1;
4866 return false;
4867 }
4868 }
4869 };
4870
4875 {
4876 public:
4877 virtual bool match(
4878 _In_reads_or_z_(end) const char* text,
4879 _In_ size_t start = 0,
4880 _In_ size_t end = (size_t)-1,
4881 _In_ int flags = match_default)
4882 {
4883 assert(text || start >= end);
4884 interval.end = start;
4885 if (interval.end < end && text[interval.end] != '"')
4886 goto error;
4887 interval.end++;
4888 content.start = interval.end;
4889 for (;;) {
4890 assert(text || interval.end >= end);
4891 if (interval.end < end && text[interval.end]) {
4892 if (text[interval.end] == '"') {
4893 content.end = interval.end;
4894 interval.end++;
4895 break;
4896 }
4897 else if (text[interval.end] == '\\') {
4898 interval.end++;
4899 if (interval.end < end && text[interval.end]) {
4900 interval.end++;
4901 }
4902 else
4903 goto error;
4904 }
4905 else if (m_chr.match(text, interval.end, end, flags))
4906 interval.end++;
4907 else
4908 goto error;
4909 }
4910 else
4911 goto error;
4912 }
4913 interval.start = start;
4914 return true;
4915
4916 error:
4917 content.start = 1;
4918 content.end = 0;
4919 interval.start = (interval.end = start) + 1;
4920 return false;
4921 }
4922
4923 virtual void invalidate()
4924 {
4925 content.start = 1;
4926 content.end = 0;
4927 tester::invalidate();
4928 }
4929
4930 public:
4932
4933 protected:
4934 http_text_char m_chr;
4935 };
4936
4940 class http_value : public tester
4941 {
4942 public:
4943 virtual bool match(
4944 _In_reads_or_z_(end) const char* text,
4945 _In_ size_t start = 0,
4946 _In_ size_t end = (size_t)-1,
4947 _In_ int flags = match_default)
4948 {
4949 assert(text || start >= end);
4950 interval.end = start;
4951 if (string.match(text, interval.end, end, flags)) {
4952 token.invalidate();
4953 interval.end = string.interval.end;
4954 interval.start = start;
4955 return true;
4956 }
4957 else if (token.match(text, interval.end, end, flags)) {
4958 string.invalidate();
4959 interval.end = token.interval.end;
4960 interval.start = start;
4961 return true;
4962 }
4963 else {
4964 interval.start = (interval.end = start) + 1;
4965 return false;
4966 }
4967 }
4968
4969 virtual void invalidate()
4970 {
4971 string.invalidate();
4972 token.invalidate();
4973 tester::invalidate();
4974 }
4975
4976 public:
4979 };
4980
4984 class http_parameter : public tester
4985 {
4986 public:
4987 virtual bool match(
4988 _In_reads_or_z_(end) const char* text,
4989 _In_ size_t start = 0,
4990 _In_ size_t end = (size_t)-1,
4991 _In_ int flags = match_default)
4992 {
4993 assert(text || start >= end);
4994 interval.end = start;
4995 if (name.match(text, interval.end, end, flags))
4996 interval.end = name.interval.end;
4997 else
4998 goto error;
4999 while (m_space.match(text, interval.end, end, flags))
5000 interval.end = m_space.interval.end;
5001 assert(text || interval.end >= end);
5002 if (interval.end < end && text[interval.end] == '=')
5003 interval.end++;
5004 else
5005 while (m_space.match(text, interval.end, end, flags))
5006 interval.end = m_space.interval.end;
5007 if (value.match(text, interval.end, end, flags))
5008 interval.end = value.interval.end;
5009 else
5010 goto error;
5011 interval.start = start;
5012 return true;
5013
5014 error:
5015 name.invalidate();
5016 value.invalidate();
5017 interval.start = (interval.end = start) + 1;
5018 return false;
5019 }
5020
5021 virtual void invalidate()
5022 {
5023 name.invalidate();
5024 value.invalidate();
5025 tester::invalidate();
5026 }
5027
5028 public:
5031
5032 protected:
5033 http_space m_space;
5034 };
5035
5039 class http_any_type : public tester
5040 {
5041 public:
5042 virtual bool match(
5043 _In_reads_or_z_(end) const char* text,
5044 _In_ size_t start = 0,
5045 _In_ size_t end = (size_t)-1,
5046 _In_ int flags = match_default)
5047 {
5048 assert(text || start >= end);
5049 if (start + 2 < end &&
5050 text[start] == '*' &&
5051 text[start + 1] == '/' &&
5052 text[start + 2] == '*')
5053 {
5054 interval.end = (interval.start = start) + 3;
5055 return true;
5056 }
5057 else if (start < end && text[start] == '*') {
5058 interval.end = (interval.start = start) + 1;
5059 return true;
5060 }
5061 else {
5062 interval.start = (interval.end = start) + 1;
5063 return false;
5064 }
5065 }
5066 };
5067
5072 {
5073 public:
5074 virtual bool match(
5075 _In_reads_or_z_(end) const char* text,
5076 _In_ size_t start = 0,
5077 _In_ size_t end = (size_t)-1,
5078 _In_ int flags = match_default)
5079 {
5080 assert(text || start >= end);
5081 interval.end = start;
5082 if (type.match(text, interval.end, end, flags))
5083 interval.end = type.interval.end;
5084 else
5085 goto error;
5086 while (m_space.match(text, interval.end, end, flags))
5087 interval.end = m_space.interval.end;
5088 if (interval.end < end && text[interval.end] == '/')
5089 interval.end++;
5090 else
5091 goto error;
5092 while (m_space.match(text, interval.end, end, flags))
5093 interval.end = m_space.interval.end;
5094 if (subtype.match(text, interval.end, end, flags))
5095 interval.end = subtype.interval.end;
5096 else
5097 goto error;
5098 interval.start = start;
5099 return true;
5100
5101 error:
5102 type.invalidate();
5103 subtype.invalidate();
5104 interval.start = (interval.end = start) + 1;
5105 return false;
5106 }
5107
5108 virtual void invalidate()
5109 {
5110 type.invalidate();
5111 subtype.invalidate();
5112 tester::invalidate();
5113 }
5114
5115 public:
5116 http_token type;
5117 http_token subtype;
5118
5119 protected:
5120 http_space m_space;
5121 };
5122
5127 {
5128 public:
5129 virtual bool match(
5130 _In_reads_or_z_(end) const char* text,
5131 _In_ size_t start = 0,
5132 _In_ size_t end = (size_t)-1,
5133 _In_ int flags = match_default)
5134 {
5135 assert(text || start >= end);
5136 if (!http_media_range::match(text, start, end, flags))
5137 goto error;
5138 params.clear();
5139 for (;;) {
5140 if (interval.end < end && text[interval.end]) {
5141 if (m_space.match(text, interval.end, end, flags))
5142 interval.end = m_space.interval.end;
5143 else if (text[interval.end] == ';') {
5144 interval.end++;
5145 while (m_space.match(text, interval.end, end, flags))
5146 interval.end = m_space.interval.end;
5147 http_parameter param;
5148 if (param.match(text, interval.end, end, flags)) {
5149 interval.end = param.interval.end;
5150 params.push_back(std::move(param));
5151 }
5152 else
5153 break;
5154 }
5155 else
5156 break;
5157 }
5158 else
5159 break;
5160 }
5161 interval.end = params.empty() ? subtype.interval.end : params.back().interval.end;
5162 return true;
5163
5164 error:
5165 http_media_range::invalidate();
5166 params.clear();
5167 interval.start = (interval.end = start) + 1;
5168 return false;
5169 }
5170
5171 virtual void invalidate()
5172 {
5173 params.clear();
5174 http_media_range::invalidate();
5175 }
5176
5177 public:
5178 std::list<http_parameter> params;
5179 };
5180
5185 {
5186 public:
5187 virtual bool match(
5188 _In_reads_or_z_(end) const char* text,
5189 _In_ size_t start = 0,
5190 _In_ size_t end = (size_t)-1,
5191 _In_ int flags = match_default)
5192 {
5193 assert(text || start >= end);
5194 interval.end = start;
5195 for (;;) {
5196 if (interval.end < end && text[interval.end]) {
5197 if ((unsigned int)text[interval.end] < 0x20 ||
5198 (unsigned int)text[interval.end] == 0x7f ||
5199 text[interval.end] == ':' ||
5200 text[interval.end] == '/' ||
5201 isspace(text[interval.end]))
5202 break;
5203 else
5204 interval.end++;
5205 }
5206 else
5207 break;
5208 }
5209 if (start < interval.end) {
5210 interval.start = start;
5211 return true;
5212 }
5213 interval.start = (interval.end = start) + 1;
5214 return false;
5215 }
5216 };
5217
5221 class http_url_port : public tester
5222 {
5223 public:
5224 http_url_port(_In_ const std::locale& locale = std::locale()) :
5225 tester(locale),
5226 value(0)
5227 {}
5228
5229 virtual bool match(
5230 _In_reads_or_z_(end) const char* text,
5231 _In_ size_t start = 0,
5232 _In_ size_t end = (size_t)-1,
5233 _In_ int flags = match_default)
5234 {
5235 assert(text || start >= end);
5236 value = 0;
5237 interval.end = start;
5238 for (;;) {
5239 if (interval.end < end && text[interval.end]) {
5240 if ('0' <= text[interval.end] && text[interval.end] <= '9') {
5241 size_t _value = (size_t)value * 10 + text[interval.end] - '0';
5242 if (_value > (uint16_t)-1) {
5243 value = 0;
5244 interval.start = (interval.end = start) + 1;
5245 return false;
5246 }
5247 value = (uint16_t)_value;
5248 interval.end++;
5249 }
5250 else
5251 break;
5252 }
5253 else
5254 break;
5255 }
5256 if (start < interval.end) {
5257 interval.start = start;
5258 return true;
5259 }
5260 interval.start = (interval.end = start) + 1;
5261 return false;
5262 }
5263
5264 virtual void invalidate()
5265 {
5266 value = 0;
5267 tester::invalidate();
5268 }
5269
5270 public:
5271 uint16_t value;
5272 };
5273
5278 {
5279 public:
5280 virtual bool match(
5281 _In_reads_or_z_(end) const char* text,
5282 _In_ size_t start = 0,
5283 _In_ size_t end = (size_t)-1,
5284 _In_ int flags = match_default)
5285 {
5286 assert(text || start >= end);
5287 interval.end = start;
5288 for (;;) {
5289 if (interval.end < end && text[interval.end]) {
5290 if ((unsigned int)text[interval.end] < 0x20 ||
5291 (unsigned int)text[interval.end] == 0x7f ||
5292 text[interval.end] == '?' ||
5293 text[interval.end] == '/' ||
5294 isspace(text[interval.end]))
5295 break;
5296 else
5297 interval.end++;
5298 }
5299 else
5300 break;
5301 }
5302 interval.start = start;
5303 return true;
5304 }
5305 };
5306
5310 class http_url_path : public tester
5311 {
5312 public:
5313 virtual bool match(
5314 _In_reads_or_z_(end) const char* text,
5315 _In_ size_t start = 0,
5316 _In_ size_t end = (size_t)-1,
5317 _In_ int flags = match_default)
5318 {
5319 assert(text || start >= end);
5321 interval.end = start;
5322 segments.clear();
5323 assert(text || interval.end >= end);
5324 if (interval.end < end && text[interval.end] != '/')
5325 goto error;
5326 interval.end++;
5327 s.match(text, interval.end, end, flags);
5328 segments.push_back(s);
5330 for (;;) {
5331 if (interval.end < end && text[interval.end]) {
5332 if (text[interval.end] == '/') {
5333 interval.end++;
5334 s.match(text, interval.end, end, flags);
5335 segments.push_back(s);
5337 }
5338 else
5339 break;
5340 }
5341 else
5342 break;
5343 }
5344 interval.start = start;
5345 return true;
5346
5347 error:
5348 segments.clear();
5349 interval.start = (interval.end = start) + 1;
5350 return false;
5351 }
5352
5353 virtual void invalidate()
5354 {
5355 segments.clear();
5356 tester::invalidate();
5357 }
5358
5359 public:
5360 std::vector<http_url_path_segment> segments;
5361 };
5362
5367 {
5368 public:
5369 virtual bool match(
5370 _In_reads_or_z_(end) const char* text,
5371 _In_ size_t start = 0,
5372 _In_ size_t end = (size_t)-1,
5373 _In_ int flags = match_default)
5374 {
5375 assert(text || start >= end);
5376 interval.end = start;
5377 name.start = interval.end;
5378 for (;;) {
5379 if (interval.end < end && text[interval.end]) {
5380 if ((unsigned int)text[interval.end] < 0x20 ||
5381 (unsigned int)text[interval.end] == 0x7f ||
5382 text[interval.end] == '&' ||
5383 text[interval.end] == '=' ||
5384 isspace(text[interval.end]))
5385 break;
5386 else
5387 interval.end++;
5388 }
5389 else
5390 break;
5391 }
5392 if (start < interval.end)
5393 name.end = interval.end;
5394 else
5395 goto error;
5396 if (text[interval.end] == '=') {
5397 interval.end++;
5398 value.start = interval.end;
5399 for (;;) {
5400 if (interval.end < end && text[interval.end]) {
5401 if ((unsigned int)text[interval.end] < 0x20 ||
5402 (unsigned int)text[interval.end] == 0x7f ||
5403 text[interval.end] == '&' ||
5404 isspace(text[interval.end]))
5405 break;
5406 else
5407 interval.end++;
5408 }
5409 else
5410 break;
5411 }
5412 value.end = interval.end;
5413 }
5414 else {
5415 value.start = 1;
5416 value.end = 0;
5417 }
5418 interval.start = start;
5419 return true;
5420
5421 error:
5422 name.start = 1;
5423 name.end = 0;
5424 value.start = 1;
5425 value.end = 0;
5426 interval.start = (interval.end = start) + 1;
5427 return false;
5428 }
5429
5430 virtual void invalidate()
5431 {
5432 name.start = 1;
5433 name.end = 0;
5434 value.start = 1;
5435 value.end = 0;
5436 tester::invalidate();
5437 }
5438
5439 public:
5442 };
5443
5447 class http_url : public tester
5448 {
5449 public:
5450 virtual bool match(
5451 _In_reads_or_z_(end) const char* text,
5452 _In_ size_t start = 0,
5453 _In_ size_t end = (size_t)-1,
5454 _In_ int flags = match_default)
5455 {
5456 assert(text || start >= end);
5457 interval.end = start;
5458
5459 if (interval.end + 7 <= end && stdex::strnicmp(text + interval.end, 7, "http://", (size_t)-1, m_locale) == 0) {
5460 interval.end += 7;
5461 if (server.match(text, interval.end, end, flags))
5462 interval.end = server.interval.end;
5463 else
5464 goto error;
5465 if (interval.end < end && text[interval.end] == ':') {
5466 interval.end++;
5467 if (port.match(text, interval.end, end, flags))
5468 interval.end = port.interval.end;
5469 }
5470 else {
5471 port.invalidate();
5472 port.value = 80;
5473 }
5474 }
5475 else {
5476 server.invalidate();
5477 port.invalidate();
5478 port.value = 80;
5479 }
5480
5481 if (path.match(text, interval.end, end, flags))
5482 interval.end = path.interval.end;
5483 else
5484 goto error;
5485
5486 params.clear();
5487
5488 if (interval.end < end && text[interval.end] == '?') {
5489 interval.end++;
5490 for (;;) {
5491 if (interval.end < end && text[interval.end]) {
5492 if ((unsigned int)text[interval.end] < 0x20 ||
5493 (unsigned int)text[interval.end] == 0x7f ||
5494 isspace(text[interval.end]))
5495 break;
5496 else if (text[interval.end] == '&')
5497 interval.end++;
5498 else {
5499 http_url_parameter param;
5500 if (param.match(text, interval.end, end, flags)) {
5501 interval.end = param.interval.end;
5502 params.push_back(std::move(param));
5503 }
5504 else
5505 break;
5506 }
5507 }
5508 else
5509 break;
5510 }
5511 }
5512
5513 interval.start = start;
5514 return true;
5515
5516 error:
5517 server.invalidate();
5518 port.invalidate();
5519 path.invalidate();
5520 params.clear();
5521 interval.start = (interval.end = start) + 1;
5522 return false;
5523 }
5524
5525 virtual void invalidate()
5526 {
5527 server.invalidate();
5528 port.invalidate();
5529 path.invalidate();
5530 params.clear();
5531 tester::invalidate();
5532 }
5533
5534 public:
5535 http_url_server server;
5536 http_url_port port;
5537 http_url_path path;
5538 std::list<http_url_parameter> params;
5539 };
5540
5544 class http_language : public tester
5545 {
5546 public:
5547 virtual bool match(
5548 _In_reads_or_z_(end) const char* text,
5549 _In_ size_t start = 0,
5550 _In_ size_t end = (size_t)-1,
5551 _In_ int flags = match_default)
5552 {
5553 assert(text || start >= end);
5554 interval.end = start;
5555 components.clear();
5556 for (;;) {
5557 if (interval.end < end && text[interval.end]) {
5559 k.end = interval.end;
5560 for (;;) {
5561 if (k.end < end && text[k.end]) {
5562 if (isalpha(text[k.end]))
5563 k.end++;
5564 else
5565 break;
5566 }
5567 else
5568 break;
5569 }
5570 if (interval.end < k.end) {
5571 k.start = interval.end;
5572 interval.end = k.end;
5573 components.push_back(k);
5574 }
5575 else
5576 break;
5577 if (interval.end < end && text[interval.end] == '-')
5578 interval.end++;
5579 else
5580 break;
5581 }
5582 else
5583 break;
5584 }
5585 if (!components.empty()) {
5586 interval.start = start;
5587 interval.end = components.back().end;
5588 return true;
5589 }
5590 interval.start = (interval.end = start) + 1;
5591 return false;
5592 }
5593
5594 virtual void invalidate()
5595 {
5596 components.clear();
5597 tester::invalidate();
5598 }
5599
5600 public:
5601 std::vector<stdex::interval<size_t>> components;
5602 };
5603
5607 class http_weight : public tester
5608 {
5609 public:
5610 http_weight(_In_ const std::locale& locale = std::locale()) :
5611 tester(locale),
5612 value(1.0f)
5613 {}
5614
5615 virtual bool match(
5616 _In_reads_or_z_(end) const char* text,
5617 _In_ size_t start = 0,
5618 _In_ size_t end = (size_t)-1,
5619 _In_ int flags = match_default)
5620 {
5621 assert(text || start >= end);
5622 size_t celi_del = 0, decimalni_del = 0, decimalni_del_n = 1;
5623 interval.end = start;
5624 for (;;) {
5625 if (interval.end < end && text[interval.end]) {
5626 if ('0' <= text[interval.end] && text[interval.end] <= '9') {
5627 celi_del = celi_del * 10 + text[interval.end] - '0';
5628 interval.end++;
5629 }
5630 else if (text[interval.end] == '.') {
5631 interval.end++;
5632 for (;;) {
5633 if (interval.end < end && text[interval.end]) {
5634 if ('0' <= text[interval.end] && text[interval.end] <= '9') {
5635 decimalni_del = decimalni_del * 10 + text[interval.end] - '0';
5636 decimalni_del_n *= 10;
5637 interval.end++;
5638 }
5639 else
5640 break;
5641 }
5642 else
5643 break;
5644 }
5645 break;
5646 }
5647 else
5648 break;
5649 }
5650 else
5651 break;
5652 }
5653 if (start < interval.end) {
5654 value = (float)((double)celi_del + (double)decimalni_del / decimalni_del_n);
5655 interval.start = start;
5656 return true;
5657 }
5658 value = 1.0f;
5659 interval.start = (interval.end = start) + 1;
5660 return false;
5661 }
5662
5663 virtual void invalidate()
5664 {
5665 value = 1.0f;
5666 tester::invalidate();
5667 }
5668
5669 public:
5670 float value;
5671 };
5672
5676 class http_asterisk : public tester
5677 {
5678 public:
5679 virtual bool match(
5680 _In_reads_or_z_(end) const char* text,
5681 _In_ size_t start = 0,
5682 _In_ size_t end = (size_t)-1,
5683 _In_ int flags = match_default)
5684 {
5685 assert(text || end <= start);
5686 if (start < end && text[start] == '*') {
5687 interval.end = (interval.start = start) + 1;
5688 return true;
5689 }
5690 interval.start = (interval.end = start) + 1;
5691 return false;
5692 }
5693 };
5694
5698 template <class T, class T_asterisk = http_asterisk>
5700 {
5701 public:
5702 virtual bool match(
5703 _In_reads_or_z_(end) const char* text,
5704 _In_ size_t start = 0,
5705 _In_ size_t end = (size_t)-1,
5706 _In_ int flags = match_default)
5707 {
5708 assert(text || start >= end);
5709 size_t konec_vrednosti;
5710 interval.end = start;
5711 if (asterisk.match(text, interval.end, end, flags)) {
5712 interval.end = konec_vrednosti = asterisk.interval.end;
5713 value.invalidate();
5714 }
5715 else if (value.match(text, interval.end, end, flags)) {
5716 interval.end = konec_vrednosti = value.interval.end;
5717 asterisk.invalidate();
5718 }
5719 else {
5720 asterisk.invalidate();
5721 value.invalidate();
5722 interval.start = (interval.end = start) + 1;
5723 return false;
5724 }
5725
5726 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
5727 if (interval.end < end && text[interval.end] == ';') {
5728 interval.end++;
5729 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
5730 if (interval.end < end && (text[interval.end] == 'q' || text[interval.end] == 'Q')) {
5731 interval.end++;
5732 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
5733 if (interval.end < end && text[interval.end] == '=') {
5734 interval.end++;
5735 while (interval.end < end && text[interval.end] && isspace(text[interval.end])) interval.end++;
5736 if (factor.match(text, interval.end, end, flags))
5737 interval.end = factor.interval.end;
5738 }
5739 }
5740 }
5741 if (!factor.interval) {
5742 factor.invalidate();
5743 interval.end = konec_vrednosti;
5744 }
5745 interval.start = start;
5746 return true;
5747 }
5748
5749 virtual void invalidate()
5750 {
5751 asterisk.invalidate();
5752 value.invalidate();
5753 factor.invalidate();
5754 tester::invalidate();
5755 }
5756
5757 public:
5758 T_asterisk asterisk;
5759 T value;
5760 http_weight factor;
5761 };
5762
5767 {
5768 public:
5769 virtual bool match(
5770 _In_reads_or_z_(end) const char* text,
5771 _In_ size_t start = 0,
5772 _In_ size_t end = (size_t)-1,
5773 _In_ int flags = match_default)
5774 {
5775 assert(text || start >= end);
5776 interval.end = start;
5777 if (interval.end < end && text[interval.end] == '$')
5778 interval.end++;
5779 else
5780 goto error;
5781 if (name.match(text, interval.end, end, flags))
5782 interval.end = name.interval.end;
5783 else
5784 goto error;
5785 while (m_space.match(text, interval.end, end, flags))
5786 interval.end = m_space.interval.end;
5787 if (interval.end < end && text[interval.end] == '=')
5788 interval.end++;
5789 else
5790 goto error;
5791 while (m_space.match(text, interval.end, end, flags))
5792 interval.end = m_space.interval.end;
5793 if (value.match(text, interval.end, end, flags))
5794 interval.end = value.interval.end;
5795 else
5796 goto error;
5797 interval.start = start;
5798 return true;
5799
5800 error:
5801 name.invalidate();
5802 value.invalidate();
5803 interval.start = (interval.end = start) + 1;
5804 return false;
5805 }
5806
5807 virtual void invalidate()
5808 {
5809 name.invalidate();
5810 value.invalidate();
5811 tester::invalidate();
5812 }
5813
5814 public:
5815 http_token name;
5816 http_value value;
5817
5818 protected:
5819 http_space m_space;
5820 };
5821
5825 class http_cookie : public tester
5826 {
5827 public:
5828 virtual bool match(
5829 _In_reads_or_z_(end) const char* text,
5830 _In_ size_t start = 0,
5831 _In_ size_t end = (size_t)-1,
5832 _In_ int flags = match_default)
5833 {
5834 assert(text || start >= end);
5835 interval.end = start;
5836 if (name.match(text, interval.end, end, flags))
5837 interval.end = name.interval.end;
5838 else
5839 goto error;
5840 while (m_space.match(text, interval.end, end, flags))
5841 interval.end = m_space.interval.end;
5842 if (interval.end < end && text[interval.end] == '=')
5843 interval.end++;
5844 else
5845 goto error;
5846 while (m_space.match(text, interval.end, end, flags))
5847 interval.end = m_space.interval.end;
5848 if (value.match(text, interval.end, end, flags))
5849 interval.end = value.interval.end;
5850 else
5851 goto error;
5852 params.clear();
5853 for (;;) {
5854 if (interval.end < end && text[interval.end]) {
5855 if (m_space.match(text, interval.end, end, flags))
5856 interval.end = m_space.interval.end;
5857 else if (text[interval.end] == ';') {
5858 interval.end++;
5859 while (m_space.match(text, interval.end, end, flags))
5860 interval.end = m_space.interval.end;
5862 if (param.match(text, interval.end, end, flags)) {
5863 interval.end = param.interval.end;
5864 params.push_back(std::move(param));
5865 }
5866 else
5867 break;
5868 }
5869 else
5870 break;
5871 }
5872 else
5873 break;
5874 }
5875 interval.start = start;
5876 interval.end = params.empty() ? value.interval.end : params.back().interval.end;
5877 return true;
5878
5879 error:
5880 name.invalidate();
5881 value.invalidate();
5882 params.clear();
5883 interval.start = (interval.end = start) + 1;
5884 return false;
5885 }
5886
5887 virtual void invalidate()
5888 {
5889 name.invalidate();
5890 value.invalidate();
5891 params.clear();
5892 tester::invalidate();
5893 }
5894
5895 public:
5898 std::list<http_cookie_parameter> params;
5899
5900 protected:
5901 http_space m_space;
5902 };
5903
5907 class http_agent : public tester
5908 {
5909 public:
5910 virtual bool match(
5911 _In_reads_or_z_(end) const char* text,
5912 _In_ size_t start = 0,
5913 _In_ size_t end = (size_t)-1,
5914 _In_ int flags = match_default)
5915 {
5916 assert(text || start >= end);
5917 interval.end = start;
5918 type.start = interval.end;
5919 for (;;) {
5920 if (interval.end < end && text[interval.end]) {
5921 if (text[interval.end] == '/') {
5922 type.end = interval.end;
5923 interval.end++;
5924 version.start = interval.end;
5925 for (;;) {
5926 if (interval.end < end && text[interval.end]) {
5927 if (isspace(text[interval.end])) {
5928 version.end = interval.end;
5929 break;
5930 }
5931 else
5932 interval.end++;
5933 }
5934 else {
5935 version.end = interval.end;
5936 break;
5937 }
5938 }
5939 break;
5940 }
5941 else if (isspace(text[interval.end])) {
5942 type.end = interval.end;
5943 break;
5944 }
5945 else
5946 interval.end++;
5947 }
5948 else {
5949 type.end = interval.end;
5950 break;
5951 }
5952 }
5953 if (start < interval.end) {
5954 interval.start = start;
5955 return true;
5956 }
5957 type.start = 1;
5958 type.end = 0;
5959 version.start = 1;
5960 version.end = 0;
5961 interval.start = 1;
5962 interval.end = 0;
5963 return false;
5964 }
5965
5966 virtual void invalidate()
5967 {
5968 type.start = 1;
5969 type.end = 0;
5970 version.start = 1;
5971 version.end = 0;
5972 tester::invalidate();
5973 }
5974
5975 public:
5978 };
5979
5983 class http_protocol : public tester
5984 {
5985 public:
5986 http_protocol(_In_ const std::locale& locale = std::locale()) :
5987 tester(locale),
5988 version(0x009)
5989 {}
5990
5991 virtual bool match(
5992 _In_reads_or_z_(end) const char* text,
5993 _In_ size_t start = 0,
5994 _In_ size_t end = (size_t)-1,
5995 _In_ int flags = match_default)
5996 {
5997 assert(text || start >= end);
5998 interval.end = start;
5999 type.start = interval.end;
6000 for (;;) {
6001 if (interval.end < end && text[interval.end]) {
6002 if (text[interval.end] == '/') {
6003 type.end = interval.end;
6004 interval.end++;
6005 break;
6006 }
6007 else if (isspace(text[interval.end]))
6008 goto error;
6009 else
6010 interval.end++;
6011 }
6012 else {
6013 type.end = interval.end;
6014 goto error;
6015 }
6016 }
6017 version_maj.start = interval.end;
6018 for (;;) {
6019 if (interval.end < end && text[interval.end]) {
6020 if (text[interval.end] == '.') {
6021 version_maj.end = interval.end;
6022 interval.end++;
6023 version_min.start = interval.end;
6024 for (;;) {
6025 if (interval.end < end && text[interval.end]) {
6026 if (isspace(text[interval.end])) {
6027 version_min.end = interval.end;
6028 version =
6029 (uint16_t)strtoui(text + version_maj.start, version_maj.size(), nullptr, 10) * 0x100 +
6030 (uint16_t)strtoui(text + version_min.start, version_min.size(), nullptr, 10);
6031 break;
6032 }
6033 else
6034 interval.end++;
6035 }
6036 else
6037 goto error;
6038 }
6039 break;
6040 }
6041 else if (isspace(text[interval.end])) {
6042 version_maj.end = interval.end;
6043 version_min.start = 1;
6044 version_min.end = 0;
6045 version = (uint16_t)strtoui(text + version_maj.start, version_maj.size(), nullptr, 10) * 0x100;
6046 break;
6047 }
6048 else
6049 interval.end++;
6050 }
6051 else
6052 goto error;
6053 }
6054 interval.start = start;
6055 return true;
6056
6057 error:
6058 type.start = 1;
6059 type.end = 0;
6060 version_maj.start = 1;
6061 version_maj.end = 0;
6062 version_min.start = 1;
6063 version_min.end = 0;
6064 version = 0x009;
6065 interval.start = 1;
6066 interval.end = 0;
6067 return false;
6068 }
6069
6070 virtual void invalidate()
6071 {
6072 type.start = 1;
6073 type.end = 0;
6074 version_maj.start = 1;
6075 version_maj.end = 0;
6076 version_min.start = 1;
6077 version_min.end = 0;
6078 version = 0x009;
6079 tester::invalidate();
6080 }
6081
6082 public:
6084 stdex::interval<size_t> version_maj;
6085 stdex::interval<size_t> version_min;
6086 uint16_t version;
6087 };
6088
6092 class http_request : public tester
6093 {
6094 public:
6095 virtual bool match(
6096 _In_reads_or_z_(end) const char* text,
6097 _In_ size_t start = 0,
6098 _In_ size_t end = (size_t)-1,
6099 _In_ int flags = match_default)
6100 {
6101 assert(text || start >= end);
6102 interval.end = start;
6103
6104 for (;;) {
6105 if (m_line_break.match(text, interval.end, end, flags))
6106 goto error;
6107 else if (interval.end < end && text[interval.end]) {
6108 if (isspace(text[interval.end]))
6109 interval.end++;
6110 else
6111 break;
6112 }
6113 else
6114 goto error;
6115 }
6116 verb.start = interval.end;
6117 for (;;) {
6118 if (m_line_break.match(text, interval.end, end, flags))
6119 goto error;
6120 else if (interval.end < end && text[interval.end]) {
6121 if (isspace(text[interval.end])) {
6122 verb.end = interval.end;
6123 interval.end++;
6124 break;
6125 }
6126 else
6127 interval.end++;
6128 }
6129 else
6130 goto error;
6131 }
6132
6133 for (;;) {
6134 if (m_line_break.match(text, interval.end, end, flags))
6135 goto error;
6136 else if (interval.end < end && text[interval.end]) {
6137 if (isspace(text[interval.end]))
6138 interval.end++;
6139 else
6140 break;
6141 }
6142 else
6143 goto error;
6144 }
6145 if (url.match(text, interval.end, end, flags))
6147 else
6148 goto error;
6149
6150 protocol.invalidate();
6151 for (;;) {
6152 if (m_line_break.match(text, interval.end, end, flags)) {
6153 interval.end = m_line_break.interval.end;
6154 goto end;
6155 }
6156 else if (interval.end < end && text[interval.end]) {
6157 if (isspace(text[interval.end]))
6158 interval.end++;
6159 else
6160 break;
6161 }
6162 else
6163 goto end;
6164 }
6165 for (;;) {
6166 if (m_line_break.match(text, interval.end, end, flags)) {
6167 interval.end = m_line_break.interval.end;
6168 goto end;
6169 }
6170 else if (protocol.match(text, interval.end, end, flags)) {
6171 interval.end = protocol.interval.end;
6172 break;
6173 }
6174 else
6175 goto end;
6176 }
6177
6178 for (;;) {
6179 if (m_line_break.match(text, interval.end, end, flags)) {
6180 interval.end = m_line_break.interval.end;
6181 break;
6182 }
6183 else if (interval.end < end && text[interval.end])
6184 interval.end++;
6185 else
6186 goto end;
6187 }
6188
6189 end:
6190 interval.start = start;
6191 return true;
6192
6193 error:
6194 verb.start = 1;
6195 verb.end = 0;
6196 url.invalidate();
6197 protocol.invalidate();
6198 interval.start = 1;
6199 interval.end = 0;
6200 return false;
6201 }
6202
6203 virtual void invalidate()
6204 {
6205 verb.start = 1;
6206 verb.end = 0;
6207 url.invalidate();
6208 protocol.invalidate();
6209 tester::invalidate();
6210 }
6211
6212 public:
6214 http_url url;
6215 http_protocol protocol;
6216
6217 protected:
6218 http_line_break m_line_break;
6219 };
6220
6224 class http_header : public tester
6225 {
6226 public:
6227 virtual bool match(
6228 _In_reads_or_z_(end) const char* text,
6229 _In_ size_t start = 0,
6230 _In_ size_t end = (size_t)-1,
6231 _In_ int flags = match_default)
6232 {
6233 assert(text || start >= end);
6234 interval.end = start;
6235
6236 if (m_line_break.match(text, interval.end, end, flags) ||
6237 interval.end < end && text[interval.end] && isspace(text[interval.end]))
6238 goto error;
6239 name.start = interval.end;
6240 for (;;) {
6241 if (m_line_break.match(text, interval.end, end, flags))
6242 goto error;
6243 else if (interval.end < end && text[interval.end]) {
6244 if (isspace(text[interval.end])) {
6245 name.end = interval.end;
6246 interval.end++;
6247 for (;;) {
6248 if (m_line_break.match(text, interval.end, end, flags))
6249 goto error;
6250 else if (interval.end < end && text[interval.end]) {
6251 if (isspace(text[interval.end]))
6252 interval.end++;
6253 else
6254 break;
6255 }
6256 else
6257 goto error;
6258 }
6259 if (interval.end < end && text[interval.end] == ':') {
6260 interval.end++;
6261 break;
6262 }
6263 else
6264 goto error;
6265 break;
6266 }
6267 else if (text[interval.end] == ':') {
6268 name.end = interval.end;
6269 interval.end++;
6270 break;
6271 }
6272 else
6273 interval.end++;
6274 }
6275 else
6276 goto error;
6277 }
6278 value.start = (size_t)-1;
6279 value.end = 0;
6280 for (;;) {
6281 if (m_line_break.match(text, interval.end, end, flags)) {
6282 interval.end = m_line_break.interval.end;
6283 if (!m_line_break.match(text, interval.end, end, flags) &&
6284 interval.end < end && text[interval.end] && isspace(text[interval.end]))
6285 interval.end++;
6286 else
6287 break;
6288 }
6289 else if (interval.end < end && text[interval.end]) {
6290 if (isspace(text[interval.end]))
6291 interval.end++;
6292 else {
6293 if (value.start == (size_t)-1) value.start = interval.end;
6294 value.end = ++interval.end;
6295 }
6296 }
6297 else
6298 break;
6299 }
6300 interval.start = start;
6301 return true;
6302
6303 error:
6304 name.start = 1;
6305 name.end = 0;
6306 value.start = 1;
6307 value.end = 0;
6308 interval.start = 1;
6309 interval.end = 0;
6310 return false;
6311 }
6312
6313 virtual void invalidate()
6314 {
6315 name.start = 1;
6316 name.end = 0;
6317 value.start = 1;
6318 value.end = 0;
6319 tester::invalidate();
6320 }
6321
6322 public:
6323 stdex::interval<size_t> name; // interval imena glave
6324 stdex::interval<size_t> value; // interval vrednosti glave
6325
6326 protected:
6327 http_line_break m_line_break;
6328 };
6329
6333 template <class T>
6334 class http_header_collection : public T
6335 {
6336 public:
6337 void insert(
6338 _In_reads_or_z_(end) const T* text,
6339 _In_ size_t start = 0,
6340 _In_ size_t end = (size_t)-1,
6341 _In_ int flags = match_default)
6342 {
6343 while (start < end) {
6344 while (start < end && text[start] && isspace(text[start])) start++;
6345 if (start < end && text[start] == ',') {
6346 start++;
6347 while (start < end&& text[start] && isspace(text[start])) start++;
6348 }
6349 T::key_type el;
6350 if (el.match(text, start, end, flags)) {
6351 start = el.interval.end;
6352 T::insert(std::move(el));
6353 }
6354 else
6355 break;
6356 }
6357 }
6358 };
6359
6360 template <class T>
6362 constexpr bool operator()(const T& a, const T& b) const noexcept
6363 {
6364 return a.factor.value > b.factor.value;
6365 }
6366 };
6367
6371 template <class T, class _Pr = http_factor_more<T>, class _Alloc = std::allocator<T>>
6373
6377 template <class T>
6379 {
6380 public:
6382 _In_ const std::shared_ptr<basic_tester<T>>& quote,
6383 _In_ const std::shared_ptr<basic_tester<T>>& chr,
6384 _In_ const std::shared_ptr<basic_tester<T>>& escape,
6385 _In_ const std::shared_ptr<basic_tester<T>>& sol,
6386 _In_ const std::shared_ptr<basic_tester<T>>& bs,
6387 _In_ const std::shared_ptr<basic_tester<T>>& ff,
6388 _In_ const std::shared_ptr<basic_tester<T>>& lf,
6389 _In_ const std::shared_ptr<basic_tester<T>>& cr,
6390 _In_ const std::shared_ptr<basic_tester<T>>& htab,
6391 _In_ const std::shared_ptr<basic_tester<T>>& uni,
6392 _In_ const std::shared_ptr<basic_integer16<T>>& hex,
6393 _In_ const std::locale& locale = std::locale()) :
6394 basic_tester<T>(locale),
6395 m_quote(quote),
6396 m_chr(chr),
6397 m_escape(escape),
6398 m_sol(sol),
6399 m_bs(bs),
6400 m_ff(ff),
6401 m_lf(lf),
6402 m_cr(cr),
6403 m_htab(htab),
6404 m_uni(uni),
6405 m_hex(hex)
6406 {}
6407
6408 virtual bool match(
6409 _In_reads_or_z_(end) const T* text,
6410 _In_ size_t start = 0,
6411 _In_ size_t end = (size_t)-1,
6412 _In_ int flags = match_default)
6413 {
6414 assert(text || start >= end);
6415 interval.end = start;
6416 if (m_quote->match(text, interval.end, end, flags)) {
6417 interval.end = m_quote->interval.end;
6418 value.clear();
6419 for (;;) {
6420 if (m_quote->match(text, interval.end, end, flags)) {
6421 interval.start = start;
6422 interval.end = m_quote->interval.end;
6423 return true;
6424 }
6425 if (m_escape->match(text, interval.end, end, flags)) {
6426 if (m_quote->match(text, m_escape->interval.end, end, flags)) {
6427 value += '"'; interval.end = m_quote->interval.end;
6428 continue;
6429 }
6430 if (m_sol->match(text, m_escape->interval.end, end, flags)) {
6431 value += '/'; interval.end = m_sol->interval.end;
6432 continue;
6433 }
6434 if (m_bs->match(text, m_escape->interval.end, end, flags)) {
6435 value += '\b'; interval.end = m_bs->interval.end;
6436 continue;
6437 }
6438 if (m_ff->match(text, m_escape->interval.end, end, flags)) {
6439 value += '\f'; interval.end = m_ff->interval.end;
6440 continue;
6441 }
6442 if (m_lf->match(text, m_escape->interval.end, end, flags)) {
6443 value += '\n'; interval.end = m_lf->interval.end;
6444 continue;
6445 }
6446 if (m_cr->match(text, m_escape->interval.end, end, flags)) {
6447 value += '\r'; interval.end = m_cr->interval.end;
6448 continue;
6449 }
6450 if (m_htab->match(text, m_escape->interval.end, end, flags)) {
6451 value += '\t'; interval.end = m_htab->interval.end;
6452 continue;
6453 }
6454 if (
6455 m_uni->match(text, m_escape->interval.end, end, flags) &&
6456 m_hex->match(text, m_uni->interval.end, std::min(m_uni->interval.end + 4, end), flags | match_case_insensitive) &&
6457 m_hex->interval.size() == 4 /* JSON requests 4-digit Unicode sequneces: \u.... */)
6458 {
6459 assert(m_hex->value <= 0xffff);
6460 if (sizeof(T) == 1) {
6461 if (m_hex->value > 0x7ff) {
6462 value += (T)(0xe0 | (m_hex->value >> 12) & 0x0f);
6463 value += (T)(0x80 | (m_hex->value >> 6) & 0x3f);
6464 value += (T)(0x80 | m_hex->value & 0x3f);
6465 }
6466 else if (m_hex->value > 0x7f) {
6467 value += (T)(0xc0 | (m_hex->value >> 6) & 0x1f);
6468 value += (T)(0x80 | m_hex->value & 0x3f);
6469 }
6470 else
6471 value += (T)(m_hex->value & 0x7f);
6472 }
6473 else
6474 value += (T)m_hex->value;
6475 interval.end = m_hex->interval.end;
6476 continue;
6477 }
6478 if (m_escape->match(text, m_escape->interval.end, end, flags)) {
6479 value += '\\'; interval.end = m_escape->interval.end;
6480 continue;
6481 }
6482 }
6483 if (m_chr->match(text, interval.end, end, flags)) {
6484 value.Prilepi(text + m_chr->interval.start, m_chr->interval.size());
6485 interval.end = m_chr->interval.end;
6486 continue;
6487 }
6488 break;
6489 }
6490 }
6491 value.clear();
6492 interval.start = (interval.end = start) + 1;
6493 return false;
6494 }
6495
6496 virtual void invalidate()
6497 {
6498 value.clear();
6500 }
6501
6502 public:
6503 std::basic_string<T> value;
6504
6505 protected:
6506 std::shared_ptr<basic_tester<T>> m_quote;
6507 std::shared_ptr<basic_tester<T>> m_chr;
6508 std::shared_ptr<basic_tester<T>> m_escape;
6509 std::shared_ptr<basic_tester<T>> m_sol;
6510 std::shared_ptr<basic_tester<T>> m_bs;
6511 std::shared_ptr<basic_tester<T>> m_ff;
6512 std::shared_ptr<basic_tester<T>> m_lf;
6513 std::shared_ptr<basic_tester<T>> m_cr;
6514 std::shared_ptr<basic_tester<T>> m_htab;
6515 std::shared_ptr<basic_tester<T>> m_uni;
6516 std::shared_ptr<basic_integer16<T>> m_hex;
6517 };
6518
6521#ifdef _UNICODE
6522 using tjson_string = wjson_string;
6523#else
6524 using tjson_string = json_string;
6525#endif
6526 }
6527}
6528
6529#ifdef _MSC_VER
6530#pragma warning(pop)
6531#endif
Test for angle in d°mm'ss.dddd form.
Definition: parser.hpp:4359
Test for any code unit.
Definition: parser.hpp:200
Test for beginning of line.
Definition: parser.hpp:582
Test for any.
Definition: parser.hpp:1024
Test for chemical formula.
Definition: parser.hpp:4633
Test for any code unit from a given string of code units.
Definition: parser.hpp:687
Test for specific code unit.
Definition: parser.hpp:266
Test for date.
Definition: parser.hpp:3979
Test for valid DNS domain character.
Definition: parser.hpp:2774
bool allow_on_edge
Is character allowed at the beginning or an end of a DNS domain?
Definition: parser.hpp:2812
Test for DNS domain/hostname.
Definition: parser.hpp:2874
bool m_allow_absolute
May DNS names end with a dot (absolute name)?
Definition: parser.hpp:2938
Test for e-mail address.
Definition: parser.hpp:3762
Test for emoticon.
Definition: parser.hpp:3870
std::shared_ptr< basic_tester< T > > nose
nose (e.g. -, o)
Definition: parser.hpp:3961
std::shared_ptr< basic_set< T > > mouth
mouth (e.g. ), ), (, (, |, P, D, p, d)
Definition: parser.hpp:3962
std::shared_ptr< basic_tester< T > > eyes
eyes (e.g. :, ;, >, |, B)
Definition: parser.hpp:3960
std::shared_ptr< basic_tester< T > > emoticon
emoticon as a whole (e.g. 😀, 🤔, 😶)
Definition: parser.hpp:3958
std::shared_ptr< basic_tester< T > > apex
apex/eyebrows/halo (e.g. O, 0)
Definition: parser.hpp:3959
Test for end of line.
Definition: parser.hpp:620
Test for fraction.
Definition: parser.hpp:1653
Test for decimal integer.
Definition: parser.hpp:1262
Test for decimal integer possibly containing thousand separators.
Definition: parser.hpp:1347
bool has_separators
Did integer have any separators?
Definition: parser.hpp:1407
size_t digit_count
Total number of digits in integer.
Definition: parser.hpp:1406
Test for hexadecimal integer.
Definition: parser.hpp:1428
Base class for integer testing.
Definition: parser.hpp:1240
size_t value
Calculated value of the numeral.
Definition: parser.hpp:1254
Test for IPv4 address.
Definition: parser.hpp:2312
Test for IPv6 address.
Definition: parser.hpp:2533
std::shared_ptr< basic_tester< T > > scope_id
Scope ID (e.g. NIC index with link-local addresses)
Definition: parser.hpp:2737
Test for valid IPv6 address scope ID character.
Definition: parser.hpp:2459
Test for repeating.
Definition: parser.hpp:877
bool m_greedy
try to match as long sequence as possible
Definition: parser.hpp:916
size_t m_min_iterations
minimum number of iterations
Definition: parser.hpp:914
size_t m_max_iterations
maximum number of iterations
Definition: parser.hpp:915
std::shared_ptr< basic_tester< T > > m_el
repeating element
Definition: parser.hpp:913
Test for JSON string.
Definition: parser.hpp:6379
Test for mixed numeral.
Definition: parser.hpp:1888
std::shared_ptr< basic_tester< T > > special_sign
Special sign (e.g. plus-minus '±')
Definition: parser.hpp:1992
std::shared_ptr< basic_tester< T > > fraction
fraction
Definition: parser.hpp:1994
std::shared_ptr< basic_tester< T > > positive_sign
Positive sign.
Definition: parser.hpp:1990
std::shared_ptr< basic_tester< T > > integer
Integer part.
Definition: parser.hpp:1993
std::shared_ptr< basic_tester< T > > negative_sign
Negative sign.
Definition: parser.hpp:1991
Test for monetary numeral.
Definition: parser.hpp:2183
std::shared_ptr< basic_tester< T > > decimal_separator
Decimal separator.
Definition: parser.hpp:2294
std::shared_ptr< basic_tester< T > > decimal
Decimal part.
Definition: parser.hpp:2295
std::shared_ptr< basic_tester< T > > currency
Currency part.
Definition: parser.hpp:2292
std::shared_ptr< basic_tester< T > > special_sign
Special sign (e.g. plus-minus '±')
Definition: parser.hpp:2291
std::shared_ptr< basic_tester< T > > positive_sign
Positive sign.
Definition: parser.hpp:2289
std::shared_ptr< basic_tester< T > > integer
Integer part.
Definition: parser.hpp:2293
std::shared_ptr< basic_tester< T > > negative_sign
Negative sign.
Definition: parser.hpp:2290
"No-op" match
Definition: parser.hpp:168
Test for permutation.
Definition: parser.hpp:1164
Test for phone number.
Definition: parser.hpp:4482
std::basic_string< T > value
Normalized phone number.
Definition: parser.hpp:4608
Test for any punctuation code unit.
Definition: parser.hpp:438
Test for Roman numeral.
Definition: parser.hpp:1537
Test for scientific numeral.
Definition: parser.hpp:2014
std::shared_ptr< basic_tester< T > > positive_exp_sign
Positive exponent sign (e.g. '+')
Definition: parser.hpp:2163
std::shared_ptr< basic_tester< T > > negative_exp_sign
Negative exponent sign (e.g. '-')
Definition: parser.hpp:2164
std::shared_ptr< basic_tester< T > > positive_sign
Positive sign.
Definition: parser.hpp:2156
double value
Calculated value of the numeral.
Definition: parser.hpp:2166
std::shared_ptr< basic_integer< T > > decimal
Decimal part.
Definition: parser.hpp:2161
std::shared_ptr< basic_tester< T > > negative_sign
Negative sign.
Definition: parser.hpp:2157
std::shared_ptr< basic_integer< T > > exponent
Exponent part.
Definition: parser.hpp:2165
std::shared_ptr< basic_tester< T > > special_sign
Special sign (e.g. plus-minus '±')
Definition: parser.hpp:2158
std::shared_ptr< basic_tester< T > > decimal_separator
Decimal separator.
Definition: parser.hpp:2160
std::shared_ptr< basic_tester< T > > exponent_symbol
Exponent symbol (e.g. 'e')
Definition: parser.hpp:2162
std::shared_ptr< basic_integer< T > > integer
Integer part.
Definition: parser.hpp:2159
Test for match score.
Definition: parser.hpp:1716
Test for sequence.
Definition: parser.hpp:973
Definition: parser.hpp:655
Test for signed numeral.
Definition: parser.hpp:1802
std::shared_ptr< basic_tester< T > > positive_sign
Positive sign.
Definition: parser.hpp:1868
std::shared_ptr< basic_tester< T > > number
Number.
Definition: parser.hpp:1871
std::shared_ptr< basic_tester< T > > negative_sign
Negative sign.
Definition: parser.hpp:1869
std::shared_ptr< basic_tester< T > > special_sign
Special sign (e.g. plus-minus '±')
Definition: parser.hpp:1870
Test for any space code unit.
Definition: parser.hpp:359
Test for any space or punctuation code unit.
Definition: parser.hpp:508
Test for any string.
Definition: parser.hpp:1092
Test for given string.
Definition: parser.hpp:782
Base template for all testers.
Definition: parser.hpp:49
interval< size_t > interval
Test for interval.
Definition: parser.hpp:148
Test for time.
Definition: parser.hpp:4256
Test for valid URL password character.
Definition: parser.hpp:3056
Test for valid URL path character.
Definition: parser.hpp:3156
Test for URL path.
Definition: parser.hpp:3264
Test for valid URL username character.
Definition: parser.hpp:2957
Test for URL.
Definition: parser.hpp:3405
Test for HTTP agent.
Definition: parser.hpp:5908
Test for HTTP any type.
Definition: parser.hpp:5040
Test for HTTP asterisk.
Definition: parser.hpp:5677
Collection of HTTP headers.
Definition: parser.hpp:6335
Test for HTTP header.
Definition: parser.hpp:6225
Test for HTTP language (RFC1766)
Definition: parser.hpp:5545
Test for HTTP line break (RFC2616: CRLF | LF)
Definition: parser.hpp:4714
Test for HTTP media range (RFC2616: media-range)
Definition: parser.hpp:5072
Test for HTTP media type (RFC2616: media-type)
Definition: parser.hpp:5127
Test for HTTP parameter (RFC2616: parameter)
Definition: parser.hpp:4985
http_token name
Parameter name.
Definition: parser.hpp:5029
http_value value
Parameter value.
Definition: parser.hpp:5030
Test for HTTP protocol.
Definition: parser.hpp:5984
uint16_t version
HTTP protocol version: 0x100 = 1.0, 0x101 = 1.1...
Definition: parser.hpp:6086
Test for HTTP quoted string (RFC2616: quoted-string)
Definition: parser.hpp:4875
stdex::interval< size_t > content
String content (without quotes)
Definition: parser.hpp:4931
Test for HTTP request.
Definition: parser.hpp:6093
Test for HTTP space (RFC2616: LWS)
Definition: parser.hpp:4750
Test for HTTP text character (RFC2616: TEXT)
Definition: parser.hpp:4787
Test for HTTP token (RFC2616: token - tolerates non-ASCII)
Definition: parser.hpp:4821
Test for HTTP URL parameter.
Definition: parser.hpp:5367
Test for HTTP URL path segment.
Definition: parser.hpp:5278
Test for HTTP URL path segment.
Definition: parser.hpp:5311
std::vector< http_url_path_segment > segments
Path segments.
Definition: parser.hpp:5360
Test for HTTP URL port.
Definition: parser.hpp:5222
Test for HTTP URL server.
Definition: parser.hpp:5185
Test for HTTP URL.
Definition: parser.hpp:5448
Test for HTTP value (RFC2616: value)
Definition: parser.hpp:4941
http_quoted_string string
Value when matched as quoted string.
Definition: parser.hpp:4977
http_token token
Value when matched as token.
Definition: parser.hpp:4978
Test for HTTP weight factor.
Definition: parser.hpp:5608
float value
Calculated value of the weight factor.
Definition: parser.hpp:5670
Test for HTTP weighted value.
Definition: parser.hpp:5700
Test for any SGML code point.
Definition: parser.hpp:230
Test for any SGML code point from a given string of SGML code points.
Definition: parser.hpp:739
Test for specific SGML code point.
Definition: parser.hpp:315
Test for valid DNS domain SGML character.
Definition: parser.hpp:2830
Test for valid IPv6 address scope ID SGML character.
Definition: parser.hpp:2498
Test for any SGML punctuation code point.
Definition: parser.hpp:479
Test for any SGML space code point.
Definition: parser.hpp:402
Test for any SGML space or punctuation code point.
Definition: parser.hpp:551
Test for SGML given string.
Definition: parser.hpp:829
Test for valid URL password SGML character.
Definition: parser.hpp:3108
Test for valid URL path SGML character.
Definition: parser.hpp:3212
Test for valid URL username SGML character.
Definition: parser.hpp:3008
Base template for collection-holding testers.
Definition: parser.hpp:933
Numerical interval.
Definition: interval.hpp:17
T end
interval end
Definition: interval.hpp:19
interval() noexcept
Constructs an invalid interval.
Definition: interval.hpp:24
T start
interval start
Definition: interval.hpp:18
Definition: parser.hpp:6361