File: vtkResourceParser.h

package info (click to toggle)
paraview 5.13.2%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 544,220 kB
  • sloc: cpp: 3,374,605; ansic: 1,332,409; python: 150,381; xml: 122,166; sql: 65,887; sh: 7,317; javascript: 5,262; yacc: 4,417; java: 3,977; perl: 2,363; lex: 1,929; f90: 1,397; makefile: 170; objc: 153; tcl: 59; pascal: 50; fortran: 29
file content (562 lines) | stat: -rw-r--r-- 21,570 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#ifndef vtkResourceParser_h
#define vtkResourceParser_h

#include "vtkIOCoreModule.h" // For export macro
#include "vtkObject.h"
#include "vtkResourceStream.h" // For SeekDirection and vtkResourceStream
#include "vtkSmartPointer.h"   // For vtkSmartPointer

#include <array>       // for std::array
#include <cstdint>     // for std::int32_t
#include <cstdlib>     // for std::size_t
#include <functional>  // for std::function
#include <limits>      // for std::numeric_limits
#include <memory>      // for std::unique_ptr
#include <string>      // for std::string
#include <type_traits> // for SFINAE helpers

VTK_ABI_NAMESPACE_BEGIN

#ifndef __VTK_WRAP__ // do not wrap

/**
 * @brief Result of a vtkResouceParser parsing operation
 *
 * This enumeration gives information about what the parsing operation did.
 * When doing basic parsing, you may just need to check `result != vtkParseResult::Ok`.
 *
 * For more complex parsing, you can configure the parser to stop on newlines
 */
enum class vtkParseResult : std::int32_t
{
  Error = -1,      // Value not parsed because of type or formatting error
  Ok = 0,          // Value parsed successfully, no special status
  EndOfStream = 1, // No value parsed, stream reached its end
  EndOfLine = 2,   // No value parsed, this is an end of line
  Limit = 3,       // Value parsed successfully, limit has been reached
};

/**
 * @brief Helper class to perform formatted input from vtkResourceStream
 *
 * vtkResourceParser is a helper class that format input from an associated vtkResourceResource.
 * This class defines function to read integers, floats, booleans and strings. Other utility
 * functions such as ReadUntil or DiscardUntil are also available.
 *
 * Quick how to:
 * - Assign a stream to the parser using `SetStream`
 * - Perform input using one of the `Parse` overload
 * - Perform low level read using `Parse(char*, size)` overload
 * - Read data until a predicate is met using `ReadUntil`
 * - Discard data until a predicate is met using `DiscardUntil`
 * - Use `Seek` and `Tell` functions to modify/get cursor position including parser context
 * - Use `Reset` when the stream has been modified externally
 */
class VTKIOCORE_EXPORT vtkResourceParser : public vtkObject
{
public:
  /**
   * @brief predicate type used by `ReadUntil` and `DiscardUntil` functions
   */
  using PredicateType = std::function<bool(char c)>;

  ///@{
  /**
   * @brief Prebuild predicates for common cases
   *
   * DiscardNone: discard no character before parsing.
   * DiscardWhitespace: discard `\n`, `\r`, `\t`, `\v`, `\f` and spaces.
   * This is the default predicate.
   * DiscardNonAlphaNumeric: discard everything except [a-z], [A-Z] and [0-9].
   */
  static const PredicateType DiscardNone;
  static const PredicateType DiscardWhitespace;
  static const PredicateType DiscardNonAlphaNumeric;
  ///@}

  /**
   * @brief receiver type used by `ReadUntil` function
   */
  using DataReceiverType = std::function<void(const char* data, std::size_t size)>;

  static constexpr std::size_t NoLimit = (std::numeric_limits<std::size_t>::max)();

private:
  /**
   * @brief Internal class for parser context, contains the real implementation
   */
  class vtkInternals;

  /**
   * @brief Class for parser context, serves as a bridge between public API and real implementation
   */
  class VTKIOCORE_EXPORT vtkParserContext
  {
  public:
    vtkParserContext();
    ~vtkParserContext();
    vtkParserContext(const vtkParserContext&) = delete;
    vtkParserContext& operator=(const vtkParserContext&) = delete;

    /// @see ResourceParser#SetStream
    void SetStream(vtkResourceStream* stream);
    /// @see ResourceParser#GetStream
    vtkResourceStream* GetStream() const;

    /// @see ResourceParser#GetStopOnNewLine
    bool GetStopOnNewLine() const;
    /// @see ResourceParser#SetStopOnNewLine
    void SetStopOnNewLine(bool on);

    /// @see ResourceParser#Seek
    vtkTypeInt64 Seek(vtkTypeInt64 pos, vtkResourceStream::SeekDirection dir);
    /// @see ResourceParser#Tell
    vtkTypeInt64 Tell();
    /// @see ResourceParser#Read
    std::size_t Read(char* output, std::size_t size);
    /// @see ResourceParser#Reset
    void Reset();

    /// @see ResourceParser#Parse
    template <typename T>
    vtkParseResult Parse(T& output, const PredicateType& discardPred);

    /// @see ResourceParser#ReadUntil
    vtkParseResult ReadUntil(
      const PredicateType& discardPred, const DataReceiverType& receiver, std::size_t limit);
    /// @see ResourceParser#DiscardUntil
    vtkParseResult DiscardUntil(const PredicateType& discardPred);
    /// @see ResourceParser#ReadLine
    vtkParseResult ReadLine(const DataReceiverType& receiver, std::size_t limit);

    /// @see ResourceParser#PrintSelf
    void PrintSelf(ostream& os, vtkIndent indent);

  private:
    std::unique_ptr<vtkInternals> Impl;
  };

  /**
   * @brief Check if type is supported for parsing
   *
   * To support add a supported type you have to:
   * - Add it in this check
   * - Add extern template declaration below in this file
   * - Add extern template instantiation in source file
   * - Implement what is needed in vtkInternals
   */
  template <typename T>
  static constexpr bool IsSupported()
  {
    // Only remove references to check const and volatile
    using Type = typename std::remove_reference<T>::type;

    return std::is_same<Type, char>::value || std::is_same<Type, signed char>::value ||
      std::is_same<Type, unsigned char>::value || std::is_same<Type, short>::value ||
      std::is_same<Type, unsigned short>::value || std::is_same<Type, int>::value ||
      std::is_same<Type, unsigned int>::value || std::is_same<Type, long>::value ||
      std::is_same<Type, unsigned long>::value || std::is_same<Type, long long>::value ||
      std::is_same<Type, unsigned long long>::value || std::is_same<Type, float>::value ||
      std::is_same<Type, double>::value || std::is_same<Type, bool>::value ||
      std::is_same<Type, std::string>::value;
  }

public:
  vtkTypeMacro(vtkResourceParser, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;
  static vtkResourceParser* New();

  /**
   * @brief Set the stream to parse
   *
   * Automatically reset the parser state if `stream != this->GetStream()`.
   * Parsing starts at the stream position when set.
   *
   * @param stream a vtkResourceStream
   */
  void SetStream(vtkResourceStream* stream) { this->Context.SetStream(stream); }

  /**
   * @brief Get the parsed stream
   *
   * @return the vtkResourceStream used by this parser
   */
  vtkResourceStream* GetStream() const { return this->Context.GetStream(); }

  ///@{
  /**
   * @brief Specifies if the parser should handle newlines as a special token to stop on
   *
   * When is property is `true` the function `Parse` will break when encountering a new line.
   * When breaking will not modify output value and will return vtkParseResult::EndOfLine
   *
   * Default value: false
   */
  bool GetStopOnNewLine() const { return this->Context.GetStopOnNewLine(); }
  void SetStopOnNewLine(bool on) { this->Context.SetStopOnNewLine(on); }
  vtkBooleanMacro(StopOnNewLine, bool);
  ///@}

  /**
   * @brief Move stream cursor
   *
   * Calling `Read` or `Seek` on the stream associated to the parser may break the parser context
   * and result in unexpected behaviour. To prevent this, `Reset` must be called if  the stream
   * is externally modified before a `Parse`.
   *
   * This function will take into account the parser context.
   * This function will move the stream and reset the parser context if needed, as-if by calling
   * `this->GetStream()->Seek(pos, dir)` followed by `this->Reset()`, but may be more efficient.
   *
   * @param pos Seeked position, same as `vtkResourceStream::Seek`
   * @param dir Seek direction, same as `vtkResourceStream::Seek`
   * @return The position of the cursor from parser context, see `Tell`. -1 if associated
   * stream does not support seeking.
   */
  vtkTypeInt64 Seek(vtkTypeInt64 pos, vtkResourceStream::SeekDirection dir)
  {
    return this->Context.Seek(pos, dir);
  }

  /**
   * @brief Get stream cursor position from parser context
   *
   * `Tell()` will give the real position of the cursor from the parser context.
   * `vtkResourceParser` will buffer data read for the stream to parse it, this is the context.
   * Because of this, the stream position will always by "in advance" of the parser real input
   * position.
   * `this->Tell()` will always be lesser or equal to `this->GetStream()->Tell()`.
   *
   * @return The position of the cursor from parser context. -1 if associated
   * stream does not support seeking.
   */
  vtkTypeInt64 Tell() { return this->Context.Tell(); }

  /**
   * @brief Read data from the input stream
   *
   * Read at most `size` bytes from input stream. Read less than `size` bytes if EOS is reached.
   * If less than `size` bytes are read, bytes outside of [0; returnSize[ are not modified.
   * It is the equivalent of the `Read` function of `vtkResourceStream`,
   * but it takes parser context into account.
   *
   * @return the number of read bytes
   */
  std::size_t Read(char* output, std::size_t size) { return this->Context.Read(output, size); }

  /**
   * @brief Reset parser internal state
   *
   * This may be required in case the stream has been modified using `Seek`, `Read` or any other
   * subclass specific member function the will break the internal state, e.g. changing the input
   * file.
   * Using multiple parsers on the same stream is valid as long as each parser get reset before use
   * each time another one was used, and that only one parser is used concurrently.
   */
  void Reset() { this->Context.Reset(); }

  /**
   * @brief Main parsing function
   *
   * __**Parsing operation:**__
   *
   * The parsing operation is divided in 2 `Steps`:
   *
   * 1. Leading *discarded characters* are discarded:
   *    * A character is a *discarded characters* if `discardPred` returns `true`.
   *    * If no `discardPred` is specified, the default predicate is `DiscardWhitespace`
   *    * If StopOnNewLine is true, this function will return `vtkParseResult::EndOfLine` if it
   *      encounters a new line regardless of what `discardPred` returns for `\n` and `\r`.
   *    * If end of stream is reached, returns `vtkParseResult::EndOfStream`.
   * 2. The value is parsed using different algorithms, depending on its type, see below.
   *
   * `vtkParseResult::EndOfStream` will only be signaled if it is reached during step `1`.
   * If it is reached during step `2`, it will return the result of the decoding operation, and
   * return `vtkParseResult::EndOfStream` during the next Parse step `1`.
   *
   * ---
   *
   * __**Supported types:**__
   * Supported types are `char`, `signed char`, `unsigned char`, `short`, `unsigned short`, `int`,
   * `unsigned int`, `long`, `unsigned long`, `long long`, `unsigned long long`, `float`, `double`,
   * `bool` and `std::string`.
   *
   * `char` parsing will read a byte and will return the value as-is.
   *
   * `std::string` parsing reads characters until discardPred returns true. This last character is
   * not appended to the string nor discarded from input.
   *
   * Other types are parsed using ::vtkValueFromString.
   *
   * @param output A reference to the variable where the result will be written. Parsed type can
   * be determined from this.
   * @param discardPred The discard predicate to use during `Step 1`
   *
   * @return vtkParseResult::Error if parsing of value failed, in that case, the internal context
   * is not modified by `Step 2`, next `Parse` will try to parse the same data as long as the
   * discard predicate is semantically the same. vtkParseResult::EndOfLine if a new line is reached
   * during `Step 1`, the new line marker will be consumed. vtkParseResult::EndOfStream if no data
   * remains after `Step 1`. vtkParseResult::Ok otherwise.
   */
  template <typename T, typename std::enable_if<IsSupported<T>(), bool>::type = true>
  vtkParseResult Parse(T& output, const PredicateType& discardPred = DiscardWhitespace)
  {
    // Static check to prevent cryptic linker error
    static_assert(IsSupported<T>(), "Unsupported type given to Parse function");
    return this->Context.Parse(output, discardPred);
  }

  /**
   * @brief Read data from the input stream until the perdicate is met.
   *
   * @param discardPred function matching `bool(char c)` prototype of `PredicateType`.
   * @param receiver function matching the `void(const char* data, std::size_t size)` prototype
   * of `DataReceiverType`.
   * @param limit maximum amount of character to read, useful for statically sized buffers
   * (default: no limit)
   *
   * @return
   * vtkParseResult::EndOfStream if EOS is reached before pred is met or limit is reached.
   * vtkParseResult::Limit if limit is reached before pred is met.
   * vtkParseResult::Ok otherwise.
   */
  vtkParseResult ReadUntil(
    const PredicateType& discardPred, const DataReceiverType& receiver, std::size_t limit = NoLimit)
  {
    return this->Context.ReadUntil(discardPred, receiver, limit);
  }

  /**
   * Structure returned by Read*To functions
   */
  template <typename It>
  struct ReadToResult
  {
    /**
     * vtkParseResult::EndOfStream if EOS is reached before pred is met or limit is reached.
     * vtkParseResult::Limit if limit is reached before pred is met.
     * vtkParseResult::Ok otherwise.
     */
    vtkParseResult Result;

    /**
     * Iterator one past the last written value.
     */
    It Output;
  };

  /**
   * @brief Read data from the input stream to any output iterator until the perdicate is met.
   *
   * @param discardPred function matching `bool(char c)` prototype of `PredicateType`.
   * @param output Output iterator where data will be written
   * @param limit maximum amount of character to read, useful for statically sized buffers.
   * (default: no limit)
   *
   * @return A ReadToResult.
   */
  template <typename OutputIt>
  ReadToResult<OutputIt> ReadUntilTo(
    const PredicateType& discardPred, OutputIt output, std::size_t limit = NoLimit)
  {
    const auto result = this->ReadUntil(
      discardPred,
      [&output](const char* data, std::size_t size) mutable {
        for (std::size_t i{}; i < size; ++i)
        {
          *output++ = data[i];
        }
      },
      limit);

    return ReadToResult<OutputIt>{ result, output };
  }

  /**
   * @brief Read data from the input stream to any output range until the perdicate is met.
   *
   * @param discardPred function matching `bool(char c)` prototype of `PredicateType`
   * @param begin Forward iterator where data will be written (range begin)
   * @param end Forward iterator one past the last available output space (range end)
   *
   * @return A ReadToResult
   */
  template <typename ForwardIt>
  ReadToResult<ForwardIt> ReadUntilTo(
    const PredicateType& discardPred, ForwardIt begin, ForwardIt end)
  {
    return this->ReadUntilTo(discardPred, begin, std::distance(begin, end));
  }

  /**
   * @brief Discard data from the input stream until the perdicate is met.
   *
   * @param pred function matching `bool(char c)` prototype of `PredicateType`
   *
   * vtkParseResult::EndOfStream if EOS is reached before pred is met.
   * vtkParseResult::Ok otherwise.
   */
  vtkParseResult DiscardUntil(const PredicateType& pred)
  {
    return this->Context.DiscardUntil(pred);
  }

  /**
   * @brief Read an entire line from the input stream
   *
   * This function is similar to `std::getline` or `std::fgets`.
   * This function handles both `\r`, `\r\n` and `\n`.
   * The new line marker, either `\r`, `\r\n` or `\n`, will be discarded. i.e. won't be passed to
   * receiver nor kept in input stream.
   *
   * Return value will be false only if the stream does not contains any characters:
   * - `\n`, `\r` and `\r\n` will return true, after calling receiver once with size == 0.
   * - `""` will return false without calling receiver at all
   *
   * When limit is reached right before an end of line identifier, it won't be discarded:
   * - `"abc\n"` with `limit == 3` will give `"abc"` to receiver
   * and keep `"\n"` for the next operation.
   *
   * @param receiver function matching the `void(const char* data, std::size_t size)` prototype
   * of `DataReceiverType`.
   * @param limit maximum amount of character to extract
   *
   * @return
   * vtkParseResult::EndOfStream if EOS is reached before any character is read,
   * vtkParseResult::Limit if limit is reached before an end of line marker,
   * vtkParseResult::EndOfLine otherwise.
   * This function never returns vtkParseResult::Ok, vtkParseResult::EndOfLine indicates success.
   */
  vtkParseResult ReadLine(const DataReceiverType& receiver, std::size_t limit = NoLimit)
  {
    return this->Context.ReadLine(receiver, limit);
  }

  /**
   * @brief Read an entire line from the input stream
   *
   * Behaves like ReadLine(const DataReceiverType& receiver, std::size_t limit) except that the
   * output data is written to given std::string. This is the closest function to std::getline.
   *
   * @param output std::string to write line to, it is automatically cleared and resized
   * @param limit maximum amount of character to extract
   *
   * @return
   * vtkParseResult::EndOfStream if EOS is reached before pred is met or limit is reached,
   * vtkParseResult::Limit if limit is reached before pred is met,
   * vtkParseResult::EndOfLine otherwise.
   * This function never returns vtkParseResult::Ok, vtkParseResult::EndOfLine indicates success.
   */
  template <typename Allocator>
  vtkParseResult ReadLine(
    std::basic_string<char, std::char_traits<char>, Allocator>& output, std::size_t limit = NoLimit)
  {
    output.clear();

    return this->ReadLine(
      [&output](const char* data, std::size_t size) { output.append(data, size); }, limit);
  }

  /**
   * @brief Read an entire line from the input stream
   *
   * Behaves like ReadLine(const DataReceiverType& receiver, std::size_t limit) except that the
   * output data is written to given output iterator.
   *
   * @param output Output iterator to write line to
   * @param limit maximum amount of character to extract
   *
   * @return A ReadUntilToResult.
   * This function never returns vtkParseResult::Ok, vtkParseResult::EndOfLine indicates success.
   */
  template <typename OutputIt>
  ReadToResult<OutputIt> ReadLineTo(OutputIt output, std::size_t limit = NoLimit)
  {
    const auto result = this->ReadLine(
      [&output](const char* data, std::size_t size) {
        for (std::size_t i{}; i < size; ++i)
        {
          *output++ = data[i];
        }
      },
      limit);

    return ReadToResult<OutputIt>{ result, output };
  }

  /**
   * @brief Read an entire line from the input stream
   *
   * Behaves like ReadLine(const DataReceiverType& receiver, std::size_t limit) except that the
   * output data is written to given range and limit is determined by end.
   *
   * @param begin Forward iterator where data will be written (range begin)
   * @param end Forward iterator one past the last available output space (range end)
   *
   * @return A ReadUntilToResult.
   * This function never returns vtkParseResult::Ok, vtkParseResult::EndOfLine indicates success.
   */
  template <typename ForwardIt>
  ReadToResult<ForwardIt> ReadLineTo(ForwardIt begin, ForwardIt end)
  {
    return this->ReadLineTo(begin, std::distance(begin, end));
  }

  /**
   * @brief Discard a line from the input stream.
   *
   * @param limit maximum amount of character to discard
   *
   * vtkParseResult::EndOfStream if EOS is reached before any character is discarded,
   * vtkParseResult::Limit if limit is reached before an end of line marker,
   * vtkParseResult::EndOfLine otherwise.
   * This function never returns vtkParseResult::Ok, vtkParseResult::EndOfLine indicates success.
   */
  vtkParseResult DiscardLine(std::size_t limit = NoLimit)
  {
    return this->ReadLine([](const char*, std::size_t) {}, limit);
  }

protected:
  /**
   * @brief Constructor
   */
  vtkResourceParser() = default;
  ~vtkResourceParser() override = default;
  vtkResourceParser(const vtkResourceParser&) = delete;
  vtkResourceParser& operator=(const vtkResourceParser&) = delete;

private:
  vtkParserContext Context;
};

#define DECLARE_PARSE_EXTERN_TEMPLATE(type)                                                        \
  extern template VTKIOCORE_EXPORT vtkParseResult                                                  \
  vtkResourceParser::vtkParserContext::Parse<type>(type&, const PredicateType& discardPred)

// Declare explicit instantiation for all supported types
DECLARE_PARSE_EXTERN_TEMPLATE(char);
DECLARE_PARSE_EXTERN_TEMPLATE(signed char);
DECLARE_PARSE_EXTERN_TEMPLATE(unsigned char);
DECLARE_PARSE_EXTERN_TEMPLATE(short);
DECLARE_PARSE_EXTERN_TEMPLATE(unsigned short);
DECLARE_PARSE_EXTERN_TEMPLATE(int);
DECLARE_PARSE_EXTERN_TEMPLATE(unsigned int);
DECLARE_PARSE_EXTERN_TEMPLATE(long);
DECLARE_PARSE_EXTERN_TEMPLATE(unsigned long);
DECLARE_PARSE_EXTERN_TEMPLATE(long long);
DECLARE_PARSE_EXTERN_TEMPLATE(unsigned long long);
DECLARE_PARSE_EXTERN_TEMPLATE(float);
DECLARE_PARSE_EXTERN_TEMPLATE(double);
DECLARE_PARSE_EXTERN_TEMPLATE(bool);
DECLARE_PARSE_EXTERN_TEMPLATE(std::string);

#undef DECLARE_PARSE_EXTERN_TEMPLATE

#endif

VTK_ABI_NAMESPACE_END

#endif