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