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__
23RAPIDJSON_DIAG_PUSH
24RAPIDJSON_DIAG_OFF(padded)
25RAPIDJSON_DIAG_OFF(switch-enum)
26RAPIDJSON_DIAG_OFF(implicit-fallthrough)
27#endif
28
29#ifdef __GNUC__
30RAPIDJSON_DIAG_PUSH
31RAPIDJSON_DIAG_OFF(effc++)
32#if __GNUC__ >= 7
33RAPIDJSON_DIAG_OFF(implicit-fallthrough)
34#endif
35#endif
36
37#ifdef _MSC_VER
38RAPIDJSON_DIAG_PUSH
39RAPIDJSON_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
46RAPIDJSON_NAMESPACE_BEGIN
47namespace internal {
48
49///////////////////////////////////////////////////////////////////////////////
50// GenericRegex
51
52static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1
53static 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*/
87template <typename Encoding, typename Allocator = CrtAllocator>
88class GenericRegex {
89public:
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
129private:
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
691typedef GenericRegex<UTF8<> > Regex;
692
693} // namespace internal
694RAPIDJSON_NAMESPACE_END
695
696#ifdef __clang__
697RAPIDJSON_DIAG_POP
698#endif
699
700#ifdef _MSC_VER
701RAPIDJSON_DIAG_POP
702#endif
703
704#endif // RAPIDJSON_INTERNAL_REGEX_H_
Concept for allocating, resizing and freeing memory block.
#define RAPIDJSON_ASSERT(x)
Assertion.
Definition: rapidjson.h:402
unsigned SizeType
Size type (for string lengths, array sizes, etc.)
Definition: rapidjson.h:380