regex.h
1 // Tencent is pleased to support the open source community by making RapidJSON available.
2 //
3 // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.
4 //
5 // Licensed under the MIT License (the "License"); you may not use this file except
6 // in compliance with the License. You may obtain a copy of the License at
7 //
8 // http://opensource.org/licenses/MIT
9 //
10 // Unless required by applicable law or agreed to in writing, software distributed
11 // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
12 // CONDITIONS OF ANY KIND, either express or implied. See the License for the
13 // specific language governing permissions and limitations under the License.
14 
15 #ifndef RAPIDJSON_INTERNAL_REGEX_H_
16 #define RAPIDJSON_INTERNAL_REGEX_H_
17 
18 #include "../allocators.h"
19 #include "../stream.h"
20 #include "stack.h"
21 
22 #ifdef __clang__
23 RAPIDJSON_DIAG_PUSH
24 RAPIDJSON_DIAG_OFF(padded)
25 RAPIDJSON_DIAG_OFF(switch-enum)
26 RAPIDJSON_DIAG_OFF(implicit-fallthrough)
27 #endif
28 
29 #ifdef __GNUC__
30 RAPIDJSON_DIAG_PUSH
31 RAPIDJSON_DIAG_OFF(effc++)
32 #if __GNUC__ >= 7
33 RAPIDJSON_DIAG_OFF(implicit-fallthrough)
34 #endif
35 #endif
36 
37 #ifdef _MSC_VER
38 RAPIDJSON_DIAG_PUSH
39 RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
40 #endif
41 
42 #ifndef RAPIDJSON_REGEX_VERBOSE
43 #define RAPIDJSON_REGEX_VERBOSE 0
44 #endif
45 
46 RAPIDJSON_NAMESPACE_BEGIN
47 namespace internal {
48 
49 ///////////////////////////////////////////////////////////////////////////////
50 // GenericRegex
51 
52 static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
53 static const SizeType kRegexInvalidRange = ~SizeType(0);
54 
55 //! Regular expression engine with subset of ECMAscript grammar.
56 /*!
57  Supported regular expression syntax:
58  - \c ab Concatenation
59  - \c a|b Alternation
60  - \c a? Zero or one
61  - \c a* Zero or more
62  - \c a+ One or more
63  - \c a{3} Exactly 3 times
64  - \c a{3,} At least 3 times
65  - \c a{3,5} 3 to 5 times
66  - \c (ab) Grouping
67  - \c ^a At the beginning
68  - \c a$ At the end
69  - \c . Any character
70  - \c [abc] Character classes
71  - \c [a-c] Character class range
72  - \c [a-z0-9_] Character class combination
73  - \c [^abc] Negated character classes
74  - \c [^a-c] Negated character class range
75  - \c [\b] Backspace (U+0008)
76  - \c \\| \\\\ ... Escape characters
77  - \c \\f Form feed (U+000C)
78  - \c \\n Line feed (U+000A)
79  - \c \\r Carriage return (U+000D)
80  - \c \\t Tab (U+0009)
81  - \c \\v Vertical tab (U+000B)
82 
83  \note This is a Thompson NFA engine, implemented with reference to
84  Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).",
85  https://swtch.com/~rsc/regexp/regexp1.html
86 */
87 template <typename Encoding, typename Allocator = CrtAllocator>
88 class GenericRegex {
89 public:
90  typedef typename Encoding::Ch Ch;
91 
92  GenericRegex(const Ch* source, Allocator* allocator = 0) :
93  states_(allocator, 256), ranges_(allocator, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(),
94  stateSet_(), state0_(allocator, 0), state1_(allocator, 0), anchorBegin_(), anchorEnd_()
95  {
96  GenericStringStream<Encoding> ss(source);
97  DecodedStream<GenericStringStream<Encoding> > ds(ss);
98  Parse(ds);
99  }
100 
101  ~GenericRegex() {
102  Allocator::Free(stateSet_);
103  }
104 
105  bool IsValid() const {
106  return root_ != kRegexInvalidState;
107  }
108 
109  template <typename InputStream>
110  bool Match(InputStream& is) const {
111  return SearchWithAnchoring(is, true, true);
112  }
113 
114  bool Match(const Ch* s) const {
115  GenericStringStream<Encoding> is(s);
116  return Match(is);
117  }
118 
119  template <typename InputStream>
120  bool Search(InputStream& is) const {
121  return SearchWithAnchoring(is, anchorBegin_, anchorEnd_);
122  }
123 
124  bool Search(const Ch* s) const {
125  GenericStringStream<Encoding> is(s);
126  return Search(is);
127  }
128 
129 private:
130  enum Operator {
131  kZeroOrOne,
132  kZeroOrMore,
133  kOneOrMore,
134  kConcatenation,
135  kAlternation,
136  kLeftParenthesis
137  };
138 
139  static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.'
140  static const unsigned kRangeCharacterClass = 0xFFFFFFFE;
141  static const unsigned kRangeNegationFlag = 0x80000000;
142 
143  struct Range {
144  unsigned start; //
145  unsigned end;
146  SizeType next;
147  };
148 
149  struct State {
150  SizeType out; //!< Equals to kInvalid for matching state
151  SizeType out1; //!< Equals to non-kInvalid for split
152  SizeType rangeStart;
153  unsigned codepoint;
154  };
155 
156  struct Frag {
157  Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {}
158  SizeType start;
159  SizeType out; //!< link-list of all output states
160  SizeType minIndex;
161  };
162 
163  template <typename SourceStream>
164  class DecodedStream {
165  public:
166  DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); }
167  unsigned Peek() { return codepoint_; }
168  unsigned Take() {
169  unsigned c = codepoint_;
170  if (c) // No further decoding when '\0'
171  Decode();
172  return c;
173  }
174 
175  private:
176  void Decode() {
177  if (!Encoding::Decode(ss_, &codepoint_))
178  codepoint_ = 0;
179  }
180 
181  SourceStream& ss_;
182  unsigned codepoint_;
183  };
184 
185  State& GetState(SizeType index) {
186  RAPIDJSON_ASSERT(index < stateCount_);
187  return states_.template Bottom<State>()[index];
188  }
189 
190  const State& GetState(SizeType index) const {
191  RAPIDJSON_ASSERT(index < stateCount_);
192  return states_.template Bottom<State>()[index];
193  }
194 
195  Range& GetRange(SizeType index) {
196  RAPIDJSON_ASSERT(index < rangeCount_);
197  return ranges_.template Bottom<Range>()[index];
198  }
199 
200  const Range& GetRange(SizeType index) const {
201  RAPIDJSON_ASSERT(index < rangeCount_);
202  return ranges_.template Bottom<Range>()[index];
203  }
204 
205  template <typename InputStream>
206  void Parse(DecodedStream<InputStream>& ds) {
207  Allocator allocator;
208  Stack<Allocator> operandStack(&allocator, 256); // Frag
209  Stack<Allocator> operatorStack(&allocator, 256); // Operator
210  Stack<Allocator> atomCountStack(&allocator, 256); // unsigned (Atom per parenthesis)
211 
212  *atomCountStack.template Push<unsigned>() = 0;
213 
214  unsigned codepoint;
215  while (ds.Peek() != 0) {
216  switch (codepoint = ds.Take()) {
217  case '^':
218  anchorBegin_ = true;
219  break;
220 
221  case '$':
222  anchorEnd_ = true;
223  break;
224 
225  case '|':
226  while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() < kAlternation)
227  if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
228  return;
229  *operatorStack.template Push<Operator>() = kAlternation;
230  *atomCountStack.template Top<unsigned>() = 0;
231  break;
232 
233  case '(':
234  *operatorStack.template Push<Operator>() = kLeftParenthesis;
235  *atomCountStack.template Push<unsigned>() = 0;
236  break;
237 
238  case ')':
239  while (!operatorStack.Empty() && *operatorStack.template Top<Operator>() != kLeftParenthesis)
240  if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
241  return;
242  if (operatorStack.Empty())
243  return;
244  operatorStack.template Pop<Operator>(1);
245  atomCountStack.template Pop<unsigned>(1);
246  ImplicitConcatenation(atomCountStack, operatorStack);
247  break;
248 
249  case '?':
250  if (!Eval(operandStack, kZeroOrOne))
251  return;
252  break;
253 
254  case '*':
255  if (!Eval(operandStack, kZeroOrMore))
256  return;
257  break;
258 
259  case '+':
260  if (!Eval(operandStack, kOneOrMore))
261  return;
262  break;
263 
264  case '{':
265  {
266  unsigned n, m;
267  if (!ParseUnsigned(ds, &n))
268  return;
269 
270  if (ds.Peek() == ',') {
271  ds.Take();
272  if (ds.Peek() == '}')
273  m = kInfinityQuantifier;
274  else if (!ParseUnsigned(ds, &m) || m < n)
275  return;
276  }
277  else
278  m = n;
279 
280  if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}')
281  return;
282  ds.Take();
283  }
284  break;
285 
286  case '.':
287  PushOperand(operandStack, kAnyCharacterClass);
288  ImplicitConcatenation(atomCountStack, operatorStack);
289  break;
290 
291  case '[':
292  {
293  SizeType range;
294  if (!ParseRange(ds, &range))
295  return;
296  SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass);
297  GetState(s).rangeStart = range;
298  *operandStack.template Push<Frag>() = Frag(s, s, s);
299  }
300  ImplicitConcatenation(atomCountStack, operatorStack);
301  break;
302 
303  case '\\': // Escape character
304  if (!CharacterEscape(ds, &codepoint))
305  return; // Unsupported escape character
306  // fall through to default
307 
308  default: // Pattern character
309  PushOperand(operandStack, codepoint);
310  ImplicitConcatenation(atomCountStack, operatorStack);
311  }
312  }
313 
314  while (!operatorStack.Empty())
315  if (!Eval(operandStack, *operatorStack.template Pop<Operator>(1)))
316  return;
317 
318  // Link the operand to matching state.
319  if (operandStack.GetSize() == sizeof(Frag)) {
320  Frag* e = operandStack.template Pop<Frag>(1);
321  Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0));
322  root_ = e->start;
323 
324 #if RAPIDJSON_REGEX_VERBOSE
325  printf("root: %d\n", root_);
326  for (SizeType i = 0; i < stateCount_ ; i++) {
327  State& s = GetState(i);
328  printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint);
329  }
330  printf("\n");
331 #endif
332  }
333 
334  // Preallocate buffer for SearchWithAnchoring()
335  RAPIDJSON_ASSERT(stateSet_ == 0);
336  if (stateCount_ > 0) {
337  stateSet_ = static_cast<unsigned*>(states_.GetAllocator().Malloc(GetStateSetSize()));
338  state0_.template Reserve<SizeType>(stateCount_);
339  state1_.template Reserve<SizeType>(stateCount_);
340  }
341  }
342 
343  SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) {
344  State* s = states_.template Push<State>();
345  s->out = out;
346  s->out1 = out1;
347  s->codepoint = codepoint;
348  s->rangeStart = kRegexInvalidRange;
349  return stateCount_++;
350  }
351 
352  void PushOperand(Stack<Allocator>& operandStack, unsigned codepoint) {
353  SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint);
354  *operandStack.template Push<Frag>() = Frag(s, s, s);
355  }
356 
357  void ImplicitConcatenation(Stack<Allocator>& atomCountStack, Stack<Allocator>& operatorStack) {
358  if (*atomCountStack.template Top<unsigned>())
359  *operatorStack.template Push<Operator>() = kConcatenation;
360  (*atomCountStack.template Top<unsigned>())++;
361  }
362 
363  SizeType Append(SizeType l1, SizeType l2) {
364  SizeType old = l1;
365  while (GetState(l1).out != kRegexInvalidState)
366  l1 = GetState(l1).out;
367  GetState(l1).out = l2;
368  return old;
369  }
370 
371  void Patch(SizeType l, SizeType s) {
372  for (SizeType next; l != kRegexInvalidState; l = next) {
373  next = GetState(l).out;
374  GetState(l).out = s;
375  }
376  }
377 
378  bool Eval(Stack<Allocator>& operandStack, Operator op) {
379  switch (op) {
380  case kConcatenation:
381  RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2);
382  {
383  Frag e2 = *operandStack.template Pop<Frag>(1);
384  Frag e1 = *operandStack.template Pop<Frag>(1);
385  Patch(e1.out, e2.start);
386  *operandStack.template Push<Frag>() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex));
387  }
388  return true;
389 
390  case kAlternation:
391  if (operandStack.GetSize() >= sizeof(Frag) * 2) {
392  Frag e2 = *operandStack.template Pop<Frag>(1);
393  Frag e1 = *operandStack.template Pop<Frag>(1);
394  SizeType s = NewState(e1.start, e2.start, 0);
395  *operandStack.template Push<Frag>() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex));
396  return true;
397  }
398  return false;
399 
400  case kZeroOrOne:
401  if (operandStack.GetSize() >= sizeof(Frag)) {
402  Frag e = *operandStack.template Pop<Frag>(1);
403  SizeType s = NewState(kRegexInvalidState, e.start, 0);
404  *operandStack.template Push<Frag>() = Frag(s, Append(e.out, s), e.minIndex);
405  return true;
406  }
407  return false;
408 
409  case kZeroOrMore:
410  if (operandStack.GetSize() >= sizeof(Frag)) {
411  Frag e = *operandStack.template Pop<Frag>(1);
412  SizeType s = NewState(kRegexInvalidState, e.start, 0);
413  Patch(e.out, s);
414  *operandStack.template Push<Frag>() = Frag(s, s, e.minIndex);
415  return true;
416  }
417  return false;
418 
419  default:
420  RAPIDJSON_ASSERT(op == kOneOrMore);
421  if (operandStack.GetSize() >= sizeof(Frag)) {
422  Frag e = *operandStack.template Pop<Frag>(1);
423  SizeType s = NewState(kRegexInvalidState, e.start, 0);
424  Patch(e.out, s);
425  *operandStack.template Push<Frag>() = Frag(e.start, s, e.minIndex);
426  return true;
427  }
428  return false;
429  }
430  }
431 
432  bool EvalQuantifier(Stack<Allocator>& operandStack, unsigned n, unsigned m) {
433  RAPIDJSON_ASSERT(n <= m);
434  RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag));
435 
436  if (n == 0) {
437  if (m == 0) // a{0} not support
438  return false;
439  else if (m == kInfinityQuantifier)
440  Eval(operandStack, kZeroOrMore); // a{0,} -> a*
441  else {
442  Eval(operandStack, kZeroOrOne); // a{0,5} -> a?
443  for (unsigned i = 0; i < m - 1; i++)
444  CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a?
445  for (unsigned i = 0; i < m - 1; i++)
446  Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a?
447  }
448  return true;
449  }
450 
451  for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a
452  CloneTopOperand(operandStack);
453 
454  if (m == kInfinityQuantifier)
455  Eval(operandStack, kOneOrMore); // a{3,} -> a a a+
456  else if (m > n) {
457  CloneTopOperand(operandStack); // a{3,5} -> a a a a
458  Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a?
459  for (unsigned i = n; i < m - 1; i++)
460  CloneTopOperand(operandStack); // a{3,5} -> a a a a? a?
461  for (unsigned i = n; i < m; i++)
462  Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a?
463  }
464 
465  for (unsigned i = 0; i < n - 1; i++)
466  Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a?
467 
468  return true;
469  }
470 
471  static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; }
472 
473  void CloneTopOperand(Stack<Allocator>& operandStack) {
474  const Frag src = *operandStack.template Top<Frag>(); // Copy constructor to prevent invalidation
475  SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_)
476  State* s = states_.template Push<State>(count);
477  memcpy(s, &GetState(src.minIndex), count * sizeof(State));
478  for (SizeType j = 0; j < count; j++) {
479  if (s[j].out != kRegexInvalidState)
480  s[j].out += count;
481  if (s[j].out1 != kRegexInvalidState)
482  s[j].out1 += count;
483  }
484  *operandStack.template Push<Frag>() = Frag(src.start + count, src.out + count, src.minIndex + count);
485  stateCount_ += count;
486  }
487 
488  template <typename InputStream>
489  bool ParseUnsigned(DecodedStream<InputStream>& ds, unsigned* u) {
490  unsigned r = 0;
491  if (ds.Peek() < '0' || ds.Peek() > '9')
492  return false;
493  while (ds.Peek() >= '0' && ds.Peek() <= '9') {
494  if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295
495  return false; // overflow
496  r = r * 10 + (ds.Take() - '0');
497  }
498  *u = r;
499  return true;
500  }
501 
502  template <typename InputStream>
503  bool ParseRange(DecodedStream<InputStream>& ds, SizeType* range) {
504  bool isBegin = true;
505  bool negate = false;
506  int step = 0;
507  SizeType start = kRegexInvalidRange;
508  SizeType current = kRegexInvalidRange;
509  unsigned codepoint;
510  while ((codepoint = ds.Take()) != 0) {
511  if (isBegin) {
512  isBegin = false;
513  if (codepoint == '^') {
514  negate = true;
515  continue;
516  }
517  }
518 
519  switch (codepoint) {
520  case ']':
521  if (start == kRegexInvalidRange)
522  return false; // Error: nothing inside []
523  if (step == 2) { // Add trailing '-'
524  SizeType r = NewRange('-');
525  RAPIDJSON_ASSERT(current != kRegexInvalidRange);
526  GetRange(current).next = r;
527  }
528  if (negate)
529  GetRange(start).start |= kRangeNegationFlag;
530  *range = start;
531  return true;
532 
533  case '\\':
534  if (ds.Peek() == 'b') {
535  ds.Take();
536  codepoint = 0x0008; // Escape backspace character
537  }
538  else if (!CharacterEscape(ds, &codepoint))
539  return false;
540  // fall through to default
541 
542  default:
543  switch (step) {
544  case 1:
545  if (codepoint == '-') {
546  step++;
547  break;
548  }
549  // fall through to step 0 for other characters
550 
551  case 0:
552  {
553  SizeType r = NewRange(codepoint);
554  if (current != kRegexInvalidRange)
555  GetRange(current).next = r;
556  if (start == kRegexInvalidRange)
557  start = r;
558  current = r;
559  }
560  step = 1;
561  break;
562 
563  default:
564  RAPIDJSON_ASSERT(step == 2);
565  GetRange(current).end = codepoint;
566  step = 0;
567  }
568  }
569  }
570  return false;
571  }
572 
573  SizeType NewRange(unsigned codepoint) {
574  Range* r = ranges_.template Push<Range>();
575  r->start = r->end = codepoint;
576  r->next = kRegexInvalidRange;
577  return rangeCount_++;
578  }
579 
580  template <typename InputStream>
581  bool CharacterEscape(DecodedStream<InputStream>& ds, unsigned* escapedCodepoint) {
582  unsigned codepoint;
583  switch (codepoint = ds.Take()) {
584  case '^':
585  case '$':
586  case '|':
587  case '(':
588  case ')':
589  case '?':
590  case '*':
591  case '+':
592  case '.':
593  case '[':
594  case ']':
595  case '{':
596  case '}':
597  case '\\':
598  *escapedCodepoint = codepoint; return true;
599  case 'f': *escapedCodepoint = 0x000C; return true;
600  case 'n': *escapedCodepoint = 0x000A; return true;
601  case 'r': *escapedCodepoint = 0x000D; return true;
602  case 't': *escapedCodepoint = 0x0009; return true;
603  case 'v': *escapedCodepoint = 0x000B; return true;
604  default:
605  return false; // Unsupported escape character
606  }
607  }
608 
609  template <typename InputStream>
610  bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) const {
611  RAPIDJSON_ASSERT(IsValid());
612  DecodedStream<InputStream> ds(is);
613 
614  state0_.Clear();
615  Stack<Allocator> *current = &state0_, *next = &state1_;
616  const size_t stateSetSize = GetStateSetSize();
617  std::memset(stateSet_, 0, stateSetSize);
618 
619  bool matched = AddState(*current, root_);
620  unsigned codepoint;
621  while (!current->Empty() && (codepoint = ds.Take()) != 0) {
622  std::memset(stateSet_, 0, stateSetSize);
623  next->Clear();
624  matched = false;
625  for (const SizeType* s = current->template Bottom<SizeType>(); s != current->template End<SizeType>(); ++s) {
626  const State& sr = GetState(*s);
627  if (sr.codepoint == codepoint ||
628  sr.codepoint == kAnyCharacterClass ||
629  (sr.codepoint == kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint)))
630  {
631  matched = AddState(*next, sr.out) || matched;
632  if (!anchorEnd && matched)
633  return true;
634  }
635  if (!anchorBegin)
636  AddState(*next, root_);
637  }
638  internal::Swap(current, next);
639  }
640 
641  return matched;
642  }
643 
644  size_t GetStateSetSize() const {
645  return (stateCount_ + 31) / 32 * 4;
646  }
647 
648  // Return whether the added states is a match state
649  bool AddState(Stack<Allocator>& l, SizeType index) const {
650  RAPIDJSON_ASSERT(index != kRegexInvalidState);
651 
652  const State& s = GetState(index);
653  if (s.out1 != kRegexInvalidState) { // Split
654  bool matched = AddState(l, s.out);
655  return AddState(l, s.out1) || matched;
656  }
657  else if (!(stateSet_[index >> 5] & (1 << (index & 31)))) {
658  stateSet_[index >> 5] |= (1 << (index & 31));
659  *l.template PushUnsafe<SizeType>() = index;
660  }
661  return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation.
662  }
663 
664  bool MatchRange(SizeType rangeIndex, unsigned codepoint) const {
665  bool yes = (GetRange(rangeIndex).start & kRangeNegationFlag) == 0;
666  while (rangeIndex != kRegexInvalidRange) {
667  const Range& r = GetRange(rangeIndex);
668  if (codepoint >= (r.start & ~kRangeNegationFlag) && codepoint <= r.end)
669  return yes;
670  rangeIndex = r.next;
671  }
672  return !yes;
673  }
674 
675  Stack<Allocator> states_;
676  Stack<Allocator> ranges_;
677  SizeType root_;
678  SizeType stateCount_;
679  SizeType rangeCount_;
680 
681  static const unsigned kInfinityQuantifier = ~0u;
682 
683  // For SearchWithAnchoring()
684  uint32_t* stateSet_; // allocated by states_.GetAllocator()
685  mutable Stack<Allocator> state0_;
686  mutable Stack<Allocator> state1_;
687  bool anchorBegin_;
688  bool anchorEnd_;
689 };
690 
691 typedef GenericRegex<UTF8<> > Regex;
692 
693 } // namespace internal
694 RAPIDJSON_NAMESPACE_END
695 
696 #ifdef __clang__
697 RAPIDJSON_DIAG_POP
698 #endif
699 
700 #ifdef _MSC_VER
701 RAPIDJSON_DIAG_POP
702 #endif
703 
704 #endif // RAPIDJSON_INTERNAL_REGEX_H_
Allocator
Concept for allocating, resizing and freeing memory block.
rapidjson::SizeType
unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition: rapidjson.h:380
RAPIDJSON_ASSERT
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition: rapidjson.h:402