BDE 4.39.x Production Release
Loading...
Searching...
No Matches
bdljsn_tokenizer.h
Go to the documentation of this file.
1/// @file bdljsn_tokenizer.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdljsn_tokenizer.h -*-C++-*-
8#ifndef INCLUDED_BDLJSN_TOKENIZER
9#define INCLUDED_BDLJSN_TOKENIZER
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdljsn_tokenizer bdljsn_tokenizer
15/// @brief Provide a tokenizer for extracting JSON data from a `streambuf`.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdljsn
19/// @{
20/// @addtogroup bdljsn_tokenizer
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdljsn_tokenizer-purpose"> Purpose</a>
25/// * <a href="#bdljsn_tokenizer-classes"> Classes </a>
26/// * <a href="#bdljsn_tokenizer-description"> Description </a>
27/// * <a href="#bdljsn_tokenizer-strict-conformance"> Strict Conformance </a>
28/// * <a href="#bdljsn_tokenizer-usage"> Usage </a>
29/// * <a href="#bdljsn_tokenizer-example-1-extracting-json-data-into-an-object"> Example 1: Extracting JSON Data into an Object </a>
30///
31/// # Purpose {#bdljsn_tokenizer-purpose}
32/// Provide a tokenizer for extracting JSON data from a `streambuf`.
33///
34/// # Classes {#bdljsn_tokenizer-classes}
35///
36/// - bdljsn::Tokenizer: tokenizer for parsing JSON data from a `streambuf`
37///
38/// @see baljsn_decoder
39///
40/// # Description {#bdljsn_tokenizer-description}
41/// This component provides a class, `bdljsn::Tokenizer`, that
42/// traverses data stored in a `bsl::streambuf` one node at a time and provides
43/// clients access to the data associated with that node, including its type and
44/// data value. Client code can use the `reset` function to associate a
45/// `bsl::streambuf` containing JSON data with a tokenizer object and then call
46/// the `advanceToNextToken` function to extract individual data values.
47///
48/// This `class` was created to be used by other components in the `bdljsn` and
49/// `baljsn` packages and in most cases clients should use the
50/// @ref bdljsn_jsonutil , @ref baljsn_decoder , or @ref bdljsn_datumutil components
51/// instead of using this `class`.
52///
53/// On malformed JSON, tokenization may fail before the end of input is reached,
54/// but not all such errors are detected. In particular, callers should check
55/// that closing brackets and braces match opening ones.
56///
57/// ## Strict Conformance {#bdljsn_tokenizer-strict-conformance}
58///
59///
60/// The `bdljsn::Tokenizer` class allows several convenient variances from the
61/// JSON grammar as described in RFC8259 (see
62/// https://www.rfc-editor.org/rfc/rfc8259). If strict conformance is needed,
63/// users can put the tokenizer into strict conformance mode (see
64/// `setConformanceMode`). The behavioral differences are each controlled by
65/// options. The differences between a default constructed tokenizer and one in
66/// strict mode are:
67/// @code
68/// Option Default Strict
69/// -------------------------------- ------- ------
70/// allowConsecutiveSeparators true false
71/// allowFormFeedAsWhitespace true false
72/// allowHeterogenousArrays true true
73/// allowNonUtf8StringLiterals true false
74/// allowStandAloneValues true true
75/// allowTrailingTopLevelComma true false
76/// allowUnescapedControlCharacters true false
77/// @endcode
78/// The default-constructed `bdljsn::Tokenizer` is created having the options
79/// shown above (in the "Default" column) and a `conformanceMode` of
80/// `bdljsn::e_RELAXED`. Accordingly, users are free to change any of the
81/// option values to any combination that may be needed; however, once a
82/// tokenizer is set to strict mode the options are set to the values shown
83/// above (in the "Strict" column) and changes are not allowed (doing so leads
84/// to undefined behavior) unless the conformance mode is again set to relaxed.
85///
86/// ## Usage {#bdljsn_tokenizer-usage}
87///
88///
89/// This section illustrates intended use of this component.
90///
91/// ## Example 1: Extracting JSON Data into an Object {#bdljsn_tokenizer-example-1-extracting-json-data-into-an-object}
92///
93///
94/// For this example, we will use `bdljsn::Tokenizer` to read each node in a
95/// JSON document and populate a simple `Employee` object.
96///
97/// First, we will define the JSON data that the tokenizer will traverse over:
98/// @code
99/// const char *INPUT = " {\n"
100/// " \"street\" : \"Lexington Ave\",\n"
101/// " \"state\" : \"New York\",\n"
102/// " \"zipcode\" : \"10022-1331\",\n"
103/// " \"floorCount\" : 55\n"
104/// " }";
105/// @endcode
106/// Next, we will construct populate a `streambuf` with this data:
107/// @code
108/// bdlsb::FixedMemInStreamBuf isb(INPUT, bsl::strlen(INPUT));
109/// @endcode
110/// Then, we will create a `bdljsn::Tokenizer` object and associate the above
111/// streambuf with it:
112/// @code
113/// bdljsn::Tokenizer tokenizer;
114/// tokenizer.reset(&isb);
115/// @endcode
116/// Next, we will create an address record type and object.
117/// @code
118/// struct Address {
119/// bsl::string d_street;
120/// bsl::string d_state;
121/// bsl::string d_zipcode;
122/// int d_floorCount;
123/// } address = { "", "", "", 0 };
124/// @endcode
125/// Then, we will traverse the JSON data one node at a time:
126/// @code
127/// // Read '{'
128///
129/// int rc = tokenizer.advanceToNextToken();
130/// assert(!rc);
131///
132/// bdljsn::Tokenizer::TokenType token = tokenizer.tokenType();
133/// assert(bdljsn::Tokenizer::e_START_OBJECT == token);
134///
135/// rc = tokenizer.advanceToNextToken();
136/// assert(!rc);
137/// token = tokenizer.tokenType();
138///
139/// // Continue reading elements till '}' is encountered
140///
141/// while (bdljsn::Tokenizer::e_END_OBJECT != token) {
142/// assert(bdljsn::Tokenizer::e_ELEMENT_NAME == token);
143///
144/// // Read element name
145///
146/// bslstl::StringRef nodeValue;
147/// rc = tokenizer.value(&nodeValue);
148/// assert(!rc);
149///
150/// bsl::string elementName = nodeValue;
151///
152/// // Read element value
153///
154/// int rc = tokenizer.advanceToNextToken();
155/// assert(!rc);
156///
157/// token = tokenizer.tokenType();
158/// assert(bdljsn::Tokenizer::e_ELEMENT_VALUE == token);
159///
160/// rc = tokenizer.value(&nodeValue);
161/// assert(!rc);
162///
163/// // Extract the simple type with the data
164///
165/// if (elementName == "street") {
166/// rc = bdljsn::StringUtil::readString(&address.d_street, nodeValue);
167/// assert(!rc);
168/// }
169/// else if (elementName == "state") {
170/// rc = bdljsn::StringUtil::readString(&address.d_state, nodeValue);
171/// assert(!rc);
172/// }
173/// else if (elementName == "zipcode") {
174/// rc = bdljsn::StringUtil::readString(&address.d_zipcode, nodeValue);
175/// assert(!rc);
176/// }
177/// else if (elementName == "floorCount") {
178/// rc = bdljsn::NumberUtil::asInt(&address.d_floorCount, nodeValue);
179/// assert(!rc);
180/// }
181///
182/// rc = tokenizer.advanceToNextToken();
183/// assert(!rc);
184/// token = tokenizer.tokenType();
185/// }
186/// @endcode
187/// Finally, we will verify that the `address` aggregate has the correct values:
188/// @code
189/// assert("Lexington Ave" == address.d_street);
190/// assert("New York" == address.d_state);
191/// assert("10022-1331" == address.d_zipcode);
192/// assert(55 == address.d_floorCount);
193/// @endcode
194/// @}
195/** @} */
196/** @} */
197
198/** @addtogroup bdl
199 * @{
200 */
201/** @addtogroup bdljsn
202 * @{
203 */
204/** @addtogroup bdljsn_tokenizer
205 * @{
206 */
207
208#include <bdlscm_version.h>
209
211
212#include <bsls_alignedbuffer.h>
213#include <bsls_assert.h>
214#include <bsls_types.h>
215
216#include <bsl_ios.h>
217#include <bsl_streambuf.h>
218#include <bsl_string.h>
219#include <bsl_string_view.h>
220#include <bsl_vector.h>
221
222
223namespace bdljsn {
224
225 // ===============
226 // class Tokenizer
227 // ===============
228
229/// This `class` provides a mechanism for traversing JSON data stored in a
230/// `bsl::streambuf` one node at a time and allows clients to access the
231/// data associated with that node, including its type and data value.
232///
233/// See @ref bdljsn_tokenizer
235
236 public:
237 // TYPES
240
242 // This 'enum' lists all the possible token types.
243
244 e_BEGIN = 1, // starting token
245 e_ELEMENT_NAME, // element name
246 e_START_OBJECT, // start of an object ('{')
247 e_END_OBJECT, // end of an object ('}')
248 e_START_ARRAY, // start of an array ('[')
249 e_END_ARRAY, // end of an array (']')
250 e_ELEMENT_VALUE, // element value of a simple type
251 e_ERROR // error token
252#ifndef BDE_OMIT_INTERNAL_DEPRECATED
261#endif // BDE_OMIT_INTERNAL_DEPRECATED
262 };
263
264 enum { k_EOF = +1 };
265
270
271 private:
272 // PRIVATE TYPES
273 enum ContextType {
274 // This 'enum' lists the possible contexts that the tokenizer can be
275 // in.
276
277 e_NO_CONTEXT, // context stack is empty
278 e_OBJECT_CONTEXT, // object context
279 e_ARRAY_CONTEXT // array context
280 };
281
282 // One intermediate data buffer used for reading data from the stream, and
283 // another for the context state stack.
284
285 enum {
286 k_BUFSIZE = 1024 * 8,
287 k_MAX_STRING_SIZE = k_BUFSIZE - 1,
288
289 k_CONTEXTSTACKBUFSIZE = 256
290 };
291
292 // DATA
294 d_buffer; // string buffer
295
297 d_stackBuffer; // context stack buffer
298
300 d_allocator; // string allocator (owned)
301
303 d_stackAllocator; // context stack allocator (owned)
304
305 bsl::string d_stringBuffer; // string buffer
306
307 bsl::streambuf *d_streambuf_p; // streambuf (held, not owned)
308
309 bsl::size_t d_cursor; // current cursor
310
311 bsl::size_t d_valueBegin; // cursor for beginning of value
312
313 bsl::size_t d_valueEnd; // cursor for end of value
314
315 bsl::size_t d_valueIter; // cursor for iterating value
316
317 Uint64 d_readOffset; // the offset to the end of the
318 // current 'd_stringBuffer'
319 // relative to the start of the
320 // streambuf
321
322 TokenType d_tokenType; // token type
323
324 bsl::vector<char> d_contextStack; // context type stack
325
326 int d_readStatus; // 0 until EOF or an error is
327 // encountered, then indicates
328 // nature of error. Returned by
329 // 'readStatus'
330
331 int d_bufEndStatus; // status of last read from
332 // '*d_streambuf_p'. If non-zero,
333 // copied to 'd_readStatus' on next
334 // read attempt.
335
336 bool d_allowConsecutiveSeparators;
337 // option for allowing consecutive
338 // separators (i.e., ':', or ',')
339
340 bool d_allowFormFeedAsWhitespace;
341 // option for allowing '\f' as
342 // whitespace in addition to ' ',
343 // '\n', '\t', '\r', and '\v'.
344
345 bool d_allowHeterogenousArrays;
346 // option for allowing arrays of
347 // heterogeneous values
348
349 bool d_allowNonUtf8StringLiterals;
350 // Disables UTF-8 validation
351
352 bool d_allowStandAloneValues;
353 // option for allowing stand alone
354 // values
355
356 bool d_allowTrailingTopLevelComma;
357 // if 'true', allows '{},'
358
359 bool d_allowUnescapedControlCharacters;
360 // option for unescaped control
361 // characters in JSON strings.
362
363 ConformanceMode d_conformanceMode; // "relaxed" (default) or "strict"
364
365 // PRIVATE MANIPULATORS
366
367 /// Increase the size of the string buffer, `d_stringBuffer`, and then
368 /// append additional characters, from the internally-held `streambuf` (
369 /// `d_streambuf_p`) to the end of the current sequence of characters.
370 /// Return 0 on success and a non-zero value otherwise.
371 int expandBufferForLargeValue();
372
373 /// Extract the string value starting at the current data cursor and
374 /// update the value begin and end pointers to refer to the begin and
375 /// end of the extracted string. Return 0 on success and a non-zero
376 /// value otherwise.
377 int extractStringValue();
378
379 /// Move the current sequence of characters being tokenized to the front
380 /// of the internal string buffer, `d_stringBuffer`, and then append
381 /// additional characters, from the internally-held `streambuf`
382 /// (`d_streambuf_p`) to the end of that sequence up to a maximum
383 /// sequence length of `d_buffer.size()` characters. Return the number of bytes read from the `streambuf`.
384 ///
385 /// \note Note that if 0 is returned, it
386 /// may mean end of file or, if UTF-8 checking is set, that invalid
387 /// UTF-8 was encountered.
388 int moveValueCharsToStartAndReloadBuffer();
389
390 /// If the `d_contextStack` is empty, return `e_NO_CONTEXT`, otherwise
391 /// pop the top context from the `d_contextStack` stack, and return it.
392 ContextType popContext();
393
394 /// Push the specified `context` onto the `d_contextStack` stack.
395 void pushContext(ContextType context);
396
397 /// Reload the string buffer with new data read from the underlying
398 /// `streambuf` and overwriting the current buffer. After reading
399 /// update the cursor to the new read location. Return the number of
400 /// bytes read from the `streambuf`.
401 int reloadStringBuffer();
402
403 /// Skip all characters until a whitespace or a token character is
404 /// encountered and position the cursor onto the first such character.
405 /// Return 0 on success and a non-zero value otherwise.
406 int skipNonWhitespaceOrTillToken();
407
408 /// Skip all whitespace characters and position the cursor onto the
409 /// first non-whitespace character. Return 0 on success and a non-zero
410 /// value otherwise.
411 int skipWhitespace();
412
413 // PRIVATE ACCESSOR
414
415 /// If the `d_contextStack` is empty, return `e_NO_CONTEXT`, otherwise
416 /// return the top context from the `d_contextStack` stack without
417 /// popping.
418 ContextType context() const;
419
420 private:
421 // NOT IMPLEMENTED
422 Tokenizer(const Tokenizer&);
423 Tokenizer& operator=(const Tokenizer&);
424
425 public:
426 // CREATORS
427
428 /// Create a `Tokenizer` object. Optionally specify a `basicAllocator`
429 /// used to supply memory. If `basicAllocator` is 0, the currently
430 /// installed default allocator is used. By default, the
431 /// `conformanceMode` is `e_RELAXED` and the value of the `Tokenizer`
432 /// options are:
433 /// @code
434 /// allowConsecutiveSeparators() == true;
435 /// allowFormFeedAsWhitespace() == true;
436 /// allowHeterogeneousArrays() == true;
437 /// allowNonUtf8StringLiterals() == true;
438 /// allowStandAloneValues() == true;
439 /// allowTrailingTopLevelComma() == true;
440 /// allowUnescapedControlCharacters() == true;
441 /// @endcode
442 /// The `reset` method must be called before any calls to
443 /// `advanceToNextToken` or `resetStreamBufGetPointer`.
444 explicit Tokenizer(bslma::Allocator *basicAllocator = 0);
445
446 /// Destroy this object.
447 ~Tokenizer();
448
449 // MANIPULATORS
450
451 /// Move to the next token in the data steam. Return 0 on success and a
452 /// non-zero value otherwise. Each call to `advanceToNextToken`
453 /// invalidates the string references returned by the `value` accessor
454 /// for prior nodes. This function *may* fail to move to the next token
455 /// if doing so would advanced past a character sequence that is not
456 /// valid JSON, and is guaranteed to do so (fail to move) if `e_RELAXED != conformanceMode()`.
457 ///
458 /// \pre The behavior is undefined unless
459 /// `reset` has been called.
461
462 /// Reset this tokenizer to read data from the specified `streambuf`.
463 ///
464 /// \note Note that the reader will not be on a valid node until
465 /// `advanceToNextToken` is called.
466 /// \note Note that this function does not
467 /// change the the `conformanceMode` nor the values of any of the
468 /// individual token options:
469 /// * `allowConsecutiveSeparators`
470 /// * `allowFormFeedAsWhitespace`
471 /// * `allowHeterogenousArrays`
472 /// * `allowNonUtf8StringLiterals`
473 /// * `allowStandAloneValues`
474 /// * `allowTrailingTopLevelComma`
475 /// * `allowUnescapedControlCharacters`
476 void reset(bsl::streambuf *streambuf);
477
478 /// Reset the get pointer of the `streambuf` held by this object to
479 /// refer to the byte following the last processed byte, if the held
480 /// `streambuf` supports seeking, and return an error otherwise leaving
481 /// this object unchanged. Return 0 on success, and a non-zero value otherwise.
482 ///
483 /// \pre The behavior is undefined unless `reset` has been called.
484 ///
485 /// \note Note that after a successful function return users can read
486 /// data from the `streambuf` that was specified during `reset` from
487 /// where this object stopped. Also note that this call implies the end
488 /// of processing for this object and any subsequent methods invoked on
489 /// this object should only be done after calling `reset` and specifying
490 /// a new `streambuf`.
492
493 /// Set the `allowConsecutiveSeparators` option to the specified
494 /// `value` and return a non-`const` reference to this tokenizer. JSON
495 /// defines two separator tokens: the colon (`:`) and the comma (`,`).
496 /// If the `allowConsecutiveSeparators` value is `true` this tokenizer
497 /// will accept multiple consecutive sequences of a given separator
498 /// (e.g., `"a"::b, "c":::d` and `"a":b,, "c":d`, ,, "e":f') as if a
499 /// single separator had appeared (i.e., `"a":b, "c":d` and
500 /// `"a":b, "c":d`, "e":f', respectively). Otherwise the tokenizer
501 /// returns an error when multiple consecutive colons are found. By
502 /// default, the value of the `allowConsecutiveSeparators` option is `true`.
503 ///
504 /// \pre The behavior is undefined unless `e_RELAXED == conformanceMode()`.
505 ///
506 /// \note Note that consecutive sequences
507 /// using both tokens (e.g., `::,,::`) is always an error.
509
510 /// Set the `allowFormFeedAsWhitespace` option to the specified value
511 /// and return a non-`const` reference to this tokenizer. If the
512 /// `allowFormFeedAsWhitespace` value is `true` the formfeed character
513 /// (`\f`) is recognized as a whitespace character in addition to `\n`,
514 /// `\t`, `\r`, and `\v`. Otherwise, formfeed is disallowed as a
515 /// whitespace character.
517
518 /// Set the `allowHeterogenousArrays` option to the specified `value`
519 /// and return a non-`const` reference to this tokenizer. If the
520 /// `allowHeterogenousArrays` value is `true` this tokenizer will
521 /// successfully tokenize heterogeneous values within an array. If the
522 /// option's value is `false` then the tokenizer will return an error
523 /// for arrays having heterogeneous values. By default, the value of
524 /// the `allowHeterogenousArrays` option is `true`.
525 ///
526 /// \pre The behavior is undefined unless `e_RELAXED == conformanceMode()`.
528
529 /// Set the `allowNonUtf8StringLiterals` option to the specified `value`
530 /// and return a non-`const` reference to this tokenizer. If the
531 /// `allowNonUtf8StringLiterals` value is `false` this tokenizer will
532 /// check string literal tokens for invalid UTF-8, enter an error mode
533 /// if it encounters a string literal token that has any content that is
534 /// not UTF-8, and fail to advance to subsequent tokens until `reset` is
535 /// called. By default, the value of the `allowNonUtf8StringLiterals` option is `true`.
536 ///
537 /// \pre The behavior is undefined unless
538 /// `e_RELAXED == conformanceMode()`.
540
541 /// Set the `allowStandAloneValues` option to the specified `value` and
542 /// return a non-`const` reference to this tokenizer. If the
543 /// `allowStandAloneValues` value is `true` this tokenizer will
544 /// successfully tokenize JSON values (strings and numbers). If the
545 /// option's value is `false` then the tokenizer will only tokenize
546 /// complete JSON documents (JSON objects and arrays) and return an
547 /// error for stand alone JSON values. By default, the value of the
548 /// `allowStandAloneValues` option is `true`.
549 ///
550 /// \pre The behavior is undefined unless `e_RELAXED == conformanceMode()`.
552
553 /// Set the `allowTrailingTopLevelComma` option to the specified `value`
554 /// and return a non-`const` reference to this tokenizer. If the
555 /// `allowTrailingTopLevelComma` value is `true` this tokenizer will
556 /// successfully tokenize JSON values where a comma follows the
557 /// top-level JSON element. If the option's value is `false` then the
558 /// tokenizer will reject documents with such trailing commas, such as
559 /// `{},`. By default, the value of the `allowTrailingTopLevelComma` option is `true` for backwards compatibility.
560 ///
561 /// \note Note that a document
562 /// without any JSON elements is invalid whether or not it contains commas.
563 ///
564 /// \pre The behavior is undefined unless
565 /// `e_RELAXED == conformanceMode()`.
567
568 /// Set the `allowUnescapedControlCharacters` option of this tokenizer
569 /// to the specified `value`. If `true`, characters in the range
570 /// `[ 0x00 .. 0x1F ]` are allowed in JSON strings. If the option is
571 /// `false`, these characters must be represented by their six byte
572 /// escape sequences `[ \u0000 .. \u001F ]`. Several values in that
573 /// range are also (conveniently) represented by two byte sequences:
574 /// @code
575 /// \" quotation mark
576 /// \\ reverse solidus
577 /// \/ solidus
578 /// \b backspace
579 /// \f form feed
580 /// \n line feed
581 /// \r carriage return
582 /// \t tab
583 /// @endcode
584 /// The `DEL` control character (`0x7F`) is accepted even in strict
585 /// mode.
586 ///
587 ///
588 /// \pre The behavior is undefined unless `e_RELAXED == conformanceMode()`.
589 ///
590 /// \note Note that the representation of these byte sequences as C/C++ string
591 /// literals requires that the escape character itself must be escaped:
592 /// @code
593 /// "Hello,\\tworld\\n"; // Can always initialize a JSON string with
594 /// // containing tab and a newline
595 /// // escape sequences
596 /// // whether the option is set or not.
597 ///
598 /// "Hello,\tworld\n"; // When this option is 'true'.
599 /// // can also initialize a JSON string
600 /// // with an actual and newline characters.
601 /// @endcode
602 /// Also note that the two resulting strings do *not* compare equal.
604
605 /// Set the `conformanceMode` of this tokenizer to the specified `mode`
606 /// and return a non-`const` reference to this tokenizer. If `mode` is
607 /// `e_STRICT_20240119` the option values of this tokenizer are set to
608 /// be fully compliant with RFC8259 (see
609 /// https://www.rfc-editor.org/rfc/rfc8259)
610 ///
611 /// Specifically, those option values are:
612 /// @code
613 /// allowConsecutiveSeparators() == false;
614 /// allowFormFeedAsWhitespace() == false;
615 /// allowHeterogeneousArrays() == true;
616 /// allowNonUtf8StringLiterals() == false;
617 /// allowStandAloneValues() == true;
618 /// allowTrailingTopLevelComma() == false;
619 /// allowUnescapedControlCharacters() = false;
620 /// @endcode
621 /// Otherwise (i.e., `mode` is `e_RELAXED`), those option values can be set in any combination.
622 ///
623 /// \note Note that the behavior is undefined if
624 /// individual options are set when `conformanceMode` is *not*
625 /// `e_RELAXED`.
627
628 // ACCESSORS
629
630 /// Return the value of the `allowConsecutiveSeparators` option of this
631 /// tokenizer.
632 bool allowConsecutiveSeparators() const;
633
634 /// Return the value of the `allowFormFeedAsWhitespace` option of this
635 /// tokenizer.
636 bool allowFormFeedAsWhitespace() const;
637
638 /// Return the value of the `allowHeterogenousArrays` option of this
639 /// tokenizer.
640 bool allowHeterogenousArrays() const;
641
642 /// Return the value of the `allowNonUtf8StringLiterals` option of this
643 /// tokenizer.
644 bool allowNonUtf8StringLiterals() const;
645
646 /// Return the value of the `allowStandAloneValues` option of this
647 /// tokenizer.
648 bool allowStandAloneValues() const;
649
650 /// Return the value of the `allowTrailingTopLevelComma` option of this
651 /// tokenizer.
652 bool allowTrailingTopLevelComma() const;
653
654 /// Return the value of the `allowUnescapedControlCharacters` option of
655 /// this tokenizer.
657
658 /// Return the `conformanceMode` of this tokenizer.
660
661 /// Return the offset of the current octet being tokenized in the stream
662 /// supplied to `reset`, or if an error occurred, the position where the failed attempt to tokenize a token occurred.
663 ///
664 /// \note Note that this
665 /// operation is intended to provide additional information in the case
666 /// of an error.
668
669 /// Return the last read position relative to when `reset` was called.
670 ///
671 /// \note Note that `readOffset() >= currentPosition()` -- the `readOffset` is
672 /// the offset of the last octet read from the stream supplied to
673 /// `reset`, and is at or beyond the current position being tokenized.
675
676 /// Return the status of the last call to `reloadStringBuffer()`:
677 /// * 0 if `reloadStringBuffer()` has not been called or if a token was
678 /// successfully read.
679 /// * `k_EOF` (which is positive) if no data could be read before
680 /// reaching EOF.
681 /// * a negative value if the `allowNonUtf8StringLiterals` option is
682 /// `false` and a UTF-8 error occurred. The specific value returned
683 /// will be one of the enumerators of the
684 /// `bdlde::Utf8Util::ErrorStatus` `enum` type indicating the nature
685 /// of the UTF-8 error.
686 int readStatus() const;
687
688 /// Return the token type of the current token.
689 TokenType tokenType() const;
690
691 /// Load into the specified `data` the value of the specified token if
692 /// the current token's type is `e_ELEMENT_NAME` or `e_ELEMENT_VALUE` or
693 /// leave `data` unmodified otherwise. Return 0 on success and a non-zero value otherwise.
694 ///
695 /// \note Note that the returned `data` is only
696 /// valid until the next manipulator call on this object.
697 int value(bsl::string_view *data) const;
698};
699
700// ============================================================================
701// INLINE DEFINITIONS
702// ============================================================================
703
704// PRIVATE MANIPULATORS
705inline
706Tokenizer::ContextType Tokenizer::popContext()
707{
708 ContextType ret = e_NO_CONTEXT;
709
710 if (!d_contextStack.empty()) {
711 ret = static_cast<ContextType>(d_contextStack.back());
712 d_contextStack.pop_back();
713 }
714
715 return ret;
716}
717
718inline
719void Tokenizer::pushContext(ContextType context)
720{
721 d_contextStack.push_back(static_cast<char>(context));
722}
723
724// PRIVATE ACCESSOR
725inline
726Tokenizer::ContextType Tokenizer::context() const
727{
728 return d_contextStack.empty()
729 ? e_NO_CONTEXT
730 : static_cast<ContextType>(d_contextStack.back());
731}
732
733// CREATORS
734inline
735Tokenizer::Tokenizer(bslma::Allocator *basicAllocator)
736: d_allocator(d_buffer.buffer(), k_BUFSIZE, basicAllocator)
737, d_stackAllocator(d_stackBuffer.buffer(),
738 k_CONTEXTSTACKBUFSIZE,
739 basicAllocator)
740, d_stringBuffer(&d_allocator)
741, d_streambuf_p(0)
742, d_cursor(0)
743, d_valueBegin(0)
744, d_valueEnd(0)
745, d_valueIter(0)
746, d_readOffset(0)
747, d_tokenType(e_BEGIN)
748, d_contextStack(200, &d_stackAllocator)
749, d_readStatus(0)
750, d_bufEndStatus(0)
751, d_allowConsecutiveSeparators(true)
752, d_allowFormFeedAsWhitespace(true)
753, d_allowHeterogenousArrays(true)
754, d_allowNonUtf8StringLiterals(true)
755, d_allowStandAloneValues(true)
756, d_allowTrailingTopLevelComma(true)
757, d_allowUnescapedControlCharacters(true)
758, d_conformanceMode(e_RELAXED)
759{
760 d_stringBuffer.reserve(k_MAX_STRING_SIZE);
761 d_contextStack.clear();
762 pushContext(e_NO_CONTEXT);
763}
764
765inline
769
770// MANIPULATORS
771inline
772void Tokenizer::reset(bsl::streambuf *streambuf)
773{
774 d_streambuf_p = streambuf;
775 d_stringBuffer.clear();
776 d_cursor = 0;
777 d_valueBegin = 0;
778 d_valueEnd = 0;
779 d_valueIter = 0;
780 d_readOffset = 0;
781 d_tokenType = e_BEGIN;
782 d_readStatus = 0;
783 d_bufEndStatus = 0;
784
785 d_contextStack.clear();
786 pushContext(e_NO_CONTEXT);
787}
788
789inline
791{
792 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
793
794 d_allowConsecutiveSeparators = value;
795 return *this;
796}
797
798inline
800{
801 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
802
803 d_allowHeterogenousArrays = value;
804 return *this;
805}
806
807inline
809{
810 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
811
812 d_allowFormFeedAsWhitespace = value;
813 return *this;
814}
815
816inline
818{
819 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
820
821 d_allowNonUtf8StringLiterals = value;
822 return *this;
823}
824
825inline
827{
828 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
829
830 d_allowStandAloneValues = value;
831 return *this;
832}
833
834inline
836{
837 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
838
839 d_allowTrailingTopLevelComma = value;
840 return *this;
841}
842
843inline
845{
846 BSLS_ASSERT(e_RELAXED == d_conformanceMode);
847
848 d_allowUnescapedControlCharacters = value;
849 return *this;
850}
851
852inline
854{
855 d_conformanceMode = mode;
856
857 switch (mode) {
858 case e_RELAXED: {
859 } break;
860 case e_STRICT_20240119: {
861 d_allowConsecutiveSeparators = false;
862 d_allowFormFeedAsWhitespace = false;
863 d_allowHeterogenousArrays = true;
864 d_allowNonUtf8StringLiterals = false;
865 d_allowStandAloneValues = true;
866 d_allowTrailingTopLevelComma = false;
867 d_allowUnescapedControlCharacters = false;
868 } break;
869 default: {
870 BSLS_ASSERT_OPT(0 == "reached");
871 }
872 }
873 return *this;
874}
875
876// ACCESSORS
877inline
879{
880 return d_allowConsecutiveSeparators;
881}
882
883inline
885{
886 return d_allowFormFeedAsWhitespace;
887}
888
889inline
891{
892 return d_allowHeterogenousArrays;
893}
894
895inline
897{
898 return d_allowNonUtf8StringLiterals;
899}
900
901inline
903{
904 return d_allowStandAloneValues;
905}
906
907inline
909{
910 return d_allowTrailingTopLevelComma;
911}
912
913inline
915{
916 return d_allowUnescapedControlCharacters;
917}
918
919inline
921{
922 return d_conformanceMode;
923}
924
925inline
927{
928 return d_readOffset - d_stringBuffer.size() + d_cursor;
929}
930
931inline
933{
934 return d_readOffset;
935}
936
937inline
939{
940 return d_readStatus;
941}
942
943inline
945{
946 return d_tokenType;
947}
948
949} // close package namespace
950
951
952#endif // INCLUDED_BDLJSN_TOKENIZER
953
954// ----------------------------------------------------------------------------
955// Copyright 2022 Bloomberg Finance L.P.
956//
957// Licensed under the Apache License, Version 2.0 (the "License");
958// you may not use this file except in compliance with the License.
959// You may obtain a copy of the License at
960//
961// http://www.apache.org/licenses/LICENSE-2.0
962//
963// Unless required by applicable law or agreed to in writing, software
964// distributed under the License is distributed on an "AS IS" BASIS,
965// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
966// See the License for the specific language governing permissions and
967// limitations under the License.
968// ----------------------------- END-OF-FILE ----------------------------------
969
970/** @} */
971/** @} */
972/** @} */
Definition bdljsn_tokenizer.h:234
bool allowConsecutiveSeparators() const
Definition bdljsn_tokenizer.h:878
int resetStreamBufGetPointer()
bool allowFormFeedAsWhitespace() const
Definition bdljsn_tokenizer.h:884
bsls::Types::Uint64 Uint64
Definition bdljsn_tokenizer.h:239
~Tokenizer()
Destroy this object.
Definition bdljsn_tokenizer.h:766
TokenType tokenType() const
Return the token type of the current token.
Definition bdljsn_tokenizer.h:944
Tokenizer & setAllowConsecutiveSeparators(bool value)
Definition bdljsn_tokenizer.h:790
bool allowNonUtf8StringLiterals() const
Definition bdljsn_tokenizer.h:896
bsls::Types::Uint64 readOffset() const
Definition bdljsn_tokenizer.h:932
Tokenizer & setAllowHeterogenousArrays(bool value)
Definition bdljsn_tokenizer.h:799
Tokenizer & setAllowStandAloneValues(bool value)
Definition bdljsn_tokenizer.h:826
bsls::Types::IntPtr IntPtr
Definition bdljsn_tokenizer.h:238
Tokenizer & setAllowUnescapedControlCharacters(bool value)
Definition bdljsn_tokenizer.h:844
bool allowStandAloneValues() const
Definition bdljsn_tokenizer.h:902
Tokenizer & setAllowTrailingTopLevelComma(bool value)
Definition bdljsn_tokenizer.h:835
int readStatus() const
Definition bdljsn_tokenizer.h:938
ConformanceMode
Definition bdljsn_tokenizer.h:266
@ e_RELAXED
Definition bdljsn_tokenizer.h:267
@ e_STRICT_20240119
Definition bdljsn_tokenizer.h:268
Tokenizer & setAllowFormFeedAsWhitespace(bool value)
Definition bdljsn_tokenizer.h:808
ConformanceMode conformanceMode() const
Return the conformanceMode of this tokenizer.
Definition bdljsn_tokenizer.h:920
void reset(bsl::streambuf *streambuf)
Definition bdljsn_tokenizer.h:772
Tokenizer & setConformanceMode(ConformanceMode mode)
Definition bdljsn_tokenizer.h:853
bool allowUnescapedControlCharacters() const
Definition bdljsn_tokenizer.h:914
bool allowHeterogenousArrays() const
Definition bdljsn_tokenizer.h:890
Tokenizer & setAllowNonUtf8StringLiterals(bool value)
Definition bdljsn_tokenizer.h:817
bsls::Types::Uint64 currentPosition() const
Definition bdljsn_tokenizer.h:926
@ k_EOF
Definition bdljsn_tokenizer.h:264
TokenType
Definition bdljsn_tokenizer.h:241
@ e_ELEMENT_NAME
Definition bdljsn_tokenizer.h:245
@ e_END_OBJECT
Definition bdljsn_tokenizer.h:247
@ BAEJSN_END_ARRAY
Definition bdljsn_tokenizer.h:258
@ e_BEGIN
Definition bdljsn_tokenizer.h:244
@ BAEJSN_ELEMENT_NAME
Definition bdljsn_tokenizer.h:254
@ e_END_ARRAY
Definition bdljsn_tokenizer.h:249
@ e_ELEMENT_VALUE
Definition bdljsn_tokenizer.h:250
@ BAEJSN_START_ARRAY
Definition bdljsn_tokenizer.h:257
@ BAEJSN_END_OBJECT
Definition bdljsn_tokenizer.h:256
@ BAEJSN_START_OBJECT
Definition bdljsn_tokenizer.h:255
@ e_ERROR
Definition bdljsn_tokenizer.h:251
@ e_START_ARRAY
Definition bdljsn_tokenizer.h:248
@ BAEJSN_ERROR
Definition bdljsn_tokenizer.h:260
@ e_START_OBJECT
Definition bdljsn_tokenizer.h:246
@ BAEJSN_ELEMENT_VALUE
Definition bdljsn_tokenizer.h:259
int value(bsl::string_view *data) const
bool allowTrailingTopLevelComma() const
Definition bdljsn_tokenizer.h:908
Definition bdlma_bufferedsequentialallocator.h:266
Definition bslstl_stringview.h:471
Definition bslstl_string.h:1252
size_type size() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:7292
void reserve(size_type newCapacity)
Definition bslstl_string.h:6020
void clear() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:6043
reference back()
Definition bslstl_vector.h:2932
bool empty() const BSLS_KEYWORD_NOEXCEPT
Return true if this vector has size 0, and false otherwise.
Definition bslstl_vector.h:3034
Definition bslstl_vector.h:1120
void push_back(const VALUE_TYPE &value)
Definition bslstl_vector.h:4343
void swap(vector &other) BSLS_KEYWORD_NOEXCEPT_SPECIFICATION(AllocatorTraits void clear() BSLS_KEYWORD_NOEXCEPT
Definition bslstl_vector.h:1938
void pop_back()
Definition bslstl_vector.h:4375
Definition bslma_allocator.h:545
Definition bsls_alignedbuffer.h:262
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_ASSERT_OPT(X)
Definition bsls_assert.h:2045
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdljsn_error.h:142
unsigned long long Uint64
Definition bsls_types.h:139
std::ptrdiff_t IntPtr
Definition bsls_types.h:132