The open source OpenXR runtime
1// MIT License
2//
3// Copyright(c) 2016 Matthias Moeller
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files(the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions :
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23#ifndef __TYTI_STEAM_VDF_PARSER_H__
24#define __TYTI_STEAM_VDF_PARSER_H__
25
26#include <algorithm>
27#include <fstream>
28#include <functional>
29#include <iterator>
30#include <map>
31#include <memory>
32#include <unordered_map>
33#include <unordered_set>
34#include <utility>
35#include <vector>
36
37#include <exception>
38#include <system_error>
39
40// for wstring support
41#include <locale>
42#include <string>
43
44// internal
45#include <stack>
46
47// VS < 2015 has only partial C++11 support
48#if defined(_MSC_VER) && _MSC_VER < 1900
49#ifndef CONSTEXPR
50#define CONSTEXPR
51#endif
52
53#ifndef NOEXCEPT
54#define NOEXCEPT
55#endif
56#else
57#ifndef CONSTEXPR
58#define CONSTEXPR constexpr
59#define TYTI_UNDEF_CONSTEXPR
60#endif
61
62#ifndef NOEXCEPT
63#define NOEXCEPT noexcept
64#define TYTI_UNDEF_NOEXCEPT
65#endif
66
67#endif
68
69namespace tyti
70{
71namespace vdf
72{
73namespace detail
74{
75///////////////////////////////////////////////////////////////////////////
76// Helper functions selecting the right encoding (char/wchar_T)
77///////////////////////////////////////////////////////////////////////////
78
79template <typename T> struct literal_macro_help
80{
81 static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT
82 {
83 return c;
84 }
85 static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT
86 {
87 return c;
88 }
89};
90
91template <> struct literal_macro_help<wchar_t>
92{
93 static CONSTEXPR const wchar_t *result(const char *,
94 const wchar_t *wc) NOEXCEPT
95 {
96 return wc;
97 }
98 static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT
99 {
100 return wc;
101 }
102};
103#define TYTI_L(type, text) \
104 vdf::detail::literal_macro_help<type>::result(text, L##text)
105
106inline std::string string_converter(const std::string &w) NOEXCEPT { return w; }
107
108// utility wrapper to adapt locale-bound facets for wstring/wbuffer convert
109// from cppreference
110template <class Facet> struct deletable_facet : Facet
111{
112 template <class... Args>
113 deletable_facet(Args &&...args) : Facet(std::forward<Args>(args)...)
114 {
115 }
116 ~deletable_facet() {}
117};
118
119inline std::string
120string_converter(const std::wstring &w) // todo: use us-locale
121{
122 std::wstring_convert<
123 deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>>
124 conv1;
125 return conv1.to_bytes(w);
126}
127
128///////////////////////////////////////////////////////////////////////////
129// Writer helper functions
130///////////////////////////////////////////////////////////////////////////
131
132template <typename charT> class tabs
133{
134 const size_t t;
135
136 public:
137 explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {}
138 std::basic_string<charT> print() const
139 {
140 return std::basic_string<charT>(t, TYTI_L(charT, '\t'));
141 }
142 inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT
143 {
144 return tabs(t + i);
145 }
146};
147
148template <typename oStreamT>
149oStreamT &operator<<(oStreamT &s, const tabs<typename oStreamT::char_type> t)
150{
151 s << t.print();
152 return s;
153}
154} // end namespace detail
155
156///////////////////////////////////////////////////////////////////////////
157// Interface
158///////////////////////////////////////////////////////////////////////////
159
160/// custom objects and their corresponding write functions
161
162/// basic object node. Every object has a name and can contains attributes saved
163/// as key_value pairs or childrens
164template <typename CharT> struct basic_object
165{
166 typedef CharT char_type;
167 std::basic_string<char_type> name;
168 std::unordered_map<std::basic_string<char_type>,
169 std::basic_string<char_type>>
170 attribs;
171 std::unordered_map<std::basic_string<char_type>,
172 std::shared_ptr<basic_object<char_type>>>
173 childs;
174
175 void add_attribute(std::basic_string<char_type> key,
176 std::basic_string<char_type> value)
177 {
178 attribs.emplace(std::move(key), std::move(value));
179 }
180 void add_child(std::unique_ptr<basic_object<char_type>> child)
181 {
182 std::shared_ptr<basic_object<char_type>> obj{child.release()};
183 childs.emplace(obj->name, obj);
184 }
185 void set_name(std::basic_string<char_type> n) { name = std::move(n); }
186};
187
188template <typename CharT> struct basic_multikey_object
189{
190 typedef CharT char_type;
191 std::basic_string<char_type> name;
192 std::unordered_multimap<std::basic_string<char_type>,
193 std::basic_string<char_type>>
194 attribs;
195 std::unordered_multimap<std::basic_string<char_type>,
196 std::shared_ptr<basic_multikey_object<char_type>>>
197 childs;
198
199 void add_attribute(std::basic_string<char_type> key,
200 std::basic_string<char_type> value)
201 {
202 attribs.emplace(std::move(key), std::move(value));
203 }
204 void add_child(std::unique_ptr<basic_multikey_object<char_type>> child)
205 {
206 std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()};
207 childs.emplace(obj->name, obj);
208 }
209 void set_name(std::basic_string<char_type> n) { name = std::move(n); }
210};
211
212typedef basic_object<char> object;
213typedef basic_object<wchar_t> wobject;
214typedef basic_multikey_object<char> multikey_object;
215typedef basic_multikey_object<wchar_t> wmultikey_object;
216
217struct Options
218{
219 bool strip_escape_symbols;
220 bool ignore_all_platform_conditionals;
221 bool ignore_includes;
222
223 Options()
224 : strip_escape_symbols(true), ignore_all_platform_conditionals(false),
225 ignore_includes(false)
226 {
227 }
228};
229
230// forward decls
231// forward decl
232template <typename OutputT, typename iStreamT>
233OutputT read(iStreamT &inStream, const Options &opt = Options{});
234
235/** \brief writes given object tree in vdf format to given stream.
236Output is prettyfied, using tabs
237*/
238template <typename oStreamT, typename T>
239void write(oStreamT &s, const T &r,
240 const detail::tabs<typename oStreamT::char_type> tab =
241 detail::tabs<typename oStreamT::char_type>(0))
242{
243 typedef typename oStreamT::char_type charT;
244 using namespace detail;
245 s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab
246 << TYTI_L(charT, "{\n");
247 for (const auto &i : r.attribs)
248 s << tab + 1 << TYTI_L(charT, '"') << i.first
249 << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n");
250 for (const auto &i : r.childs)
251 if (i.second)
252 write(s, *i.second, tab + 1);
253 s << tab << TYTI_L(charT, "}\n");
254}
255
256namespace detail
257{
258template <typename iStreamT>
259std::basic_string<typename iStreamT::char_type> read_file(iStreamT &inStream)
260{
261 // cache the file
262 typedef typename iStreamT::char_type charT;
263 std::basic_string<charT> str;
264 inStream.seekg(0, std::ios::end);
265 str.resize(static_cast<size_t>(inStream.tellg()));
266 if (str.empty())
267 return str;
268
269 inStream.seekg(0, std::ios::beg);
270 inStream.read(&str[0], str.size());
271 return str;
272}
273
274/** \brief Read VDF formatted sequences defined by the range [first, last).
275If the file is mailformatted, parser will try to read it until it can.
276@param first begin iterator
277@param end end iterator
278@param exclude_files list of files which cant be included anymore.
279 prevents circular includes
280
281can thow:
282 - "std::runtime_error" if a parsing error occured
283 - "std::bad_alloc" if not enough memory coup be allocated
284*/
285template <typename OutputT, typename IterT>
286std::vector<std::unique_ptr<OutputT>> read_internal(
287 IterT first, const IterT last,
288 std::unordered_set<
289 std::basic_string<typename std::iterator_traits<IterT>::value_type>>
290 &exclude_files,
291 const Options &opt)
292{
293 static_assert(std::is_default_constructible<OutputT>::value,
294 "Output Type must be default constructible (provide "
295 "constructor without arguments)");
296 static_assert(std::is_move_constructible<OutputT>::value,
297 "Output Type must be move constructible");
298
299 typedef typename std::iterator_traits<IterT>::value_type charT;
300
301 const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/");
302 const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t");
303
304#ifdef WIN32
305 std::function<bool(const std::basic_string<charT> &)> is_platform_str =
306 [](const std::basic_string<charT> &in)
307 {
308 return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS");
309 };
310#elif __APPLE__
311 // WIN32 stands for pc in general
312 std::function<bool(const std::basic_string<charT> &)> is_platform_str =
313 [](const std::basic_string<charT> &in)
314 {
315 return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
316 in == TYTI_L(charT, "$OSX");
317 };
318
319#elif __linux__
320 // WIN32 stands for pc in general
321 std::function<bool(const std::basic_string<charT> &)> is_platform_str =
322 [](const std::basic_string<charT> &in)
323 {
324 return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
325 in == TYTI_L(charT, "$LINUX");
326 };
327#else
328 std::function<bool(const std::basic_string<charT> &)> is_platform_str =
329 [](const std::basic_string<charT> &in) { return false; };
330#endif
331
332 if (opt.ignore_all_platform_conditionals)
333 is_platform_str = [](const std::basic_string<charT> &)
334 { return false; };
335
336 // function for skipping a comment block
337 // iter: iterator poition to the position after a '/'
338 auto skip_comments = [&comment_end_str](IterT iter,
339 const IterT &last) -> IterT
340 {
341 ++iter;
342 if (iter == last)
343 return last;
344
345 if (*iter == TYTI_L(charT, '/'))
346 {
347 // line comment, skip whole line
348 iter = std::find(iter + 1, last, TYTI_L(charT, '\n'));
349 if (iter == last)
350 return last;
351 }
352
353 if (*iter == '*')
354 {
355 // block comment, skip until next occurance of "*\"
356 iter = std::search(iter + 1, last, std::begin(comment_end_str),
357 std::end(comment_end_str));
358 if (std::distance(iter, last) <= 2)
359 return last;
360 iter += 2;
361 }
362
363 return iter;
364 };
365
366 auto end_quote = [](IterT iter, const IterT &last) -> IterT
367 {
368 const auto begin = iter;
369 auto last_esc = iter;
370 if (iter == last)
371 throw std::runtime_error{"quote was opened but not closed."};
372 do
373 {
374 ++iter;
375 iter = std::find(iter, last, TYTI_L(charT, '\"'));
376 if (iter == last)
377 break;
378
379 last_esc = std::prev(iter);
380 while (last_esc != begin && *last_esc == '\\')
381 --last_esc;
382 } while (!(std::distance(last_esc, iter) % 2) && iter != last);
383 if (iter == last)
384 throw std::runtime_error{"quote was opened but not closed."};
385 return iter;
386 };
387
388 auto end_word = [&whitespaces](IterT iter, const IterT &last) -> IterT
389 {
390 const auto begin = iter;
391 auto last_esc = iter;
392 if (iter == last)
393 throw std::runtime_error{"quote was opened but not closed."};
394 do
395 {
396 ++iter;
397 iter = std::find_first_of(iter, last, std::begin(whitespaces),
398 std::end(whitespaces));
399 if (iter == last)
400 break;
401
402 last_esc = std::prev(iter);
403 while (last_esc != begin && *last_esc == '\\')
404 --last_esc;
405 } while (!(std::distance(last_esc, iter) % 2) && iter != last);
406 if (iter == last)
407 throw std::runtime_error{"word wasnt properly ended"};
408 return iter;
409 };
410
411 auto skip_whitespaces = [&whitespaces](IterT iter,
412 const IterT &last) -> IterT
413 {
414 if (iter == last)
415 return iter;
416 iter = std::find_if_not(iter, last,
417 [&whitespaces](charT c)
418 {
419 // return true if whitespace
420 return std::any_of(std::begin(whitespaces),
421 std::end(whitespaces),
422 [c](charT pc)
423 { return pc == c; });
424 });
425 return iter;
426 };
427
428 std::function<void(std::basic_string<charT> &)> strip_escape_symbols =
429 [](std::basic_string<charT> &s)
430 {
431 auto quote_searcher = [&s](size_t pos)
432 { return s.find(TYTI_L(charT, "\\\""), pos); };
433 auto p = quote_searcher(0);
434 while (p != s.npos)
435 {
436 s.replace(p, 2, TYTI_L(charT, "\""));
437 p = quote_searcher(p);
438 }
439 auto searcher = [&s](size_t pos)
440 { return s.find(TYTI_L(charT, "\\\\"), pos); };
441 p = searcher(0);
442 while (p != s.npos)
443 {
444 s.replace(p, 2, TYTI_L(charT, "\\"));
445 p = searcher(p);
446 }
447 };
448
449 if (!opt.strip_escape_symbols)
450 strip_escape_symbols = [](std::basic_string<charT> &) {};
451
452 auto conditional_fullfilled =
453 [&skip_whitespaces, &is_platform_str](IterT &iter, const IterT &last)
454 {
455 iter = skip_whitespaces(iter, last);
456 if (iter == last)
457 return true;
458 if (*iter == '[')
459 {
460 ++iter;
461 if (iter == last)
462 throw std::runtime_error("conditional not closed");
463 const auto end = std::find(iter, last, ']');
464 if (end == last)
465 throw std::runtime_error("conditional not closed");
466 const bool negate = *iter == '!';
467 if (negate)
468 ++iter;
469 auto conditional = std::basic_string<charT>(iter, end);
470
471 const bool is_platform = is_platform_str(conditional);
472 iter = end + 1;
473
474 return static_cast<bool>(is_platform ^ negate);
475 }
476 return true;
477 };
478
479 // read header
480 // first, quoted name
481 std::unique_ptr<OutputT> curObj = nullptr;
482 std::vector<std::unique_ptr<OutputT>> roots;
483 std::stack<std::unique_ptr<OutputT>> lvls;
484 auto curIter = first;
485
486 while (curIter != last && *curIter != '\0')
487 {
488 // find first starting attrib/child, or ending
489 curIter = skip_whitespaces(curIter, last);
490 if (curIter == last || *curIter == '\0')
491 break;
492 if (*curIter == TYTI_L(charT, '/'))
493 {
494 curIter = skip_comments(curIter, last);
495 if (curIter == last || *curIter == '\0')
496 throw std::runtime_error("Unexpected eof");
497 }
498 else if (*curIter != TYTI_L(charT, '}'))
499 {
500 // get key
501 const auto keyEnd = (*curIter == TYTI_L(charT, '\"'))
502 ? end_quote(curIter, last)
503 : end_word(curIter, last);
504 if (*curIter == TYTI_L(charT, '\"'))
505 ++curIter;
506 std::basic_string<charT> key(curIter, keyEnd);
507 strip_escape_symbols(key);
508 curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0);
509 if (curIter == last)
510 throw std::runtime_error{"key opened, but never closed"};
511
512 curIter = skip_whitespaces(curIter, last);
513
514 auto conditional = conditional_fullfilled(curIter, last);
515 if (!conditional)
516 continue;
517 if (curIter == last)
518 throw std::runtime_error{"key declared, but no value"};
519
520 while (*curIter == TYTI_L(charT, '/'))
521 {
522
523 curIter = skip_comments(curIter, last);
524 if (curIter == last || *curIter == '}')
525 throw std::runtime_error{"key declared, but no value"};
526 curIter = skip_whitespaces(curIter, last);
527 if (curIter == last || *curIter == '}')
528 throw std::runtime_error{"key declared, but no value"};
529 }
530 // get value
531 if (*curIter != '{')
532 {
533 if (curIter == last)
534 throw std::runtime_error{"key declared, but no value"};
535 const auto valueEnd = (*curIter == TYTI_L(charT, '\"'))
536 ? end_quote(curIter, last)
537 : end_word(curIter, last);
538 if (valueEnd == last)
539 throw std::runtime_error("No closed word");
540 if (*curIter == TYTI_L(charT, '\"'))
541 ++curIter;
542 if (curIter == last)
543 throw std::runtime_error("No closed word");
544
545 auto value = std::basic_string<charT>(curIter, valueEnd);
546 strip_escape_symbols(value);
547 curIter =
548 valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0);
549
550 auto conditional = conditional_fullfilled(curIter, last);
551 if (!conditional)
552 continue;
553
554 // process value
555 if (key != TYTI_L(charT, "#include") &&
556 key != TYTI_L(charT, "#base"))
557 {
558 if (curObj)
559 {
560 curObj->add_attribute(std::move(key), std::move(value));
561 }
562 else
563 {
564 throw std::runtime_error{
565 "unexpected key without object"};
566 }
567 }
568 else
569 {
570 if (!opt.ignore_includes &&
571 exclude_files.find(value) == exclude_files.end())
572 {
573 exclude_files.insert(value);
574 std::basic_ifstream<charT> i(
575 detail::string_converter(value));
576 auto str = read_file(i);
577 auto file_objs = read_internal<OutputT>(
578 str.begin(), str.end(), exclude_files, opt);
579 for (auto &n : file_objs)
580 {
581 if (curObj)
582 curObj->add_child(std::move(n));
583 else
584 roots.push_back(std::move(n));
585 }
586 exclude_files.erase(value);
587 }
588 }
589 }
590 else if (*curIter == '{')
591 {
592 if (curObj)
593 lvls.push(std::move(curObj));
594 curObj = std::make_unique<OutputT>();
595 curObj->set_name(std::move(key));
596 ++curIter;
597 }
598 }
599 // end of new object
600 else if (curObj && *curIter == TYTI_L(charT, '}'))
601 {
602 if (!lvls.empty())
603 {
604 // get object before
605 std::unique_ptr<OutputT> prev{std::move(lvls.top())};
606 lvls.pop();
607
608 // add finished obj to obj before and release it from processing
609 prev->add_child(std::move(curObj));
610 curObj = std::move(prev);
611 }
612 else
613 {
614 roots.push_back(std::move(curObj));
615 curObj.reset();
616 }
617 ++curIter;
618 }
619 else
620 {
621 throw std::runtime_error{"unexpected '}'"};
622 }
623 }
624 if (curObj != nullptr || !lvls.empty())
625 {
626 throw std::runtime_error{"object is not closed with '}'"};
627 }
628
629 return roots;
630}
631
632} // namespace detail
633
634/** \brief Read VDF formatted sequences defined by the range [first, last).
635If the file is mailformatted, parser will try to read it until it can.
636@param first begin iterator
637@param end end iterator
638
639can thow:
640 - "std::runtime_error" if a parsing error occured
641 - "std::bad_alloc" if not enough memory coup be allocated
642*/
643template <typename OutputT, typename IterT>
644OutputT read(IterT first, const IterT last, const Options &opt = Options{})
645{
646 auto exclude_files = std::unordered_set<
647 std::basic_string<typename std::iterator_traits<IterT>::value_type>>{};
648 auto roots =
649 detail::read_internal<OutputT>(first, last, exclude_files, opt);
650
651 OutputT result;
652 if (roots.size() > 1)
653 {
654 for (auto &i : roots)
655 result.add_child(std::move(i));
656 }
657 else if (roots.size() == 1)
658 result = std::move(*roots[0]);
659
660 return result;
661}
662
663/** \brief Read VDF formatted sequences defined by the range [first, last).
664If the file is mailformatted, parser will try to read it until it can.
665@param first begin iterator
666@param end end iterator
667@param ec output bool. 0 if ok, otherwise, holds an system error code
668
669Possible error codes:
670std::errc::protocol_error: file is mailformatted
671std::errc::not_enough_memory: not enough space
672std::errc::invalid_argument: iterators throws e.g. out of range
673*/
674template <typename OutputT, typename IterT>
675OutputT read(IterT first, IterT last, std::error_code &ec,
676 const Options &opt = Options{}) NOEXCEPT
677
678{
679 ec.clear();
680 OutputT r{};
681 try
682 {
683 r = read<OutputT>(first, last, opt);
684 }
685 catch (std::runtime_error &)
686 {
687 ec = std::make_error_code(std::errc::protocol_error);
688 }
689 catch (std::bad_alloc &)
690 {
691 ec = std::make_error_code(std::errc::not_enough_memory);
692 }
693 catch (...)
694 {
695 ec = std::make_error_code(std::errc::invalid_argument);
696 }
697 return r;
698}
699
700/** \brief Read VDF formatted sequences defined by the range [first, last).
701If the file is mailformatted, parser will try to read it until it can.
702@param first begin iterator
703@param end end iterator
704@param ok output bool. true, if parser successed, false, if parser failed
705*/
706template <typename OutputT, typename IterT>
707OutputT read(IterT first, const IterT last, bool *ok,
708 const Options &opt = Options{}) NOEXCEPT
709{
710 std::error_code ec;
711 auto r = read<OutputT>(first, last, ec, opt);
712 if (ok)
713 *ok = !ec;
714 return r;
715}
716
717template <typename IterT>
718inline auto read(IterT first, const IterT last, bool *ok,
719 const Options &opt = Options{}) NOEXCEPT
720 ->basic_object<typename std::iterator_traits<IterT>::value_type>
721{
722 return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
723 first, last, ok, opt);
724}
725
726template <typename IterT>
727inline auto read(IterT first, IterT last, std::error_code &ec,
728 const Options &opt = Options{}) NOEXCEPT
729 ->basic_object<typename std::iterator_traits<IterT>::value_type>
730{
731 return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
732 first, last, ec, opt);
733}
734
735template <typename IterT>
736inline auto read(IterT first, const IterT last, const Options &opt = Options{})
737 -> basic_object<typename std::iterator_traits<IterT>::value_type>
738{
739 return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
740 first, last, opt);
741}
742
743/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
744 formatted data. throws "std::bad_alloc" if file buffer could not be allocated
745*/
746template <typename OutputT, typename iStreamT>
747OutputT read(iStreamT &inStream, std::error_code &ec,
748 const Options &opt = Options{})
749{
750 // cache the file
751 typedef typename iStreamT::char_type charT;
752 std::basic_string<charT> str = detail::read_file(inStream);
753
754 // parse it
755 return read<OutputT>(str.begin(), str.end(), ec, opt);
756}
757
758template <typename iStreamT>
759inline basic_object<typename iStreamT::char_type>
760read(iStreamT &inStream, std::error_code &ec, const Options &opt = Options{})
761{
762 return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt);
763}
764
765/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
766 formatted data. throws "std::bad_alloc" if file buffer could not be allocated
767 ok == false, if a parsing error occured
768*/
769template <typename OutputT, typename iStreamT>
770OutputT read(iStreamT &inStream, bool *ok, const Options &opt = Options{})
771{
772 std::error_code ec;
773 const auto r = read<OutputT>(inStream, ec, opt);
774 if (ok)
775 *ok = !ec;
776 return r;
777}
778
779template <typename iStreamT>
780inline basic_object<typename iStreamT::char_type>
781read(iStreamT &inStream, bool *ok, const Options &opt = Options{})
782{
783 return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt);
784}
785
786/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
787 formatted data. throws "std::bad_alloc" if file buffer could not be allocated
788 throws "std::runtime_error" if a parsing error occured
789*/
790template <typename OutputT, typename iStreamT>
791OutputT read(iStreamT &inStream, const Options &opt)
792{
793
794 // cache the file
795 typedef typename iStreamT::char_type charT;
796 std::basic_string<charT> str = detail::read_file(inStream);
797 // parse it
798 return read<OutputT>(str.begin(), str.end(), opt);
799}
800
801template <typename iStreamT>
802inline basic_object<typename iStreamT::char_type>
803read(iStreamT &inStream, const Options &opt = Options{})
804{
805 return read<basic_object<typename iStreamT::char_type>>(inStream, opt);
806}
807
808} // namespace vdf
809} // namespace tyti
810#ifndef TYTI_NO_L_UNDEF
811#undef TYTI_L
812#endif
813
814#ifdef TYTI_UNDEF_CONSTEXPR
815#undef CONSTEXPR
816#undef TYTI_NO_L_UNDEF
817#endif
818
819#ifdef TYTI_UNDEF_NOTHROW
820#undef NOTHROW
821#undef TYTI_UNDEF_NOTHROW
822#endif
823
824#endif //__TYTI_STEAM_VDF_PARSER_H__