openMSX
rapidsax.hh
Go to the documentation of this file.
1 #ifndef RAPIDSAX_HH
2 #define RAPIDSAX_HH
3 
4 // This code is _heavily_ based on RapidXml 1.13
5 // http://rapidxml.sourceforge.net/
6 //
7 // RapidXml is a very fast XML parser.
8 // http://xmlbench.sourceforge.net/results/benchmark200910/index.html
9 // One of the main reasons it can be this fast is that doesn't do any string
10 // copies. Instead the XML input data is modified in-place (e.g. for stuff like
11 // < replacements). Though this also means the output produced by the parser
12 // is tied to the lifetime of the XML input data.
13 //
14 // RapidXml produces a DOM-like output. This parser has a SAX-like interface.
15 
16 #include "one_of.hh"
17 #include "small_compare.hh"
18 #include <cassert>
19 #include <cstdint>
20 #include <string_view>
21 
22 namespace rapidsax {
23 
24 // Parse given XML text and call callback functions in the given handler.
25 // - XML text must be zero-terminated
26 // - Handler must implement the methods defined in NullHandler (below). An
27 // easy way to do this is to inherit from NullHandler and only reimplement
28 // the methods that you need.
29 // - The behavior of the parser can be fine-tuned with the FLAGS parameter,
30 // see below for more details.
31 // - When a parse error is encounter, an instance of ParseError is thrown.
32 // - The lifetime of the string_view's in the callback handler is the same as
33 // the lifetime of the input XML data (no string copies are made, instead
34 // the XML file is modified in-place and references to this data are passed).
35 template<int FLAGS, typename HANDLER> void parse(HANDLER& handler, char* xml);
36 
37 // When loading an XML file from disk, the buffer needs to be 8 bytes bigger
38 // than the filesize. The first of these bytes must be filled with zero
39 // (zero-terminate the xml data). The other bytes are only there to allow to
40 // read up-to 8 bytes past the end without triggering memory protection errors.
41 constexpr size_t EXTRA_BUFFER_SPACE = 8;
42 
43 
44 // Flags that influence parsing behavior. The flags can be OR'ed together.
45 
46 // Should XML entities like &lt; be expanded or not?
47 constexpr int noEntityTranslation = 0x1;
48 // Should leading and trailing whitespace be trimmed?
49 constexpr int trimWhitespace = 0x2;
50 // Should sequences of whitespace characters be replaced with a single
51 // space character?
52 constexpr int normalizeWhitespace = 0x4;
53 
54 
55 // Callback handler with all empty implementations (can be used as a base
56 // class in case you only need to reimplement a few of the methods).
58 {
59 public:
60  // Called when an opening XML tag is encountered.
61  // 'name' is the name of the XML tag.
62  void start(std::string_view /*name*/) {}
63 
64  // Called when a XML tag is closed.
65  // Note: the parser does currently not check whether the name of the
66  // opening nd closing tags matches.
67  void stop() {}
68 
69  // Called when text inside a tag is parsed.
70  // XML entities are replaced (optional)
71  // Whitespace is (optionally) trimmed or normalized.
72  // This method is not called for an empty text string.
73  // (Unlike other SAX parsers) the whole text string is always
74  // passed in a single chunk (so no need to concatenate this text
75  // with previous chunks in the callback).
76  void text(std::string_view /*text*/) {}
77 
78  // Called for each parsed attribute.
79  // Attributes can occur inside xml tags or inside XML declarations.
80  void attribute(std::string_view /*name*/, std::string_view /*value*/) {}
81 
82  // Called for parsed CDATA sections.
83  void cdata(std::string_view /*value*/) {}
84 
85  // Called when a XML comment (<!-- ... -->) is parsed.
86  void comment(std::string_view /*value*/) {}
87 
88  // Called when XML declaration (<?xml .. ?>) is parsed.
89  // Inside a XML declaration there can be attributes.
90  void declarationStart() {}
91  void declAttribute(std::string_view /*name*/, std::string_view /*value*/) {}
92  void declarationStop() {}
93 
94  // Called when the <!DOCTYPE ..> is parsed.
95  void doctype(std::string_view /*text*/) {}
96 
97  // Called when XML processing instructions (<? .. ?>) are parsed.
98  void procInstr(std::string_view /*target*/, std::string_view /*instr*/) {}
99 };
100 
101 
103 {
104 public:
105  ParseError(const char* what_, char* where_)
106  : m_what(what_)
107  , m_where(where_)
108  {
109  }
110 
111  [[nodiscard]] const char* what() const { return m_what; }
112  [[nodiscard]] char* where() const { return m_where; }
113 
114 private:
115  const char* m_what;
116  char* m_where;
117 };
118 
119 
120 namespace internal {
121 
122 extern const uint8_t lutChar [256]; // Character class
123 extern const uint8_t lutDigits[256]; // Digits
124 
125 // Detect whitespace character (space \n \r \t)
127  [[nodiscard]] static bool test(char ch) { return (lutChar[uint8_t(ch)] & 0x02) != 0; }
128 };
129 
130 // Detect node name character (anything but space \n \r \t / > ? \0)
131 struct NodeNamePred {
132  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x43); }
133 };
134 
135 // Detect attribute name character (anything but space \n \r \t / < > = ? ! \0)
137  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0xC7); }
138 };
139 
140 // Detect text character (PCDATA) (anything but < \0)
141 struct TextPred {
142  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x05); }
143 };
144 
145 // Detect text character (PCDATA) that does not require processing when ws
146 // normalization is disabled (anything but < \0 &)
148  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x0D); }
149 };
150 
151 // Detect text character (PCDATA) that does not require processing when ws
152 // normalizationis is enabled (anything but < \0 & space \n \r \t)
154  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x0F); }
155 };
156 
157 // Detect attribute value character, single quote (anything but ' \0)
158 struct AttPred1 {
159  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x11); }
160 };
161 // Detect attribute value character, double quote (anything but " \0)
162 struct AttPred2 {
163  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x21); }
164 };
165 
166 // Detect attribute value character, single quote, that does not require
167 // processing (anything but ' \0 &)
168 struct AttPurePred1 {
169  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x19); }
170 };
171 // Detect attribute value character, double quote, that does not require
172 // processing (anything but " \0 &)
173 struct AttPurePred2 {
174  [[nodiscard]] static bool test(char ch) { return !(lutChar[uint8_t(ch)] & 0x29); }
175 };
176 
177 // Insert coded character, using UTF8
178 static inline void insertUTF8char(char*& text, uint32_t code)
179 {
180  if (code < 0x80) { // 1 byte sequence
181  text[0] = char(code);
182  text += 1;
183  } else if (code < 0x800) {// 2 byte sequence
184  text[1] = char((code | 0x80) & 0xBF); code >>= 6;
185  text[0] = char (code | 0xC0);
186  text += 2;
187  } else if (code < 0x10000) { // 3 byte sequence
188  text[2] = char((code | 0x80) & 0xBF); code >>= 6;
189  text[1] = char((code | 0x80) & 0xBF); code >>= 6;
190  text[0] = char (code | 0xE0);
191  text += 3;
192  } else if (code < 0x110000) { // 4 byte sequence
193  text[3] = char((code | 0x80) & 0xBF); code >>= 6;
194  text[2] = char((code | 0x80) & 0xBF); code >>= 6;
195  text[1] = char((code | 0x80) & 0xBF); code >>= 6;
196  text[0] = char (code | 0xF0);
197  text += 4;
198  } else { // Invalid, only codes up to 0x10FFFF are allowed in Unicode
199  throw ParseError("invalid numeric character entity", text);
200  }
201 }
202 
203 template<char C0, char C1> [[nodiscard]] static inline bool next(const char* p)
204 {
205  return small_compare<C0, C1>(p);
206 }
207 template<char C0, char C1, char C2> [[nodiscard]] static inline bool next(const char* p)
208 {
209  return small_compare<C0, C1, C2>(p);
210 }
211 template<char C0, char C1, char C2, char C3> [[nodiscard]] static inline bool next(const char* p)
212 {
213  return small_compare<C0, C1, C2, C3>(p);
214 }
215 template<char C0, char C1, char C2, char C3, char C4, char C5>
216 [[nodiscard]] static inline bool next(const char* p)
217 {
218  return small_compare<C0, C1, C2, C3, C4, C5>(p);
219 }
220 
221 
222 // Skip characters until predicate evaluates to true
223 template<class StopPred> static inline void skip(char*& text)
224 {
225  char* tmp = text;
226  while (StopPred::test(*tmp)) ++tmp;
227  text = tmp;
228 }
229 
230 // Skip characters until predicate evaluates to true while doing the following:
231 // - replacing XML character entity references with proper characters
232 // (&apos; &amp; &quot; &lt; &gt; &#...;)
233 // - condensing whitespace sequences to single space character
234 template<class StopPred, class StopPredPure, int FLAGS>
235 [[nodiscard]] static inline char* skipAndExpand(char*& text)
236 {
237  // If entity translation, whitespace condense and whitespace
238  // trimming is disabled, use plain skip.
239  if ( (FLAGS & noEntityTranslation) &&
240  !(FLAGS & normalizeWhitespace) &&
241  !(FLAGS & trimWhitespace)) {
242  skip<StopPred>(text);
243  return text;
244  }
245 
246  // Use simple skip until first modification is detected
247  skip<StopPredPure>(text);
248 
249  // Use translation skip
250  char* src = text;
251  char* dest = src;
252  while (StopPred::test(*src)) {
253  // Test if replacement is needed
254  if (!(FLAGS & noEntityTranslation) &&
255  (src[0] == '&')) {
256  switch (src[1]) {
257  case 'a': // &amp; &apos;
258  if (next<'m','p',';'>(&src[2])) {
259  *dest = '&';
260  ++dest;
261  src += 5;
262  continue;
263  }
264  if (next<'p','o','s',';'>(&src[2])) {
265  *dest = '\'';
266  ++dest;
267  src += 6;
268  continue;
269  }
270  break;
271 
272  case 'q': // &quot;
273  if (next<'u','o','t',';'>(&src[2])) {
274  *dest = '"';
275  ++dest;
276  src += 6;
277  continue;
278  }
279  break;
280 
281  case 'g': // &gt;
282  if (next<'t',';'>(&src[2])) {
283  *dest = '>';
284  ++dest;
285  src += 4;
286  continue;
287  }
288  break;
289 
290  case 'l': // &lt;
291  if (next<'t',';'>(&src[2])) {
292  *dest = '<';
293  ++dest;
294  src += 4;
295  continue;
296  }
297  break;
298 
299  case '#': // &#...; - assumes ASCII
300  if (src[2] == 'x') {
301  uint32_t code = 0;
302  src += 3; // skip &#x
303  while (true) {
304  uint8_t digit = lutDigits[uint8_t(*src)];
305  if (digit == 0xFF) break;
306  code = code * 16 + digit;
307  ++src;
308  }
309  insertUTF8char(dest, code);
310  } else {
311  uint32_t code = 0;
312  src += 2; // skip &#
313  while (true) {
314  uint8_t digit = lutDigits[uint8_t(*src)];
315  if (digit == 0xFF) break;
316  code = code * 10 + digit;
317  ++src;
318  }
319  insertUTF8char(dest, code);
320  }
321  if (*src != ';') {
322  throw ParseError("expected ;", src);
323  }
324  ++src;
325  continue;
326 
327  default:
328  // Something else, ignore, just copy '&' verbatim
329  break;
330  }
331  }
332 
333  // Test if condensing is needed
334  if ((FLAGS & normalizeWhitespace) &&
335  (WhitespacePred::test(*src))) {
336  *dest++ = ' '; // single space in dest
337  ++src; // skip first whitespace char
338  // Skip remaining whitespace chars
339  while (WhitespacePred::test(*src)) ++src;
340  continue;
341  }
342 
343  // No replacement, only copy character
344  *dest++ = *src++;
345  }
346 
347  // Return new end
348  text = src;
349  return dest;
350 }
351 
352 static inline void skipBOM(char*& text)
353 {
354  if (next<char(0xEF), char(0xBB), char(0xBF)>(text)) {
355  text += 3; // skip utf-8 bom
356  }
357 }
358 
359 
360 template<int FLAGS, typename HANDLER> class Parser
361 {
362  HANDLER& handler;
363 
364 public:
365  Parser(HANDLER& handler_, char* text)
366  : handler(handler_)
367  {
368  skipBOM(text);
369  while (true) {
370  // Skip whitespace before node
371  skip<WhitespacePred>(text);
372  if (*text == 0) break;
373 
374  if (*text != '<') {
375  throw ParseError("expected <", text);
376  }
377  ++text; // skip '<'
378  parseNode(text);
379  }
380  }
381 
382 private:
383  // Parse XML declaration (<?xml...)
384  void parseDeclaration(char*& text)
385  {
386  handler.declarationStart();
387  skip<WhitespacePred>(text); // skip ws before attributes or ?>
388  parseAttributes(text, true);
389  handler.declarationStop();
390 
391  // skip ?>
392  if (!next<'?','>'>(text)) {
393  throw ParseError("expected ?>", text);
394  }
395  text += 2;
396  }
397 
398  // Parse XML comment (<!--...)
399  void parseComment(char*& text)
400  {
401  // Skip until end of comment
402  char* value = text; // remember value start
403  while (!next<'-','-','>'>(text)) {
404  if (text[0] == 0) {
405  throw ParseError("unexpected end of data", text);
406  }
407  ++text;
408  }
409  handler.comment(std::string_view(value, text - value));
410  text += 3; // skip '-->'
411  }
412 
413  void parseDoctype(char*& text)
414  {
415  char* value = text; // remember value start
416 
417  // skip to >
418  while (*text != '>') {
419  switch (*text) {
420  case '[': {
421  // If '[' encountered, scan for matching ending
422  // ']' using naive algorithm with depth. This
423  // works for all W3C test files except for 2
424  // most wicked.
425  ++text; // skip '['
426  int depth = 1;
427  while (depth > 0) {
428  switch (*text) {
429  case char('['): ++depth; break;
430  case char(']'): --depth; break;
431  case 0: throw ParseError(
432  "unexpected end of data", text);
433  }
434  ++text;
435  }
436  break;
437  }
438  case '\0':
439  throw ParseError("unexpected end of data", text);
440 
441  default:
442  ++text;
443  }
444  }
445 
446  handler.doctype(std::string_view(value, text - value));
447  text += 1; // skip '>'
448  }
449 
450  void parsePI(char*& text)
451  {
452  // Extract PI target name
453  char* name = text;
454  skip<NodeNamePred>(text);
455  char* nameEnd = text;
456  if (name == nameEnd) {
457  throw ParseError("expected PI target", text);
458  }
459 
460  // Skip whitespace between pi target and pi
461  skip<WhitespacePred>(text);
462 
463  // Skip to '?>'
464  char* value = text; // Remember start of pi
465  while (!next<'?','>'>(text)) {
466  if (*text == 0) {
467  throw ParseError("unexpected end of data", text);
468  }
469  ++text;
470  }
471  // Set pi value (verbatim, no entity expansion or ws normalization)
472  handler.procInstr(std::string_view(name, nameEnd - name),
473  std::string_view(value, text - value));
474  text += 2; // skip '?>'
475  }
476 
477  void parseText(char*& text, char* contentsStart)
478  {
479  // Backup to contents start if whitespace trimming is disabled
480  if (!(FLAGS & trimWhitespace)) {
481  text = contentsStart;
482  }
483  // Skip until end of data
484  char* value = text;
485  char* end = (FLAGS & normalizeWhitespace)
486  ? skipAndExpand<TextPred, TextPureWithWsPred, FLAGS>(text)
487  : skipAndExpand<TextPred, TextPureNoWsPred , FLAGS>(text);
488 
489  // Trim trailing whitespace; leading was already trimmed by
490  // whitespace skip after >
491  if (FLAGS & trimWhitespace) {
492  if (FLAGS & normalizeWhitespace) {
493  // Whitespace is already condensed to single
494  // space characters by skipping function, so
495  // just trim 1 char off the end.
496  if (end[-1] == ' ') {
497  --end;
498  }
499  } else {
500  // Backup until non-whitespace character is found
501  while (WhitespacePred::test(end[-1])) {
502  --end;
503  }
504  }
505  }
506 
507  // check next char before calling handler.text()
508  if (*text == '\0') {
509  throw ParseError("unexpected end of data", text);
510  } else {
511  assert(*text == '<');
512  }
513 
514  // Handle text, but only if non-empty.
515  auto len = end - value;
516  if (len) handler.text(std::string_view(value, len));
517  }
518 
519  void parseCdata(char*& text)
520  {
521  // Skip until end of cdata
522  char* value = text;
523  while (!next<']',']','>'>(text)) {
524  if (text[0] == 0) {
525  throw ParseError("unexpected end of data", text);
526  }
527  ++text;
528  }
529  handler.cdata(std::string_view(value, text - value));
530  text += 3; // skip ]]>
531  }
532 
533  void parseElement(char*& text)
534  {
535  // Extract element name
536  char* name = text;
537  skip<NodeNamePred>(text);
538  char* nameEnd = text;
539  if (name == nameEnd) {
540  throw ParseError("expected element name", text);
541  }
542  handler.start(std::string_view(name, nameEnd - name));
543 
544  skip<WhitespacePred>(text); // skip ws before attributes or >
545  parseAttributes(text, false);
546 
547  // Determine ending type
548  if (*text == '>') {
549  ++text;
550  parseNodeContents(text);
551  } else if (*text == '/') {
552  handler.stop();
553  ++text;
554  if (*text != '>') {
555  throw ParseError("expected >", text);
556  }
557  ++text;
558  } else {
559  throw ParseError("expected >", text);
560  }
561  }
562 
563  // Determine node type, and parse it
564  void parseNode(char*& text)
565  {
566  switch (text[0]) {
567  case '?': // <?...
568  ++text; // skip ?
569  // Note: this doesn't detect mixed case (xMl), does
570  // that matter?
571  if ((next<'x','m','l'>(text) ||
572  next<'X','M','L'>(text)) &&
573  WhitespacePred::test(text[3])) {
574  // '<?xml ' - xml declaration
575  text += 4; // skip 'xml '
576  parseDeclaration(text);
577  } else {
578  parsePI(text);
579  }
580  break;
581 
582  case '!': // <!...
583  // Parse proper subset of <! node
584  switch (text[1]) {
585  case '-': // <!-
586  if (text[2] == '-') {
587  // '<!--' - xml comment
588  text += 3; // skip '!--'
589  parseComment(text);
590  return;
591  }
592  break;
593 
594  case '[': // <![
595  if (next<'C','D','A','T','A','['>(&text[2])) {
596  // '<![CDATA[' - cdata
597  text += 8; // skip '![CDATA['
598  parseCdata(text);
599  return;
600  }
601  break;
602 
603  case 'D': // <!D
604  if (next<'O','C','T','Y','P','E'>(&text[2]) &&
605  WhitespacePred::test(text[8])) {
606  // '<!DOCTYPE ' - doctype
607  text += 9; // skip '!DOCTYPE '
608  parseDoctype(text);
609  return;
610  }
611  break;
612  }
613  // Attempt to skip other, unrecognized types starting with <!
614  ++text; // skip !
615  while (*text != '>') {
616  if (*text == 0) {
617  throw ParseError(
618  "unexpected end of data", text);
619  }
620  ++text;
621  }
622  ++text; // skip '>'
623  break;
624 
625  default: // <...
626  parseElement(text);
627  break;
628  }
629  }
630 
631  // Parse contents of the node - children, data etc.
632  void parseNodeContents(char*& text)
633  {
634  while (true) {
635  char* contentsStart = text; // start before ws is skipped
636  skip<WhitespacePred>(text); // Skip ws between > and contents
637 
638  switch (*text) {
639  case '<': // Node closing or child node
640 afterText: // After parseText() jump here instead of continuing
641  // the loop, because skipping whitespace is unnecessary.
642  if (text[1] == '/') {
643  // Node closing
644  text += 2; // skip '</'
645  skip<NodeNamePred>(text);
646  // TODO validate closing tag??
647  handler.stop();
648  // Skip remaining whitespace after node name
649  skip<WhitespacePred>(text);
650  if (*text != '>') {
651  throw ParseError("expected >", text);
652  }
653  ++text; // skip '>'
654  return;
655  } else {
656  // Child node
657  ++text; // skip '<'
658  parseNode(text);
659  }
660  break;
661 
662  case '\0':
663  throw ParseError("unexpected end of data", text);
664 
665  default:
666  parseText(text, contentsStart);
667  goto afterText;
668  }
669  }
670  }
671 
672  // Parse XML attributes of the node
673  void parseAttributes(char*& text, bool declaration)
674  {
675  // For all attributes
676  while (AttributeNamePred::test(*text)) {
677  // Extract attribute name
678  char* name = text;
679  ++text; // Skip first character of attribute name
680  skip<AttributeNamePred>(text);
681  char* nameEnd = text;
682  if (name == nameEnd) {
683  throw ParseError("expected attribute name", name);
684  }
685 
686  skip<WhitespacePred>(text); // skip ws after name
687  if (*text != '=') {
688  throw ParseError("expected =", text);
689  }
690  ++text; // skip =
691  skip<WhitespacePred>(text); // skip ws after =
692 
693  // Skip quote and remember if it was ' or "
694  char quote = *text;
695  if (quote != one_of('\'', '"')) {
696  throw ParseError("expected ' or \"", text);
697  }
698  ++text;
699 
700  // Extract attribute value and expand char refs in it
701  // No whitespace normalization in attributes
702  constexpr int FLAGS2 = FLAGS & ~normalizeWhitespace;
703  char* value = text;
704  char* valueEnd = (quote == '\'')
705  ? skipAndExpand<AttPred1, AttPurePred1, FLAGS2>(text)
706  : skipAndExpand<AttPred2, AttPurePred2, FLAGS2>(text);
707  // Make sure that end quote is present
708  // check before calling handler.xxx()
709  if (*text != quote) {
710  throw ParseError("expected ' or \"", text);
711  }
712  ++text; // skip quote
713 
714  if (!declaration) {
715  handler.attribute(std::string_view(name, nameEnd - name),
716  std::string_view(value, valueEnd - value));
717  } else {
718  handler.declAttribute(std::string_view(name, nameEnd - name),
719  std::string_view(value, valueEnd - value));
720  }
721 
722  skip<WhitespacePred>(text); // skip ws after value
723  }
724  }
725 };
726 
727 } // namespace internal
728 
729 template<int FLAGS, typename HANDLER>
730 inline void parse(HANDLER& handler, char* xml)
731 {
732  internal::Parser<FLAGS, HANDLER> parser(handler, xml);
733 }
734 
735 } // namespace rapidsax
736 
737 #endif
one_of.hh
rapidsax::NullHandler::text
void text(std::string_view)
Definition: rapidsax.hh:76
rapidsax::internal::AttPred2
Definition: rapidsax.hh:162
rapidsax::internal::Parser::Parser
Parser(HANDLER &handler_, char *text)
Definition: rapidsax.hh:365
rapidsax::internal::AttributeNamePred::test
static bool test(char ch)
Definition: rapidsax.hh:137
rapidsax::NullHandler::stop
void stop()
Definition: rapidsax.hh:67
rapidsax::internal::AttPred2::test
static bool test(char ch)
Definition: rapidsax.hh:163
rapidsax::ParseError::ParseError
ParseError(const char *what_, char *where_)
Definition: rapidsax.hh:105
rapidsax::NullHandler
Definition: rapidsax.hh:57
rapidsax::internal::lutChar
const uint8_t lutChar[256]
Definition: rapidsax.cc:14
rapidsax::internal::AttributeNamePred
Definition: rapidsax.hh:136
rapidsax::internal::AttPurePred2
Definition: rapidsax.hh:173
t
TclObject t
Definition: TclObject_test.cc:264
rapidsax::internal::lutDigits
const uint8_t lutDigits[256]
Definition: rapidsax.cc:36
rapidsax::internal::TextPureWithWsPred
Definition: rapidsax.hh:153
rapidsax::internal::TextPureNoWsPred::test
static bool test(char ch)
Definition: rapidsax.hh:148
rapidsax::noEntityTranslation
constexpr int noEntityTranslation
Definition: rapidsax.hh:47
rapidsax::internal::Parser
Definition: rapidsax.hh:360
rapidsax::internal::WhitespacePred
Definition: rapidsax.hh:126
rapidsax::internal::AttPurePred1
Definition: rapidsax.hh:168
rapidsax::internal::AttPurePred1::test
static bool test(char ch)
Definition: rapidsax.hh:169
one_of
Definition: one_of.hh:7
rapidsax::internal::NodeNamePred
Definition: rapidsax.hh:131
rapidsax::NullHandler::cdata
void cdata(std::string_view)
Definition: rapidsax.hh:83
rapidsax::normalizeWhitespace
constexpr int normalizeWhitespace
Definition: rapidsax.hh:52
small_compare.hh
rapidsax::internal::TextPred
Definition: rapidsax.hh:141
rapidsax::parse
void parse(HANDLER &handler, char *xml)
Definition: rapidsax.hh:730
rapidsax::ParseError
Definition: rapidsax.hh:102
rapidsax::NullHandler::attribute
void attribute(std::string_view, std::string_view)
Definition: rapidsax.hh:80
rapidsax::internal::AttPurePred2::test
static bool test(char ch)
Definition: rapidsax.hh:174
rapidsax::NullHandler::start
void start(std::string_view)
Definition: rapidsax.hh:62
rapidsax::internal::AttPred1::test
static bool test(char ch)
Definition: rapidsax.hh:159
rapidsax
Definition: rapidsax.cc:3
rapidsax::internal::NodeNamePred::test
static bool test(char ch)
Definition: rapidsax.hh:132
rapidsax::internal::AttPred1
Definition: rapidsax.hh:158
rapidsax::ParseError::what
const char * what() const
Definition: rapidsax.hh:111
rapidsax::internal::WhitespacePred::test
static bool test(char ch)
Definition: rapidsax.hh:127
rapidsax::EXTRA_BUFFER_SPACE
constexpr size_t EXTRA_BUFFER_SPACE
Definition: rapidsax.hh:41
rapidsax::NullHandler::declarationStop
void declarationStop()
Definition: rapidsax.hh:92
rapidsax::trimWhitespace
constexpr int trimWhitespace
Definition: rapidsax.hh:49
rapidsax::NullHandler::comment
void comment(std::string_view)
Definition: rapidsax.hh:86
rapidsax::ParseError::where
char * where() const
Definition: rapidsax.hh:112
rapidsax::NullHandler::declAttribute
void declAttribute(std::string_view, std::string_view)
Definition: rapidsax.hh:91
rapidsax::NullHandler::declarationStart
void declarationStart()
Definition: rapidsax.hh:90
rapidsax::NullHandler::procInstr
void procInstr(std::string_view, std::string_view)
Definition: rapidsax.hh:98
rapidsax::internal::TextPureNoWsPred
Definition: rapidsax.hh:147
rapidsax::internal::TextPureWithWsPred::test
static bool test(char ch)
Definition: rapidsax.hh:154
rapidsax::NullHandler::doctype
void doctype(std::string_view)
Definition: rapidsax.hh:95
rapidsax::internal::TextPred::test
static bool test(char ch)
Definition: rapidsax.hh:142