Configuru
configuru.hpp
1 /*
2 www.github.com/emilk/configuru
3 
4 # Configuru
5  Configuru, an experimental config library for C++, by Emil Ernerfeldt.
6 
7 # License
8  This software is in the public domain. Where that dedication is not
9  recognized, you are granted a perpetual, irrevocable license to copy
10  and modify this file as you see fit.
11 
12  That being said, I would appreciate credit!
13  If you find this library useful, send a tweet to [@ernerfeldt](https://twitter.com/ernerfeldt) or mail me at emil.ernerfeldt@gmail.com.
14 
15 # Version history
16  0.0.0: 2014-07-21 - Initial steps
17  0.1.0: 2015-11-08 - First commit as stand-alone library
18  0.2.0: 2016-03-25 - check_dangling changes
19  0.2.1: 2016-04-11 - mark_accessed in dump_string by default
20  0.2.2: 2016-07-27 - optimizations
21  0.2.3: 2016-08-09 - optimizations + add Config::emplace(key, value)
22  0.2.4: 2016-08-18 - fix compilation error for when CONFIGURU_VALUE_SEMANTICS=0
23  0.3.0: 2016-09-15 - Add option to not align values (object_align_values)
24  0.3.1: 2016-09-19 - Fix crashes on some compilers/stdlibs
25  0.3.2: 2016-09-22 - Add support for Json::array(some_container)
26  0.3.3: 2017-01-10 - Add some missing iterator members
27  0.3.4: 2017-01-17 - Add cast conversion to std::array
28 
29 # Getting started
30  For using:
31  `#include <configuru.hpp>`
32 
33  And in one .cpp file:
34 
35  #define CONFIGURU_IMPLEMENTATION 1
36  #include <configuru.hpp>
37 
38  For more info, please see README.md (at www.github.com/emilk/configuru).
39 */
40 
41 // dP""b8 dP"Yb 88b 88 888888 88 dP""b8 88 88 88""Yb 88 88
42 // dP `" dP Yb 88Yb88 88__ 88 dP `" 88 88 88__dP 88 88
43 // Yb Yb dP 88 Y88 88"" 88 Yb "88 Y8 8P 88"Yb Y8 8P
44 // YboodP YbodP 88 Y8 88 88 YboodP `YbodP' 88 Yb `YbodP'
45 
46 // Disable all warnings from gcc/clang:
47 #if defined(__clang__)
48  #pragma clang system_header
49 #elif defined(__GNUC__)
50  #pragma GCC system_header
51 #endif
52 
53 #ifndef CONFIGURU_HEADER_HPP
54 #define CONFIGURU_HEADER_HPP
55 
56 #include <algorithm>
57 #include <array>
58 #include <atomic>
59 #include <cmath>
60 #include <cstddef>
61 #include <cstring>
62 #include <functional>
63 #include <initializer_list>
64 #include <iosfwd>
65 #include <iterator>
66 #include <map>
67 #include <memory>
68 #include <stdexcept>
69 #include <string>
70 #include <utility>
71 #include <vector>
72 
73 #ifndef CONFIGURU_ONERROR
74  #define CONFIGURU_ONERROR(message_str) \
75  throw std::runtime_error(message_str)
76 #endif // CONFIGURU_ONERROR
77 
78 #ifndef CONFIGURU_ASSERT
79  #include <cassert>
80  #define CONFIGURU_ASSERT(test) assert(test)
81 #endif // CONFIGURU_ASSERT
82 
83 #ifndef CONFIGURU_ON_DANGLING
84  #define CONFIGURU_ON_DANGLING(message_str) \
86  CONFIGURU_ONERROR(message_str)
87 #endif // CONFIGURU_ON_DANGLING
88 
89 #define CONFIGURU_NORETURN __attribute__((noreturn))
90 
91 #ifndef CONFIGURU_IMPLICIT_CONVERSIONS
92  #define CONFIGURU_IMPLICIT_CONVERSIONS 0
94 #endif
95 
96 #ifndef CONFIGURU_VALUE_SEMANTICS
97  #define CONFIGURU_VALUE_SEMANTICS 0
100 #endif
101 
102 #define CONFIGURU_NORETURN __attribute__((noreturn))
103 
104 #undef Bool // Needed on Ubuntu 14.04 with GCC 4.8.5
105 #undef check // Needed on OSX
106 
108 namespace configuru
109 {
110  struct DocInfo;
111  using DocInfo_SP = std::shared_ptr<DocInfo>;
112 
113  using Index = unsigned;
114  const Index BAD_INDEX = static_cast<Index>(-1);
115 
116  struct Include
117  {
118  DocInfo_SP doc;
119  Index line = BAD_INDEX;
120 
121  Include() {}
122  Include(DocInfo_SP d, Index l) : doc(d), line(l) {}
123  };
124 
126  struct DocInfo
127  {
128  std::vector<Include> includers;
129 
130  std::string filename;
131 
132  DocInfo(const std::string& fn) : filename(fn) { }
133 
134  void append_include_info(std::string& ret, const std::string& indent=" ") const;
135  };
136 
137  struct BadLookupInfo;
138 
140  template<typename Config_T>
142  {
143  Config_T _value;
144  Index _nr = BAD_INDEX;
145  mutable bool _accessed = false;
146 
147  Config_Entry() {}
148  Config_Entry(Config_T value, Index nr) : _value(std::move(value)), _nr(nr) {}
149  };
150 
151  using Comment = std::string;
152  using Comments = std::vector<Comment>;
153 
156  {
159  Comments prefix;
160  Comments postfix;
161  Comments pre_end_brace;
162 
164 
165  bool empty() const;
166  void append(ConfigComments&& other);
167  };
168 
170  class Config;
171 
186  template<typename T>
187  inline T as(const configuru::Config& config);
188 
194  class Config
195  {
196  public:
197  enum Type
198  {
201  Null, Bool, Int, Float, String, Array, Object
202  };
203 
205 
206  using ConfigArrayImpl = std::vector<Config>;
207  using ConfigObjectImpl = std::map<std::string, ObjectEntry>;
208  struct ConfigArray
209  {
210  #if !CONFIGURU_VALUE_SEMANTICS
211  std::atomic<unsigned> _ref_count { 1 };
212  #endif
213  ConfigArrayImpl _impl;
214  };
215  struct ConfigObject;
216 
217  // ----------------------------------------
218  // Constructors:
219 
221  Config() : _type(Uninitialized) { }
222  Config(std::nullptr_t) : _type(Null) { }
223  Config(float f) : _type(Float) { _u.f = f; }
224  Config(double f) : _type(Float) { _u.f = f; }
225  Config(bool b) : _type(Bool) { _u.b = b; }
226  Config(int i) : _type(Int) { _u.i = i; }
227  Config(unsigned int i) : _type(Int) { _u.i = i; }
228  Config(long i) : _type(Int) { _u.i = i; }
229  Config(unsigned long i) : Config(static_cast<unsigned long long>(i)) {}
230  Config(long long i) : _type(Int) { _u.i = i; }
231  Config(unsigned long long i) : _type(Int)
232  {
233  if ((i & 0x8000000000000000ull) != 0) {
234  CONFIGURU_ONERROR("Integer too large to fit into 63 bits");
235  }
236  _u.i = static_cast<int64_t>(i);
237  }
238  Config(const char* str);
239  Config(std::string str);
240 
261  Config(std::initializer_list<std::pair<std::string, Config>> values);
262 
264  template<typename T>
265  Config(const std::vector<T>& values) : _type(Uninitialized)
266  {
267  make_array();
268  _u.array->_impl.reserve(values.size());
269  for (const auto& v : values) {
270  push_back(v);
271  }
272  }
273 
275  Config(const std::vector<bool>& values) : _type(Uninitialized)
276  {
277  make_array();
278  _u.array->_impl.reserve(values.size());
279  for (const auto v : values) {
280  push_back(!!v);
281  }
282  }
283 
285  template<typename T>
286  Config(const std::map<std::string, T>& values) : _type(Uninitialized)
287  {
288  make_object();
289  for (const auto& p : values) {
290  (*this)[p.first] = p.second;
291  }
292  }
293 
295  void make_object();
296 
298  void make_array();
299 
301  void tag(const DocInfo_SP& doc, Index line, Index column);
302 
304  static Config object();
305 
307  static Config object(std::initializer_list<std::pair<std::string, Config>> values);
308 
310  static Config array();
311 
313  static Config array(std::initializer_list<Config> values);
314 
316  template<typename Container>
317  static Config array(const Container& container)
318  {
319  Config ret;
320  ret.make_array();
321  auto& impl = ret._u.array->_impl;
322  impl.reserve(container.size());
323  for (auto&& v : container) {
324  impl.emplace_back(v);
325  }
326  return ret;
327  }
328 
329  // ----------------------------------------
330 
331  ~Config();
332 
333  Config(const Config& o);
334  Config(Config&& o) noexcept;
335  Config& operator=(const Config& o);
336 
338  Config& operator=(Config&& o) noexcept;
339 
341  void swap(Config& o) noexcept;
342 
343  #ifdef CONFIG_EXTENSION
344  CONFIG_EXTENSION
345  #endif
346 
347  // ----------------------------------------
348  // Inspectors:
349 
350  Type type() const { return _type; }
351 
352  bool is_uninitialized() const { return _type == Uninitialized; }
353  bool is_null() const { return _type == Null; }
354  bool is_bool() const { return _type == Bool; }
355  bool is_int() const { return _type == Int; }
356  bool is_float() const { return _type == Float; }
357  bool is_string() const { return _type == String; }
358  bool is_object() const { return _type == Object; }
359  bool is_array() const { return _type == Array; }
360  bool is_number() const { return is_int() || is_float(); }
361 
363  std::string where() const;
364 
366  Index line() const { return _line; }
367 
369  const DocInfo_SP& doc() const { return _doc; }
370  void set_doc(const DocInfo_SP& doc) { _doc = doc; }
371 
372  // ----------------------------------------
373  // Convertors:
374 
375 #if CONFIGURU_IMPLICIT_CONVERSIONS
376  template<typename T>
378  explicit operator T() const { return as<T>(*this); }
379 
380  inline operator bool() const { return as_bool(); }
381  inline operator signed char() const { return as_integer<signed char>(); }
382  inline operator unsigned char() const { return as_integer<unsigned char>(); }
383  inline operator signed short() const { return as_integer<signed short>(); }
384  inline operator unsigned short() const { return as_integer<unsigned short>(); }
385  inline operator signed int() const { return as_integer<signed int>(); }
386  inline operator unsigned int() const { return as_integer<unsigned int>(); }
387  inline operator signed long() const { return as_integer<signed long>(); }
388  inline operator unsigned long() const { return as_integer<unsigned long>(); }
389  inline operator signed long long() const { return as_integer<signed long long>(); }
390  inline operator unsigned long long() const { return as_integer<unsigned long long>(); }
391  inline operator float() const { return as_float(); }
392  inline operator double() const { return as_double(); }
393  inline operator std::string() const { return as_string(); }
394  inline operator Config::ConfigArrayImpl() const { return as_array(); }
395 
397  template<typename T>
398  operator std::vector<T>() const
399  {
400  const auto& array = as_array();
401  std::vector<T> ret;
402  ret.reserve(array.size());
403  for (auto&& config : array) {
404  ret.push_back((T)config);
405  }
406  return ret;
407  }
408 
410  template<typename T, size_t N>
411  operator std::array<T, N>() const
412  {
413  const auto& array = as_array();
414  check(array.size() == N, "Array size mismatch.");
415  std::array<T, N> ret;
416  std::copy(array.begin(), array.end(), ret.begin());
417  return ret;
418  }
419 
422  template<typename Left, typename Right>
423  operator std::pair<Left, Right>() const
424  {
425  const auto& array = as_array();
426  check(array.size() == 2u, "Mismatched array length.");
427  return {(Left)array[0], (Right)array[1]};
428  }
429 #else
430  template<typename T>
432  explicit operator T() const { return as<T>(*this); }
433 
435  template<typename T>
436  explicit operator std::vector<T>() const
437  {
438  const auto& array = as_array();
439  std::vector<T> ret;
440  ret.reserve(array.size());
441  for (auto&& config : array) {
442  ret.push_back(static_cast<T>(config));
443  }
444  return ret;
445  }
446 
448  template<typename T, size_t N>
449  explicit operator std::array<T, N>() const
450  {
451  const auto& array = as_array();
452  check(array.size() == N, "Array size mismatch.");
453  std::array<T, N> ret;
454  for (size_t i =0; i < N; ++i) {
455  ret[i] = static_cast<T>(array[i]);
456  }
457  return ret;
458  }
459 
462  template<typename Left, typename Right>
463  explicit operator std::pair<Left, Right>() const
464  {
465  const auto& array = as_array();
466  check(array.size() == 2u, "Mismatched array length.");
467  return {static_cast<Left>(array[0]), static_cast<Right>(array[1])};
468  }
469 #endif
470 
471  const std::string& as_string() const { assert_type(String); return *_u.str; }
472  const char* c_str() const { assert_type(String); return _u.str->c_str(); }
473 
475  bool as_bool() const
476  {
477  assert_type(Bool);
478  return _u.b;
479  }
480 
481  template<typename IntT>
482  IntT as_integer() const
483  {
484  static_assert(std::is_integral<IntT>::value, "Not an integer.");
485  assert_type(Int);
486  check(static_cast<int64_t>(static_cast<IntT>(_u.i)) == _u.i, "Integer out of range");
487  return static_cast<IntT>(_u.i);
488  }
489 
490  float as_float() const
491  {
492  if (_type == Int) {
493  return _u.i;
494  } else {
495  assert_type(Float);
496  return static_cast<float>(_u.f);
497  }
498  }
499 
500  double as_double() const
501  {
502  if (_type == Int) {
503  return _u.i;
504  } else {
505  assert_type(Float);
506  return _u.f;
507  }
508  }
509 
511  template<typename T>
512  T get() const;
513 
515  template<typename T>
516  T get_or(const T& default_value) const
517  {
518  if (_type == BadLookupType) {
519  return default_value;
520  } else {
521  return static_cast<T>(*this);
522  }
523  }
524 
525  // ----------------------------------------
526  // Array:
527 
529  size_t array_size() const
530  {
531  return as_array().size();
532  }
533 
535  ConfigArrayImpl& as_array()
536  {
537  assert_type(Array);
538  return _u.array->_impl;
539  }
540 
542  const ConfigArrayImpl& as_array() const
543  {
544  assert_type(Array);
545  return _u.array->_impl;
546  }
547 
549  Config& operator[](size_t ix)
550  {
551  auto&& array = as_array();
552  check(ix < array.size(), "Array index out of range");
553  return array[ix];
554  }
555 
557  const Config& operator[](size_t ix) const
558  {
559  auto&& array = as_array();
560  check(ix < array.size(), "Array index out of range");
561  return array[ix];
562  }
563 
565  void push_back(Config value)
566  {
567  as_array().push_back(std::move(value));
568  }
569 
570  // ----------------------------------------
571  // Object:
572 
574  size_t object_size() const;
575 
579  {
580  assert_type(Object);
581  return *_u.object;
582  }
583 
586  const ConfigObject& as_object() const
587  {
588  assert_type(Object);
589  return *_u.object;
590  }
591 
593  const Config& operator[](const std::string& key) const;
594 
596  Config& operator[](const std::string& key);
597 
599  template<std::size_t N>
600  Config& operator[](const char (&key)[N]) { return operator[](std::string(key)); }
601  template<std::size_t N>
602  const Config& operator[](const char (&key)[N]) const { return operator[](std::string(key)); }
603 
605  bool has_key(const std::string& key) const;
606 
608  size_t count(const std::string& key) const { return has_key(key) ? 1 : 0; }
609 
611  bool emplace(std::string key, Config value);
612 
614  void insert_or_assign(const std::string& key, Config&& value);
615 
617  bool erase(const std::string& key);
618 
620  template<typename T>
621  T get(const std::string& key) const
622  {
623  return as<T>((*this)[key]);
624  }
625 
627  template<typename T>
628  T get_or(const std::string& key, const T& default_value) const;
629 
631  std::string get_or(const std::string& key, const char* default_value) const
632  {
633  return get_or<std::string>(key, default_value);
634  }
635 
637  template<typename T>
638  T get_or(std::initializer_list<std::string> keys, const T& default_value) const;
639 
641  std::string get_or(std::initializer_list<std::string> keys, const char* default_value) const
642  {
643  return get_or<std::string>(keys, default_value);
644  }
645 
646  // --------------------------------------------------------------------------------
647 
649  static bool deep_eq(const Config& a, const Config& b);
650 
651 #if !CONFIGURU_VALUE_SEMANTICS // No need for a deep_clone method when all copies are deep clones.
652  Config deep_clone() const;
654 #endif
655 
656  // ----------------------------------------
657 
659  void visit_dangling(const std::function<void(const std::string& key, const Config& value)>& visitor) const;
660 
662  void check_dangling() const;
663 
665  void mark_accessed(bool v) const;
666 
667  // ----------------------------------------
668 
670  bool has_comments() const
671  {
672  return _comments && !_comments->empty();
673  }
674 
677  {
678  if (!_comments) {
679  _comments.reset(new ConfigComments());
680  }
681  return *_comments;
682  }
683 
685  const ConfigComments& comments() const
686  {
687  static const ConfigComments s_empty {};
688  if (_comments) {
689  return *_comments;
690  } else {
691  return s_empty;
692  }
693  }
694 
696  const char* debug_descr() const;
697 
699  static const char* type_str(Type t);
700 
701  // ----------------------------------------
702  // Helper functions for checking the type is what we expect:
703 
704  inline void check(bool b, const char* msg) const
705  {
706  if (!b) {
707  on_error(msg);
708  }
709  }
710 
711  void assert_type(Type t) const;
712 
713  void on_error(const std::string& msg) const CONFIGURU_NORETURN;
714 
715  private:
716  void free();
717 
718  using ConfigComments_UP = std::unique_ptr<ConfigComments>;
719 
720  union {
721  bool b;
722  int64_t i;
723  double f;
724  const std::string* str;
725  ConfigObject* object;
726  ConfigArray* array;
727  BadLookupInfo* bad_lookup;
728  } _u;
729 
730  DocInfo_SP _doc; // So we can name the file
731  ConfigComments_UP _comments;
732  Index _line = BAD_INDEX; // Where in the source, or BAD_INDEX. Lines are 1-indexed.
733  Type _type = Uninitialized;
734  };
735 
736  // ------------------------------------------------------------------------
737 
739  {
740  #if !CONFIGURU_VALUE_SEMANTICS
741  std::atomic<unsigned> _ref_count { 1 };
742  #endif
743  ConfigObjectImpl _impl;
744 
745  class iterator
746  {
747  public:
748  iterator() = default;
749  explicit iterator(ConfigObjectImpl::iterator it) : _it(std::move(it)) {}
750 
751  const iterator& operator*() const {
752  _it->second._accessed = true;
753  return *this;
754  }
755 
756  iterator& operator++() {
757  ++_it;
758  return *this;
759  }
760 
761  friend bool operator==(const iterator& a, const iterator& b) {
762  return a._it == b._it;
763  }
764 
765  friend bool operator!=(const iterator& a, const iterator& b) {
766  return a._it != b._it;
767  }
768 
769  const std::string& key() const { return _it->first; }
770  Config& value() const { return _it->second._value; }
771 
772  private:
773  ConfigObjectImpl::iterator _it;
774  };
775 
777  {
778  public:
779  const_iterator() = default;
780  explicit const_iterator(ConfigObjectImpl::const_iterator it) : _it(std::move(it)) {}
781 
782  const const_iterator& operator*() const {
783  _it->second._accessed = true;
784  return *this;
785  }
786 
787  const_iterator& operator++() {
788  ++_it;
789  return *this;
790  }
791 
792  friend bool operator==(const const_iterator& a, const const_iterator& b) {
793  return a._it == b._it;
794  }
795 
796  friend bool operator!=(const const_iterator& a, const const_iterator& b) {
797  return a._it != b._it;
798  }
799 
800  const std::string& key() const { return _it->first; }
801  const Config& value() const { return _it->second._value; }
802 
803  private:
804  ConfigObjectImpl::const_iterator _it;
805  };
806 
807  iterator begin() { return iterator{_impl.begin()}; }
808  iterator end() { return iterator{_impl.end()}; }
809  const_iterator begin() const { return const_iterator{_impl.cbegin()}; }
810  const_iterator end() const { return const_iterator{_impl.cend()}; }
811  const_iterator cbegin() const { return const_iterator{_impl.cbegin()}; }
812  const_iterator cend() const { return const_iterator{_impl.cend()}; }
813  };
814 
815  // ------------------------------------------------------------------------
816 
817  inline bool operator==(const Config& a, const Config& b)
818  {
819  return Config::deep_eq(a, b);
820  }
821 
822  inline bool operator!=(const Config& a, const Config& b)
823  {
824  return !Config::deep_eq(a, b);
825  }
826 
827  // ------------------------------------------------------------------------
828 
829  template<> inline bool Config::get() const { return as_bool(); }
830  template<> inline signed char Config::get() const { return as_integer<signed char>(); }
831  template<> inline unsigned char Config::get() const { return as_integer<unsigned char>(); }
832  template<> inline signed short Config::get() const { return as_integer<signed short>(); }
833  template<> inline unsigned short Config::get() const { return as_integer<unsigned short>(); }
834  template<> inline signed int Config::get() const { return as_integer<signed int>(); }
835  template<> inline unsigned int Config::get() const { return as_integer<unsigned int>(); }
836  template<> inline signed long Config::get() const { return as_integer<signed long>(); }
837  template<> inline unsigned long Config::get() const { return as_integer<unsigned long>(); }
838  template<> inline signed long long Config::get() const { return as_integer<signed long long>(); }
839  template<> inline unsigned long long Config::get() const { return as_integer<unsigned long long>(); }
840  template<> inline float Config::get() const { return as_float(); }
841  template<> inline double Config::get() const { return as_double(); }
842  template<> inline const std::string& Config::get() const { return as_string(); }
843  template<> inline std::string Config::get() const { return as_string(); }
844  template<> inline const Config::ConfigArrayImpl& Config::get() const { return as_array(); }
845  // template<> inline std::vector<std::string> Config::get() const { return as_vector<T>(); }
846 
847  // ------------------------------------------------------------------------
848 
849  template<typename T>
850  inline T as(const configuru::Config& config)
851  {
852  return config.get<T>();
853  }
854 
855  template<typename T>
856  T Config::get_or(const std::string& key, const T& default_value) const
857  {
858  auto&& object = as_object()._impl;
859  auto it = object.find(key);
860  if (it == object.end()) {
861  return default_value;
862  } else {
863  const auto& entry = it->second;
864  entry._accessed = true;
865  return as<T>(entry._value);
866  }
867  }
868 
869  template<typename T>
870  T Config::get_or(std::initializer_list<std::string> keys, const T& default_value) const
871  {
872  const Config* obj = this;
873  for (const auto& key : keys)
874  {
875  if (obj->has_key(key)) {
876  obj = &(*obj)[key];
877  } else {
878  return default_value;
879  }
880  }
881  return as<T>(*obj);
882  }
883 
884  // ------------------------------------------------------------------------
885 
887  std::ostream& operator<<(std::ostream& os, const Config& cfg);
888 
889  // ------------------------------------------------------------------------
890 
892  template<class Config, class Visitor>
893  void visit_configs(Config&& config, Visitor&& visitor)
894  {
895  visitor(config);
896  if (config.is_object()) {
897  for (auto&& p : config.as_object()) {
898  visit_configs(p.value(), visitor);
899  }
900  } else if (config.is_array()) {
901  for (auto&& e : config.as_array()) {
902  visit_configs(e, visitor);
903  }
904  }
905  }
906 
907  inline void clear_doc(Config& root) // TODO: shouldn't be needed. Replace with some info of wether a Config is the root of the document it is in.
908  {
909  visit_configs(root, [&](Config& cfg){ cfg.set_doc(nullptr); });
910  }
911 
912  /*
913  inline void replace_doc(Config& root, DocInfo_SP find, DocInfo_SP replacement)
914  {
915  visit_configs(root, [&](Config& config){
916  if (config.doc() == find) {
917  config.set_doc(replacement);
918  }
919  });
920  }
921 
922  // Will try to merge from 'src' do 'dst', replacing with 'src' on any conflict.
923  inline void merge_replace(Config& dst, const Config& src)
924  {
925  if (dst.is_object() && src.is_object()) {
926  for (auto&& p : src.as_object()) {
927  merge_replace(dst[p.key()], p.value());
928  }
929  } else {
930  dst = src;
931  }
932  }
933  */
934 
935  // ----------------------------------------------------------
936 
938  class ParseError : public std::exception
939  {
940  public:
941  ParseError(const DocInfo_SP& doc, Index line, Index column, const std::string& msg)
942  : _line(line), _column(column)
943  {
944  _what = doc->filename + ":" + std::to_string(line) + ":" + std::to_string(column);
945  doc->append_include_info(_what);
946  _what += ": " + msg;
947  }
948 
950  const char* what() const noexcept override
951  {
952  return _what.c_str();
953  }
954 
955  Index line() const noexcept { return _line; }
956  Index column() const noexcept { return _column; }
957 
958  private:
959  Index _line, _column;
960  std::string _what;
961  };
962 
963  // ----------------------------------------------------------
964 
967  {
971  std::string indentation = "\t";
972  bool enforce_indentation = true;
973  bool end_with_newline = true;
974 
975  // Top file:
976  bool empty_file = false;
977  bool implicit_top_object = true;
978  bool implicit_top_array = true;
979 
980  // Comments:
981  bool single_line_comments = true;
982  bool block_comments = true; /* Allow this? */
983  bool nesting_block_comments = true;
984 
985  // Numbers:
986  bool inf = true;
987  bool nan = true;
988  bool hexadecimal_integers = true;
989  bool binary_integers = true;
990  bool unary_plus = true;
991  bool distinct_floats = true;
992 
993  // Arrays
994  bool array_omit_comma = true;
995  bool array_trailing_comma = true;
996 
997  // Objects:
998  bool identifiers_keys = true;
999  bool object_separator_equal = false;
1000  bool allow_space_before_colon = false;
1001  bool omit_colon_before_object = false;
1002  bool object_omit_comma = true;
1003  bool object_trailing_comma = true;
1004  bool object_duplicate_keys = false;
1005  bool object_align_values = true;
1006 
1007  // Strings
1008  bool str_csharp_verbatim = true;
1009  bool str_python_multiline = true;
1010  bool str_32bit_unicode = true;
1011  bool str_allow_tab = true;
1012 
1013  // Special
1014  bool allow_macro = true;
1015 
1016  // When writing:
1017  bool write_comments = true;
1018 
1020  bool sort_keys = false;
1021 
1023  bool write_uninitialized = false;
1024 
1026  bool mark_accessed = true;
1027 
1028  bool compact() const { return indentation.empty(); }
1029  };
1030 
1033  {
1034  FormatOptions options;
1035 
1036  options.indentation = "\t";
1037  options.enforce_indentation = false;
1038 
1039  // Top file:
1040  options.empty_file = false;
1041  options.implicit_top_object = false;
1042  options.implicit_top_array = false;
1043 
1044  // Comments:
1045  options.single_line_comments = false;
1046  options.block_comments = false;
1047  options.nesting_block_comments = false;
1048 
1049  // Numbers:
1050  options.inf = false;
1051  options.nan = false;
1052  options.hexadecimal_integers = false;
1053  options.binary_integers = false;
1054  options.unary_plus = false;
1055  options.distinct_floats = true;
1056 
1057  // Arrays
1058  options.array_omit_comma = false;
1059  options.array_trailing_comma = false;
1060 
1061  // Objects:
1062  options.identifiers_keys = false;
1063  options.object_separator_equal = false;
1064  options.allow_space_before_colon = true;
1065  options.omit_colon_before_object = false;
1066  options.object_omit_comma = false;
1067  options.object_trailing_comma = false;
1068  options.object_duplicate_keys = false; // To be 100% JSON compatile, this should be true, but it is error prone.
1069  options.object_align_values = true; // Looks better.
1070 
1071  // Strings
1072  options.str_csharp_verbatim = false;
1073  options.str_python_multiline = false;
1074  options.str_32bit_unicode = false;
1075  options.str_allow_tab = false;
1076 
1077  // Special
1078  options.allow_macro = false;
1079 
1080  // When writing:
1081  options.write_comments = false;
1082  options.sort_keys = false;
1083 
1084  return options;
1085  }
1086 
1089  {
1090  FormatOptions options;
1091 
1092  options.indentation = "\t";
1093  options.enforce_indentation = false;
1094 
1095  // Top file:
1096  options.empty_file = true;
1097  options.implicit_top_object = true;
1098  options.implicit_top_array = true;
1099 
1100  // Comments:
1101  options.single_line_comments = true;
1102  options.block_comments = true;
1103  options.nesting_block_comments = true;
1104 
1105  // Numbers:
1106  options.inf = true;
1107  options.nan = true;
1108  options.hexadecimal_integers = true;
1109  options.binary_integers = true;
1110  options.unary_plus = true;
1111  options.distinct_floats = true;
1112 
1113  // Arrays
1114  options.array_omit_comma = true;
1115  options.array_trailing_comma = true;
1116 
1117  // Objects:
1118  options.identifiers_keys = true;
1119  options.object_separator_equal = true;
1120  options.allow_space_before_colon = true;
1121  options.omit_colon_before_object = true;
1122  options.object_omit_comma = true;
1123  options.object_trailing_comma = true;
1124  options.object_duplicate_keys = true;
1125 
1126  // Strings
1127  options.str_csharp_verbatim = true;
1128  options.str_python_multiline = true;
1129  options.str_32bit_unicode = true;
1130  options.str_allow_tab = true;
1131 
1132  // Special
1133  options.allow_macro = true;
1134 
1135  // When writing:
1136  options.write_comments = false;
1137  options.sort_keys = false;
1138 
1139  return options;
1140  }
1141 
1143  static const FormatOptions CFG = FormatOptions();
1144 
1146  static const FormatOptions JSON = make_json_options();
1147 
1149  static const FormatOptions FORGIVING = make_forgiving_options();
1150 
1151  struct ParseInfo
1152  {
1153  std::map<std::string, Config> parsed_files; // Two #include gives same Config tree.
1154  };
1155 
1159  Config parse_string(const char* str, const FormatOptions& options, const char* name);
1160  Config parse_file(const std::string& path, const FormatOptions& options);
1161 
1163  Config parse_string(const char* str, const FormatOptions& options, DocInfo _doc, ParseInfo& info);
1164  Config parse_file(const std::string& path, const FormatOptions& options, DocInfo_SP doc, ParseInfo& info);
1165 
1166  // ----------------------------------------------------------
1171  std::string dump_string(const Config& config, const FormatOptions& options);
1172 
1175  void dump_file(const std::string& path, const Config& config, const FormatOptions& options);
1176 } // namespace configuru
1177 
1178 #endif // CONFIGURU_HEADER_HPP
1179 
1180 // ----------------------------------------------------------------------------
1181 // 88 8b d8 88""Yb 88 888888 8b d8 888888 88b 88 888888 db 888888 88 dP"Yb 88b 88
1182 // 88 88b d88 88__dP 88 88__ 88b d88 88__ 88Yb88 88 dPYb 88 88 dP Yb 88Yb88
1183 // 88 88YbdP88 88""" 88 .o 88"" 88YbdP88 88"" 88 Y88 88 dP__Yb 88 88 Yb dP 88 Y88
1184 // 88 88 YY 88 88 88ood8 888888 88 YY 88 888888 88 Y8 88 dP""""Yb 88 88 YbodP 88 Y8
1185 
1186 /* In one of your .cpp files you need to do the following:
1187 #define CONFIGURU_IMPLEMENTATION
1188 #include <configuru.hpp>
1189 
1190 This will define all the Configuru functions so that the linker may find them.
1191 */
1192 
1193 #if defined(CONFIGURU_IMPLEMENTATION) && !defined(CONFIGURU_HAS_BEEN_IMPLEMENTED)
1194 #define CONFIGURU_HAS_BEEN_IMPLEMENTED
1195 
1196 #include <algorithm>
1197 #include <limits>
1198 #include <ostream>
1199 
1200 // ----------------------------------------------------------------------------
1201 namespace configuru
1202 {
1203  void DocInfo::append_include_info(std::string& ret, const std::string& indent) const
1204  {
1205  if (!includers.empty()) {
1206  ret += ", included at:\n";
1207  for (auto&& includer : includers) {
1208  ret += indent + includer.doc->filename + ":" + std::to_string(includer.line);
1209  includer.doc->append_include_info(ret, indent + " ");
1210  ret += "\n";
1211  }
1212  ret.pop_back();
1213  }
1214  }
1215 
1216  struct BadLookupInfo
1217  {
1218  const DocInfo_SP doc; // Of parent object
1219  const unsigned line; // Of parent object
1220  const std::string key;
1221 
1222  #if !CONFIGURU_VALUE_SEMANTICS
1223  std::atomic<unsigned> _ref_count { 1 };
1224  #endif
1225 
1226  BadLookupInfo(DocInfo_SP doc_, Index line_, std::string key_)
1227  : doc(std::move(doc_)), line(line_), key(std::move(key_)) {}
1228  };
1229 
1230  Config::Config(const char* str) : _type(String)
1231  {
1232  CONFIGURU_ASSERT(str != nullptr);
1233  _u.str = new std::string(str);
1234  }
1235 
1236  Config::Config(std::string str) : _type(String)
1237  {
1238  _u.str = new std::string(move(str));
1239  }
1240 
1241  Config::Config(std::initializer_list<std::pair<std::string, Config>> values) : _type(Uninitialized)
1242  {
1243  make_object();
1244  for (auto&& v : values) {
1245  (*this)[v.first] = std::move(v.second);
1246  }
1247  }
1248 
1249  void Config::make_object()
1250  {
1251  assert_type(Uninitialized);
1252  _type = Object;
1253  _u.object = new ConfigObject();
1254  }
1255 
1256  void Config::make_array()
1257  {
1258  assert_type(Uninitialized);
1259  _type = Array;
1260  _u.array = new ConfigArray();
1261  }
1262 
1264  {
1265  Config ret;
1266  ret.make_object();
1267  return ret;
1268  }
1269 
1270  Config Config::object(std::initializer_list<std::pair<std::string, Config>> values)
1271  {
1272  Config ret;
1273  ret.make_object();
1274  for (auto&& p : values) {
1275  ret[static_cast<std::string>(p.first)] = std::move(p.second);
1276  }
1277  return ret;
1278  }
1279 
1281  {
1282  Config ret;
1283  ret.make_array();
1284  return ret;
1285  }
1286 
1287  Config Config::array(std::initializer_list<Config> values)
1288  {
1289  Config ret;
1290  ret.make_array();
1291  ret._u.array->_impl.reserve(values.size());
1292  for (auto&& v : values) {
1293  ret.push_back(std::move(v));
1294  }
1295  return ret;
1296  }
1297 
1298  void Config::tag(const DocInfo_SP& doc, Index line, Index column)
1299  {
1300  _doc = doc;
1301  _line = line;
1302  (void)column; // TODO: include this info too.
1303  }
1304 
1305  // ------------------------------------------------------------------------
1306 
1307  Config::Config(const Config& o) : _type(Uninitialized)
1308  {
1309  *this = o;
1310  }
1311 
1312  Config::Config(Config&& o) noexcept : _type(Uninitialized)
1313  {
1314  this->swap(o);
1315  }
1316 
1317  void Config::swap(Config& o) noexcept
1318  {
1319  if (&o == this) { return; }
1320  std::swap(_type, o._type);
1321  std::swap(_u, o._u);
1322  std::swap(_doc, o._doc);
1323  std::swap(_line, o._line);
1324  std::swap(_comments, o._comments);
1325  }
1326 
1327  Config& Config::operator=(Config&& o) noexcept
1328  {
1329  if (&o == this) { return *this; }
1330 
1331  std::swap(_type, o._type);
1332  std::swap(_u, o._u);
1333 
1334  // Remember where we come from even when assigned a new value:
1335  if (o._doc || o._line != BAD_INDEX) {
1336  std::swap(_doc, o._doc);
1337  std::swap(_line, o._line);
1338  }
1339 
1340  if (o._comments) {
1341  std::swap(_comments, o._comments);
1342  }
1343 
1344  return *this;
1345  }
1346 
1347  Config& Config::operator=(const Config& o)
1348  {
1349  if (&o == this) { return *this; }
1350 
1351  free();
1352 
1353  _type = o._type;
1354 
1355  #if CONFIGURU_VALUE_SEMANTICS
1356  if (_type == String) {
1357  _u.str = new std::string(*o._u.str);
1358  } else if (_type == BadLookupType) {
1359  _u.bad_lookup = new BadLookupInfo(*o._u.bad_lookup);
1360  } else if (_type == Object) {
1361  _u.object = new ConfigObject(*o._u.object);
1362  } else if (_type == Array) {
1363  _u.array = new ConfigArray(*o._u.array);
1364  } else {
1365  memcpy(&_u, &o._u, sizeof(_u));
1366  }
1367  #else // !CONFIGURU_VALUE_SEMANTICS:
1368  if (_type == String) {
1369  _u.str = new std::string(*o._u.str);
1370  } else {
1371  memcpy(&_u, &o._u, sizeof(_u));
1372  if (_type == BadLookupType) { ++_u.bad_lookup->_ref_count; }
1373  if (_type == Array) { ++_u.array->_ref_count; }
1374  if (_type == Object) { ++_u.object->_ref_count; }
1375  }
1376  #endif // !CONFIGURU_VALUE_SEMANTICS
1377 
1378  // Remember where we come from even when assigned a new value:
1379  if (o._doc || o._line != BAD_INDEX) {
1380  _doc = o._doc;
1381  _line = o._line;
1382  }
1383 
1384  if (o._comments) {
1385  _comments.reset(new ConfigComments(*o._comments));
1386  }
1387 
1388  #if CONFIGURU_VALUE_SEMANTICS
1389  o.mark_accessed(true);
1390  #endif
1391 
1392  return *this;
1393  }
1394 
1395  Config::~Config()
1396  {
1397  free();
1398  }
1399 
1400  void Config::free()
1401  {
1402  #if CONFIGURU_VALUE_SEMANTICS
1403  if (_type == BadLookupType) {
1404  delete _u.bad_lookup;
1405  } else if (_type == Object) {
1406  delete _u.object;
1407  } else if (_type == Array) {
1408  delete _u.array;
1409  } else if (_type == String) {
1410  delete _u.str;
1411  }
1412  #else // !CONFIGURU_VALUE_SEMANTICS:
1413  if (_type == BadLookupType) {
1414  if (--_u.bad_lookup->_ref_count == 0) {
1415  delete _u.bad_lookup;
1416  }
1417  } else if (_type == Object) {
1418  if (--_u.object->_ref_count == 0) {
1419  delete _u.object;
1420  }
1421  } else if (_type == Array) {
1422  if (--_u.array->_ref_count == 0) {
1423  delete _u.array;
1424  }
1425  } else if (_type == String) {
1426  delete _u.str;
1427  }
1428  #endif // !CONFIGURU_VALUE_SEMANTICS
1429 
1430  _type = Uninitialized;
1431 
1432  // Keep _doc, _line, _comments until overwritten/destructor.
1433  }
1434 
1435  // ------------------------------------------------------------------------
1436 
1437  size_t Config::object_size() const
1438  {
1439  return as_object()._impl.size();
1440  }
1441 
1442  const Config& Config::operator[](const std::string& key) const
1443  {
1444  auto&& object = as_object()._impl;
1445  auto it = object.find(key);
1446  if (it == object.end()) {
1447  on_error("Key '" + key + "' not in object");
1448  } else {
1449  const auto& entry = it->second;
1450  entry._accessed = true;
1451  return entry._value;
1452  }
1453  }
1454 
1455  Config& Config::operator[](const std::string& key)
1456  {
1457  auto&& object = as_object()._impl;
1458  auto&& entry = object[key];
1459  if (entry._nr == BAD_INDEX) {
1460  // New entry
1461  entry._nr = static_cast<Index>(object.size()) - 1;
1462  entry._value._type = BadLookupType;
1463  entry._value._u.bad_lookup = new BadLookupInfo{_doc, _line, key};
1464  } else {
1465  entry._accessed = true;
1466  }
1467  return entry._value;
1468  }
1469 
1470  bool Config::has_key(const std::string& key) const
1471  {
1472  return as_object()._impl.count(key) != 0;
1473  }
1474 
1475  bool Config::emplace(std::string key, Config value)
1476  {
1477  auto&& object = as_object()._impl;
1478  return object.emplace(
1479  std::move(key),
1480  Config::ObjectEntry{std::move(value), (unsigned)object.size()}).second;
1481  }
1482 
1483  void Config::insert_or_assign(const std::string& key, Config&& config)
1484  {
1485  auto&& object = as_object()._impl;
1486  auto&& entry = object[key];
1487  if (entry._nr == BAD_INDEX) {
1488  // New entry
1489  entry._nr = static_cast<Index>(object.size()) - 1;
1490  } else {
1491  entry._accessed = true;
1492  }
1493  entry._value = std::move(config);
1494  }
1495 
1496  bool Config::erase(const std::string& key)
1497  {
1498  auto& object = as_object()._impl;
1499  auto it = object.find(key);
1500  if (it == object.end()) {
1501  return false;
1502  } else {
1503  object.erase(it);
1504  return true;
1505  }
1506  }
1507 
1508  bool Config::deep_eq(const Config& a, const Config& b)
1509  {
1510  if (a._type != b._type) { return false; }
1511  if (a._type == Null) { return true; }
1512  if (a._type == Bool) { return a._u.b == b._u.b; }
1513  if (a._type == Int) { return a._u.i == b._u.i; }
1514  if (a._type == Float) { return a._u.f == b._u.f; }
1515  if (a._type == String) { return *a._u.str == *b._u.str; }
1516  if (a._type == Object) {
1517  if (a._u.object == b._u.object) { return true; }
1518  auto&& a_object = a.as_object()._impl;
1519  auto&& b_object = b.as_object()._impl;
1520  if (a_object.size() != b_object.size()) { return false; }
1521  for (auto&& p: a_object) {
1522  auto it = b_object.find(p.first);
1523  if (it == b_object.end()) { return false; }
1524  if (!deep_eq(p.second._value, it->second._value)) { return false; }
1525  }
1526  return true;
1527  }
1528  if (a._type == Array) {
1529  if (a._u.array == b._u.array) { return true; }
1530  auto&& a_array = a.as_array();
1531  auto&& b_array = b.as_array();
1532  if (a_array.size() != b_array.size()) { return false; }
1533  for (size_t i=0; i<a_array.size(); ++i) {
1534  if (!deep_eq(a_array[i], a_array[i])) {
1535  return false;
1536  }
1537  }
1538  return true;
1539  }
1540 
1541  return false;
1542  }
1543 
1544 #if !CONFIGURU_VALUE_SEMANTICS
1545  Config Config::deep_clone() const
1546  {
1547  Config ret = *this;
1548  if (ret._type == Object) {
1549  ret = Config::object();
1550  for (auto&& p : this->as_object()._impl) {
1551  auto& dst = ret._u.object->_impl[p.first];
1552  dst._nr = p.second._nr;
1553  dst._value = p.second._value.deep_clone();
1554  }
1555  }
1556  if (ret._type == Array) {
1557  ret = Config::array();
1558  for (auto&& value : this->as_array()) {
1559  ret.push_back( value.deep_clone() );
1560  }
1561  }
1562  return ret;
1563  }
1564 #endif
1565 
1566  void Config::visit_dangling(const std::function<void(const std::string& key, const Config& value)>& visitor) const
1567  {
1568  if (is_object()) {
1569  for (auto&& p : as_object()._impl) {
1570  auto&& entry = p.second;
1571  auto&& value = entry._value;
1572  if (entry._accessed) {
1573  value.check_dangling();
1574  } else {
1575  visitor(p.first, value);
1576  }
1577  }
1578  } else if (is_array()) {
1579  for (auto&& e : as_array()) {
1580  e.check_dangling();
1581  }
1582  }
1583  }
1584 
1585  void Config::check_dangling() const
1586  {
1587  std::string message = "";
1588 
1589  visit_dangling([&](const std::string& key, const Config& value){
1590  message += "\n " + value.where() + "Key '" + key + "' never accessed.";
1591  });
1592 
1593  if (!message.empty()) {
1594  message = "Dangling keys:" + message;
1595  CONFIGURU_ON_DANGLING(message);
1596  }
1597  }
1598 
1599  void Config::mark_accessed(bool v) const
1600  {
1601  if (is_object()) {
1602  for (auto&& p : as_object()._impl) {
1603  auto&& entry = p.second;
1604  entry._accessed = v;
1605  entry._value.mark_accessed(v);
1606  }
1607  } else if (is_array()) {
1608  for (auto&& e : as_array()) {
1609  e.mark_accessed(v);
1610  }
1611  }
1612  }
1613 
1614  const char* Config::debug_descr() const
1615  {
1616  switch (_type) {
1617  case Bool: return _u.b ? "true" : "false";
1618  case String: return _u.str->c_str();
1619  default: return type_str(_type);
1620  }
1621  }
1622 
1623  const char* Config::type_str(Type t)
1624  {
1625  switch (t) {
1626  case Uninitialized: return "uninitialized";
1627  case BadLookupType: return "undefined";
1628  case Null: return "null";
1629  case Bool: return "bool";
1630  case Int: return "integer";
1631  case Float: return "float";
1632  case String: return "string";
1633  case Array: return "array";
1634  case Object: return "object";
1635  }
1636  return "BROKEN Config";
1637  }
1638 
1639  std::string where_is(const DocInfo_SP& doc, Index line)
1640  {
1641  if (doc) {
1642  std::string ret = doc->filename;
1643  if (line != BAD_INDEX) {
1644  ret += ":" + std::to_string(line);
1645  }
1646  doc->append_include_info(ret);
1647  ret += ": ";
1648  return ret;
1649  } else if (line != BAD_INDEX) {
1650  return "line " + std::to_string(line) + ": ";
1651  } else {
1652  return "";
1653  }
1654  }
1655 
1656  std::string Config::where() const
1657  {
1658  return where_is(_doc, _line);
1659  }
1660 
1661  void Config::on_error(const std::string& msg) const
1662  {
1663  CONFIGURU_ONERROR(where() + msg);
1664  abort(); // We shouldn't get here.
1665  }
1666 
1667  void Config::assert_type(Type exepected) const
1668  {
1669  if (_type == BadLookupType) {
1670  auto where = where_is(_u.bad_lookup->doc, _u.bad_lookup->line);
1671  CONFIGURU_ONERROR(where + "Failed to find key '" + _u.bad_lookup->key + "'");
1672  } else if (_type != exepected) {
1673  const auto message = where() + "Expected " + type_str(exepected) + ", got " + type_str(_type);
1674  if (_type == Uninitialized && exepected == Object) {
1675  CONFIGURU_ONERROR(message + ". Did you forget to call Config::object()?");
1676  } else if (_type == Uninitialized && exepected == Array) {
1677  CONFIGURU_ONERROR(message + ". Did you forget to call Config::array()?");
1678  } else {
1679  CONFIGURU_ONERROR(message);
1680  }
1681  }
1682  }
1683 
1684  std::ostream& operator<<(std::ostream& os, const Config& cfg)
1685  {
1686  auto format = JSON;
1687  // Make sure that all config types are serializable:
1688  format.inf = true;
1689  format.nan = true;
1690  format.write_uninitialized = true;
1691  format.end_with_newline = false;
1692  format.mark_accessed = false;
1693  return os << dump_string(cfg, format);
1694  }
1695 }
1696 
1697 // ----------------------------------------------------------------------------
1698 // 88""Yb db 88""Yb .dP"Y8 888888 88""Yb
1699 // 88__dP dPYb 88__dP `Ybo." 88__ 88__dP
1700 // 88""" dP__Yb 88"Yb o.`Y8b 88"" 88"Yb
1701 // 88 dP""""Yb 88 Yb 8bodP' 888888 88 Yb
1702 
1703 #include <cerrno>
1704 #include <cstdlib>
1705 
1706 namespace configuru
1707 {
1708  void append(Comments& a, Comments&& b)
1709  {
1710  for (auto&& entry : b) {
1711  a.emplace_back(std::move(entry));
1712  }
1713  }
1714 
1715  bool ConfigComments::empty() const
1716  {
1717  return prefix.empty()
1718  && postfix.empty()
1719  && pre_end_brace.empty();
1720  }
1721 
1722  void ConfigComments::append(ConfigComments&& other)
1723  {
1724  configuru::append(this->prefix, std::move(other.prefix));
1725  configuru::append(this->postfix, std::move(other.postfix));
1726  configuru::append(this->pre_end_brace, std::move(other.pre_end_brace));
1727  }
1728 
1729  // Returns the number of bytes written, or 0 on error
1730  size_t encode_utf8(std::string& dst, uint64_t c)
1731  {
1732  if (c <= 0x7F) // 0XXX XXXX - one byte
1733  {
1734  dst += static_cast<char>(c);
1735  return 1;
1736  }
1737  else if (c <= 0x7FF) // 110X XXXX - two bytes
1738  {
1739  dst += static_cast<char>( 0xC0 | (c >> 6) );
1740  dst += static_cast<char>( 0x80 | (c & 0x3F) );
1741  return 2;
1742  }
1743  else if (c <= 0xFFFF) // 1110 XXXX - three bytes
1744  {
1745  dst += static_cast<char>(0xE0 | (c >> 12));
1746  dst += static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1747  dst += static_cast<char>(0x80 | (c & 0x3F));
1748  return 3;
1749  }
1750  else if (c <= 0x1FFFFF) // 1111 0XXX - four bytes
1751  {
1752  dst += static_cast<char>(0xF0 | (c >> 18));
1753  dst += static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1754  dst += static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1755  dst += static_cast<char>(0x80 | (c & 0x3F));
1756  return 4;
1757  }
1758  else if (c <= 0x3FFFFFF) // 1111 10XX - five bytes
1759  {
1760  dst += static_cast<char>(0xF8 | (c >> 24));
1761  dst += static_cast<char>(0x80 | (c >> 18));
1762  dst += static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1763  dst += static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1764  dst += static_cast<char>(0x80 | (c & 0x3F));
1765  return 5;
1766  }
1767  else if (c <= 0x7FFFFFFF) // 1111 110X - six bytes
1768  {
1769  dst += static_cast<char>(0xFC | (c >> 30));
1770  dst += static_cast<char>(0x80 | ((c >> 24) & 0x3F));
1771  dst += static_cast<char>(0x80 | ((c >> 18) & 0x3F));
1772  dst += static_cast<char>(0x80 | ((c >> 12) & 0x3F));
1773  dst += static_cast<char>(0x80 | ((c >> 6) & 0x3F));
1774  dst += static_cast<char>(0x80 | (c & 0x3F));
1775  return 6;
1776  }
1777  else {
1778  return 0; // Error
1779  }
1780  }
1781 
1782  std::string quote(char c)
1783  {
1784  if (c == 0) { return "<eof>"; }
1785  if (c == ' ') { return "<space>"; }
1786  if (c == '\n') { return "'\\n'"; }
1787  if (c == '\t') { return "'\\t'"; }
1788  if (c == '\r') { return "'\\r'"; }
1789  if (c == '\b') { return "'\\b'"; }
1790  return std::string("'") + c + "'";
1791  }
1792 
1793  struct State
1794  {
1795  const char* ptr;
1796  unsigned line_nr;
1797  const char* line_start;
1798  };
1799 
1800  struct Parser
1801  {
1802  Parser(const char* str, const FormatOptions& options, DocInfo_SP doc, ParseInfo& info);
1803 
1804  bool skip_white(Comments* out_comments, int& out_indentation, bool break_on_newline);
1805 
1806  bool skip_white_ignore_comments()
1807  {
1808  int indentation;
1809  return skip_white(nullptr, indentation, false);
1810  }
1811 
1812  bool skip_pre_white(Config* config, int& out_indentation)
1813  {
1814  if (!MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
1815  // Early out
1816  out_indentation = -1;
1817  return false;
1818  }
1819 
1820  Comments comments;
1821  bool did_skip = skip_white(&comments, out_indentation, false);
1822  if (!comments.empty()) {
1823  append(config->comments().prefix, std::move(comments));
1824  }
1825  return did_skip;
1826  }
1827 
1828  bool skip_post_white(Config* config)
1829  {
1830  if (!MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
1831  // Early out
1832  return false;
1833  }
1834 
1835  Comments comments;
1836  int indentation;
1837  bool did_skip = skip_white(&comments, indentation, true);
1838  if (!comments.empty()) {
1839  append(config->comments().postfix, std::move(comments));
1840  }
1841  return did_skip;
1842  }
1843 
1844  Config top_level();
1845  void parse_value(Config& out, bool* out_did_skip_postwhites);
1846  void parse_array(Config& dst);
1847  void parse_array_contents(Config& dst);
1848  void parse_object(Config& dst);
1849  void parse_object_contents(Config& dst);
1850  void parse_int(Config& out);
1851  void parse_float(Config& out);
1852  void parse_finite_number(Config& dst);
1853  std::string parse_string();
1854  std::string parse_c_sharp_string();
1855  uint64_t parse_hex(int count);
1856  void parse_macro(Config& dst);
1857 
1858  void tag(Config& var)
1859  {
1860  var.tag(_doc, _line_nr, column());
1861  }
1862 
1863  State get_state() const
1864  {
1865  return { _ptr, _line_nr, _line_start };
1866  }
1867 
1868  void set_state(State s) {
1869  _ptr = s.ptr;
1870  _line_nr = s.line_nr;
1871  _line_start = s.line_start;
1872  }
1873 
1874  Index column() const
1875  {
1876  return static_cast<unsigned>(_ptr - _line_start + 1);
1877  }
1878 
1879  const char* start_of_line() const
1880  {
1881  return _line_start;
1882  }
1883 
1884  const char* end_of_line() const
1885  {
1886  const char* p = _ptr;
1887  while (*p && *p != '\r' && *p != '\n') {
1888  ++p;
1889  }
1890  return p;
1891  }
1892 
1893  void throw_error(const std::string& desc) CONFIGURU_NORETURN {
1894  const char* sol = start_of_line();
1895  const char* eol = end_of_line();
1896  std::string orientation;
1897  for (const char* p = sol; p != eol; ++p) {
1898  if (*p == '\t') {
1899  orientation += " ";
1900  } else {
1901  orientation.push_back(*p);
1902  }
1903  }
1904 
1905  orientation += "\n";
1906  for (const char* p = sol; p != _ptr; ++p) {
1907  if (*p == '\t') {
1908  orientation += " ";
1909  } else {
1910  orientation.push_back(' ');
1911  }
1912  }
1913  orientation += "^";
1914 
1915  throw ParseError(_doc, _line_nr, column(), desc + "\n" + orientation);
1916  }
1917 
1918  void throw_indentation_error(int found_tabs, int expected_tabs) {
1919  if (_options.enforce_indentation) {
1920  char buff[128];
1921  snprintf(buff, sizeof(buff), "Bad indentation: expected %d tabs, found %d", found_tabs, expected_tabs);
1922  throw_error(buff);
1923  }
1924  }
1925 
1926  void parse_assert(bool b, const char* error_msg) {
1927  if (!b) {
1928  throw_error(error_msg);
1929  }
1930  }
1931 
1932  void parse_assert(bool b, const char* error_msg, const State& error_state) {
1933  if (!b) {
1934  set_state(error_state);
1935  throw_error(error_msg);
1936  }
1937  }
1938 
1939  void swallow(char c) {
1940  if (_ptr[0] == c) {
1941  _ptr += 1;
1942  } else {
1943  throw_error("Expected " + quote(c));
1944  }
1945  }
1946 
1947  bool try_swallow(const char* str) {
1948  auto n = strlen(str);
1949  if (strncmp(str, _ptr, n) == 0) {
1950  _ptr += n;
1951  return true;
1952  } else {
1953  return false;
1954  }
1955  }
1956 
1957  void swallow(const char* str, const char* error_msg) {
1958  parse_assert(try_swallow(str), error_msg);
1959  }
1960 
1961  bool is_reserved_identifier(const char* ptr)
1962  {
1963  if (strncmp(ptr, "true", 4)==0 || strncmp(ptr, "null", 4)==0) {
1964  return !IDENT_CHARS[static_cast<uint8_t>(ptr[4])];
1965  } else if (strncmp(ptr, "false", 5)==0) {
1966  return !IDENT_CHARS[static_cast<uint8_t>(ptr[5])];
1967  } else {
1968  return false;
1969  }
1970  }
1971 
1972  private:
1973  bool IDENT_STARTERS[256] = { 0 };
1974  bool IDENT_CHARS[256] = { 0 };
1975  bool MAYBE_WHITE[256] = { 0 };
1976  bool SPECIAL_CHARACTERS[256] = { 0 };
1977 
1978  private:
1979  FormatOptions _options;
1980  DocInfo_SP _doc;
1981  ParseInfo& _info;
1982 
1983  const char* _ptr;
1984  Index _line_nr;
1985  const char* _line_start;
1986  int _indentation = 0; // Expected number of tabs between a \n and the next key/value
1987  };
1988 
1989  // --------------------------------------------
1990 
1991  // Sets an inclusive range
1992  void set_range(bool lookup[256], char a, char b)
1993  {
1994  for (char c=a; c<=b; ++c) {
1995  lookup[static_cast<uint8_t>(c)] = true;
1996  }
1997  }
1998 
1999  Parser::Parser(const char* str, const FormatOptions& options, DocInfo_SP doc, ParseInfo& info) : _doc(doc), _info(info)
2000  {
2001  _options = options;
2002  _line_nr = 1;
2003  _ptr = str;
2004  _line_start = str;
2005 
2006  IDENT_STARTERS[static_cast<uint8_t>('_')] = true;
2007  set_range(IDENT_STARTERS, 'a', 'z');
2008  set_range(IDENT_STARTERS, 'A', 'Z');
2009 
2010  IDENT_CHARS[static_cast<uint8_t>('_')] = true;
2011  set_range(IDENT_CHARS, 'a', 'z');
2012  set_range(IDENT_CHARS, 'A', 'Z');
2013  set_range(IDENT_CHARS, '0', '9');
2014 
2015  MAYBE_WHITE[static_cast<uint8_t>('\n')] = true;
2016  MAYBE_WHITE[static_cast<uint8_t>('\r')] = true;
2017  MAYBE_WHITE[static_cast<uint8_t>('\t')] = true;
2018  MAYBE_WHITE[static_cast<uint8_t>(' ')] = true;
2019  MAYBE_WHITE[static_cast<uint8_t>('/')] = true; // Maybe a comment
2020 
2021  SPECIAL_CHARACTERS[static_cast<uint8_t>('\0')] = true;
2022  SPECIAL_CHARACTERS[static_cast<uint8_t>('\\')] = true;
2023  SPECIAL_CHARACTERS[static_cast<uint8_t>('\"')] = true;
2024  SPECIAL_CHARACTERS[static_cast<uint8_t>('\n')] = true;
2025  SPECIAL_CHARACTERS[static_cast<uint8_t>('\t')] = true;
2026 
2027  CONFIGURU_ASSERT(_options.indentation != "" || !_options.enforce_indentation);
2028  }
2029 
2030  // Returns true if we did skip white-space.
2031  // out_indentation is the depth of indentation on the last line we did skip on.
2032  // iff out_indentation is -1 there is a non-tab on the last line.
2033  bool Parser::skip_white(Comments* out_comments, int& out_indentation, bool break_on_newline)
2034  {
2035  auto start_ptr = _ptr;
2036  out_indentation = 0;
2037  bool found_newline = false;
2038 
2039  const std::string& indentation = _options.indentation;
2040 
2041  while (MAYBE_WHITE[static_cast<uint8_t>(_ptr[0])]) {
2042  if (_ptr[0] == '\n') {
2043  // Unix style newline
2044  _ptr += 1;
2045  _line_nr += 1;
2046  _line_start = _ptr;
2047  out_indentation = 0;
2048  if (break_on_newline) { return true; }
2049  found_newline = true;
2050  }
2051  else if (_ptr[0] == '\r') {
2052  // CR-LF - windows style newline
2053  parse_assert(_ptr[1] == '\n', "CR with no LF. \\r only allowed before \\n."); // TODO: this is OK in JSON.
2054  _ptr += 2;
2055  _line_nr += 1;
2056  _line_start = _ptr;
2057  out_indentation = 0;
2058  if (break_on_newline) { return true; }
2059  found_newline = true;
2060  }
2061  else if (!indentation.empty() &&
2062  strncmp(_ptr, indentation.c_str(), indentation.size()) == 0)
2063  {
2064  _ptr += indentation.size();
2065  if (_options.enforce_indentation && indentation == "\t") {
2066  parse_assert(out_indentation != -1, "Tabs should only occur on the start of a line!");
2067  }
2068  ++out_indentation;
2069  }
2070  else if (_ptr[0] == '\t') {
2071  ++_ptr;
2072  if (_options.enforce_indentation) {
2073  parse_assert(out_indentation != -1, "Tabs should only occur on the start of a line!");
2074  }
2075  ++out_indentation;
2076  }
2077  else if (_ptr[0] == ' ') {
2078  if (found_newline && _options.enforce_indentation) {
2079  if (indentation == "\t") {
2080  throw_error("Found a space at beginning of a line. Indentation must be done using tabs!");
2081  } else {
2082  throw_error("Indentation should be a multiple of " + std::to_string(indentation.size()) + " spaces.");
2083  }
2084  }
2085  ++_ptr;
2086  out_indentation = -1;
2087  }
2088  else if (_ptr[0] == '/' && _ptr[1] == '/') {
2089  parse_assert(_options.single_line_comments, "Single line comments forbidden.");
2090  // Single line comment
2091  auto start = _ptr;
2092  _ptr += 2;
2093  while (_ptr[0] && _ptr[0] != '\n') {
2094  _ptr += 1;
2095  }
2096  if (out_comments) { out_comments->emplace_back(start, _ptr - start); }
2097  out_indentation = 0;
2098  if (break_on_newline) { return true; }
2099  }
2100  else if (_ptr[0] == '/' && _ptr[1] == '*') {
2101  parse_assert(_options.block_comments, "Block comments forbidden.");
2102  // Multi-line comment
2103  auto state = get_state(); // So we can point out the start if there's an error
2104  _ptr += 2;
2105  unsigned nesting = 1; // We allow nested /**/ comments
2106  do
2107  {
2108  if (_ptr[0]==0) {
2109  set_state(state);
2110  throw_error("Non-ending /* comment");
2111  }
2112  else if (_ptr[0]=='/' && _ptr[1]=='*') {
2113  _ptr += 2;
2114  parse_assert(_options.nesting_block_comments, "Nesting comments (/* /* */ */) forbidden.");
2115  nesting += 1;
2116  }
2117  else if (_ptr[0]=='*' && _ptr[1]=='/') {
2118  _ptr += 2;
2119  nesting -= 1;
2120  }
2121  else if (_ptr[0] == '\n') {
2122  _ptr += 1;
2123  _line_nr += 1;
2124  _line_start = _ptr;
2125  } else {
2126  _ptr += 1;
2127  }
2128  } while (nesting > 0);
2129  if (out_comments) { out_comments->emplace_back(state.ptr, _ptr - state.ptr); }
2130  out_indentation = -1;
2131  if (break_on_newline) { return true; }
2132  }
2133  else {
2134  break;
2135  }
2136  }
2137 
2138  if (start_ptr == _ptr) {
2139  out_indentation = -1;
2140  return false;
2141  } else {
2142  return true;
2143  }
2144  }
2145 
2146  /*
2147  The top-level can be any value, OR the innerds of an object:
2148  foo = 1
2149  "bar": 2
2150  */
2151  Config Parser::top_level()
2152  {
2153  bool is_object = false;
2154 
2155  if (_options.implicit_top_object)
2156  {
2157  auto state = get_state();
2158  skip_white_ignore_comments();
2159 
2160  if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2161  is_object = true;
2162  } else if (_ptr[0] == '"' || _ptr[0] == '@') {
2163  parse_string();
2164  skip_white_ignore_comments();
2165  is_object = (_ptr[0] == ':' || _ptr[0] == '=');
2166  }
2167 
2168  set_state(state); // restore
2169  }
2170 
2171  Config ret;
2172  tag(ret);
2173 
2174  if (is_object) {
2175  parse_object_contents(ret);
2176  } else {
2177  parse_array_contents(ret);
2178  parse_assert(ret.array_size() <= 1 || _options.implicit_top_array, "Multiple values not allowed without enclosing []");
2179  }
2180 
2181  skip_post_white(&ret);
2182 
2183  parse_assert(_ptr[0] == 0, "Expected EoF");
2184 
2185  if (!is_object && ret.array_size() == 0) {
2186  if (_options.empty_file) {
2187  auto empty_object = Config::object();
2188  if (ret.has_comments()) {
2189  empty_object.comments() = std::move(ret.comments());
2190  }
2191  return empty_object;
2192  } else {
2193  throw_error("Empty file");
2194  }
2195  }
2196 
2197  if (!is_object && ret.array_size() == 1) {
2198  // A single value - not an array after all:
2199  Config first( std::move(ret[0]) );
2200  if (ret.has_comments()) {
2201  first.comments().append(std::move(ret.comments()));
2202  }
2203  return first;
2204  }
2205 
2206  return ret;
2207  }
2208 
2209  void Parser::parse_value(Config& dst, bool* out_did_skip_postwhites)
2210  {
2211  int line_indentation;
2212  skip_pre_white(&dst, line_indentation);
2213  tag(dst);
2214 
2215  if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2216  throw_indentation_error(_indentation - 1, line_indentation);
2217  }
2218 
2219  if (_ptr[0] == '"' || _ptr[0] == '@') {
2220  dst = parse_string();
2221  }
2222  else if (_ptr[0] == 'n') {
2223  parse_assert(_ptr[1]=='u' && _ptr[2]=='l' && _ptr[3]=='l', "Expected 'null'");
2224  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])], "Expected 'null'");
2225  _ptr += 4;
2226  dst = nullptr;
2227  }
2228  else if (_ptr[0] == 't') {
2229  parse_assert(_ptr[1]=='r' && _ptr[2]=='u' && _ptr[3]=='e', "Expected 'true'");
2230  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])], "Expected 'true'");
2231  _ptr += 4;
2232  dst = true;
2233  }
2234  else if (_ptr[0] == 'f') {
2235  parse_assert(_ptr[1]=='a' && _ptr[2]=='l' && _ptr[3]=='s' && _ptr[4]=='e', "Expected 'false'");
2236  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[5])], "Expected 'false'");
2237  _ptr += 5;
2238  dst = false;
2239  }
2240  else if (_ptr[0] == '{') {
2241  parse_object(dst);
2242  }
2243  else if (_ptr[0] == '[') {
2244  parse_array(dst);
2245  }
2246  else if (_ptr[0] == '#') {
2247  parse_macro(dst);
2248  }
2249  else if (_ptr[0] == '+' || _ptr[0] == '-' || _ptr[0] == '.' || ('0' <= _ptr[0] && _ptr[0] <= '9')) {
2250  // Some kind of number:
2251 
2252  if (_ptr[0] == '-' && _ptr[1] == 'i' && _ptr[2]=='n' && _ptr[3]=='f') {
2253  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])], "Expected -inf");
2254  parse_assert(_options.inf, "infinity forbidden.");
2255  _ptr += 4;
2256  dst = -std::numeric_limits<double>::infinity();
2257  }
2258  else if (_ptr[0] == '+' && _ptr[1] == 'i' && _ptr[2]=='n' && _ptr[3]=='f') {
2259  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])], "Expected +inf");
2260  parse_assert(_options.inf, "infinity forbidden.");
2261  _ptr += 4;
2262  dst = std::numeric_limits<double>::infinity();
2263  }
2264  else if (_ptr[0] == '+' && _ptr[1] == 'N' && _ptr[2]=='a' && _ptr[3]=='N') {
2265  parse_assert(!IDENT_CHARS[static_cast<uint8_t>(_ptr[4])], "Expected +NaN");
2266  parse_assert(_options.nan, "NaN (Not a Number) forbidden.");
2267  _ptr += 4;
2268  dst = std::numeric_limits<double>::quiet_NaN();
2269  } else {
2270  parse_finite_number(dst);
2271  }
2272  } else {
2273  throw_error("Expected value");
2274  }
2275 
2276  *out_did_skip_postwhites = skip_post_white(&dst);
2277  }
2278 
2279  void Parser::parse_array(Config& array)
2280  {
2281  auto state = get_state();
2282 
2283  swallow('[');
2284 
2285  _indentation += 1;
2286  parse_array_contents(array);
2287  _indentation -= 1;
2288 
2289  if (_ptr[0] == ']') {
2290  _ptr += 1;
2291  } else {
2292  set_state(state);
2293  throw_error("Non-terminated array");
2294  }
2295  }
2296 
2297  void Parser::parse_array_contents(Config& array_cfg)
2298  {
2299  array_cfg.make_array();
2300  auto& array_impl = array_cfg.as_array();
2301 
2302  Comments next_prefix_comments;
2303 
2304  for (;;)
2305  {
2306  Config value;
2307  if (!next_prefix_comments.empty()) {
2308  std::swap(value.comments().prefix, next_prefix_comments);
2309  }
2310  int line_indentation;
2311  skip_pre_white(&value, line_indentation);
2312 
2313  if (_ptr[0] == ']') {
2314  if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2315  throw_indentation_error(_indentation - 1, line_indentation);
2316  }
2317  if (value.has_comments()) {
2318  array_cfg.comments().pre_end_brace = value.comments().prefix;
2319  }
2320  break;
2321  }
2322 
2323  if (!_ptr[0]) {
2324  if (value.has_comments()) {
2325  array_cfg.comments().pre_end_brace = value.comments().prefix;
2326  }
2327  break;
2328  }
2329 
2330  if (line_indentation >= 0 && _indentation != line_indentation) {
2331  throw_indentation_error(_indentation, line_indentation);
2332  }
2333 
2334  if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2335  throw_error("Found identifier; expected value. Did you mean to use a {object} rather than a [array]?");
2336  }
2337 
2338  bool has_separator;
2339  parse_value(value, &has_separator);
2340  int ignore;
2341  skip_white(&next_prefix_comments, ignore, false);
2342 
2343  auto comma_state = get_state();
2344  bool has_comma = _ptr[0] == ',';
2345 
2346  if (has_comma) {
2347  _ptr += 1;
2348  skip_post_white(&value);
2349  has_separator = true;
2350  }
2351 
2352  array_impl.emplace_back(std::move(value));
2353 
2354  bool is_last_element = !_ptr[0] || _ptr[0] == ']';
2355 
2356  if (is_last_element) {
2357  parse_assert(!has_comma || _options.array_trailing_comma,
2358  "Trailing comma forbidden.", comma_state);
2359  } else {
2360  if (_options.array_omit_comma) {
2361  parse_assert(has_separator, "Expected a space, newline, comma or ]");
2362  } else {
2363  parse_assert(has_comma, "Expected a comma or ]");
2364  }
2365  }
2366  }
2367  }
2368 
2369  void Parser::parse_object(Config& object)
2370  {
2371  auto state = get_state();
2372 
2373  swallow('{');
2374 
2375  _indentation += 1;
2376  parse_object_contents(object);
2377  _indentation -= 1;
2378 
2379  if (_ptr[0] == '}') {
2380  _ptr += 1;
2381  } else {
2382  set_state(state);
2383  throw_error("Non-terminated object");
2384  }
2385  }
2386 
2387  void Parser::parse_object_contents(Config& object)
2388  {
2389  object.make_object();
2390 
2391  Comments next_prefix_comments;
2392 
2393  for (;;)
2394  {
2395  Config value;
2396  if (!next_prefix_comments.empty()) {
2397  std::swap(value.comments().prefix, next_prefix_comments);
2398  }
2399  int line_indentation;
2400  skip_pre_white(&value, line_indentation);
2401 
2402  if (_ptr[0] == '}') {
2403  if (line_indentation >= 0 && _indentation - 1 != line_indentation) {
2404  throw_indentation_error(_indentation - 1, line_indentation);
2405  }
2406  if (value.has_comments()) {
2407  object.comments().pre_end_brace = value.comments().prefix;
2408  }
2409  break;
2410  }
2411 
2412  if (!_ptr[0]) {
2413  if (value.has_comments()) {
2414  object.comments().pre_end_brace = value.comments().prefix;
2415  }
2416  break;
2417  }
2418 
2419  if (line_indentation >= 0 && _indentation != line_indentation) {
2420  throw_indentation_error(_indentation, line_indentation);
2421  }
2422 
2423  auto pre_key_state = get_state();
2424  std::string key;
2425 
2426  if (IDENT_STARTERS[static_cast<uint8_t>(_ptr[0])] && !is_reserved_identifier(_ptr)) {
2427  parse_assert(_options.identifiers_keys, "You need to surround keys with quotes");
2428  while (IDENT_CHARS[static_cast<uint8_t>(_ptr[0])]) {
2429  key += _ptr[0];
2430  _ptr += 1;
2431  }
2432  }
2433  else if (_ptr[0] == '"' || _ptr[0] == '@') {
2434  key = parse_string();
2435  } else {
2436  throw_error("Object key expected (either an identifier or a quoted string), got " + quote(_ptr[0]));
2437  }
2438 
2439  if (!_options.object_duplicate_keys && object.has_key(key)) {
2440  set_state(pre_key_state);
2441  throw_error("Duplicate key: \"" + key + "\". Already set at " + object[key].where());
2442  }
2443 
2444  bool space_after_key = skip_white_ignore_comments();
2445 
2446  if (_ptr[0] == ':' || (_options.object_separator_equal && _ptr[0] == '=')) {
2447  parse_assert(_options.allow_space_before_colon || _ptr[0] != ':' || !space_after_key, "No space allowed before colon");
2448  _ptr += 1;
2449  skip_white_ignore_comments();
2450  } else if (_options.omit_colon_before_object && (_ptr[0] == '{' || _ptr[0] == '#')) {
2451  // Ok to omit : in this case
2452  } else {
2453  if (_options.object_separator_equal && _options.omit_colon_before_object) {
2454  throw_error("Expected one of '=', ':', '{' or '#' after object key");
2455  } else {
2456  throw_error("Expected : after object key");
2457  }
2458  }
2459 
2460  bool has_separator;
2461  parse_value(value, &has_separator);
2462  int ignore;
2463  skip_white(&next_prefix_comments, ignore, false);
2464 
2465  auto comma_state = get_state();
2466  bool has_comma = _ptr[0] == ',';
2467 
2468  if (has_comma) {
2469  _ptr += 1;
2470  skip_post_white(&value);
2471  has_separator = true;
2472  }
2473 
2474  object.emplace(std::move(key), std::move(value));
2475 
2476  bool is_last_element = !_ptr[0] || _ptr[0] == '}';
2477 
2478  if (is_last_element) {
2479  parse_assert(!has_comma || _options.object_trailing_comma,
2480  "Trailing comma forbidden.", comma_state);
2481  } else {
2482  if (_options.object_omit_comma) {
2483  parse_assert(has_separator, "Expected a space, newline, comma or }");
2484  } else {
2485  parse_assert(has_comma, "Expected a comma or }");
2486  }
2487  }
2488  }
2489  }
2490 
2491  void Parser::parse_int(Config& out)
2492  {
2493  const auto start = _ptr;
2494  const auto result = strtoll(start, const_cast<char**>(&_ptr), 10);
2495  parse_assert(start < _ptr, "Invalid integer");
2496  parse_assert(start[0] != '0' || result == 0, "Integer may not start with a zero");
2497  out = result;
2498  }
2499 
2500  void Parser::parse_float(Config& out)
2501  {
2502  const auto start = _ptr;
2503  const double result = strtod(start, const_cast<char**>(&_ptr));
2504  parse_assert(start < _ptr, "Invalid number");
2505  out = result;
2506  }
2507 
2508  void Parser::parse_finite_number(Config& out)
2509  {
2510  const auto pre_sign = _ptr;
2511  int sign = +1;
2512 
2513  if (_ptr[0] == '+') {
2514  parse_assert(_options.unary_plus, "Prefixing numbers with + is forbidden.");
2515  _ptr += 1;
2516  }
2517  if (_ptr[0] == '-') {
2518  _ptr += 1;
2519  sign = -1;
2520  }
2521 
2522  parse_assert(_ptr[0] != '+' && _ptr[0] != '-', "Duplicate sign");
2523 
2524  // Check if it's an integer:
2525  if (_ptr[0] == '0' && _ptr[1] == 'x') {
2526  parse_assert(_options.hexadecimal_integers, "Hexadecimal numbers forbidden.");
2527  _ptr += 2;
2528  auto start = _ptr;
2529  out = sign * static_cast<int64_t>(strtoull(start, const_cast<char**>(&_ptr), 16));
2530  parse_assert(start < _ptr, "Missing hexaxdecimal digits after 0x");
2531  return;
2532  }
2533 
2534  if (_ptr[0] == '0' && _ptr[1] == 'b') {
2535  parse_assert(_options.binary_integers, "Binary numbers forbidden.");
2536  _ptr += 2;
2537  auto start = _ptr;
2538  out = sign * static_cast<int64_t>(strtoull(start, const_cast<char**>(&_ptr), 2));
2539  parse_assert(start < _ptr, "Missing binary digits after 0b");
2540  return;
2541  }
2542 
2543  const char* p = _ptr;
2544 
2545  while ('0' <= *p && *p <= '9') {
2546  p += 1;
2547  }
2548 
2549  if (*p == '.' || *p == 'e' || *p == 'E') {
2550  _ptr = pre_sign;
2551  return parse_float(out);
2552  }
2553 
2554  // It looks like an integer - but it may be too long to represent as one!
2555  const auto MAX_INT_STR = (sign == +1 ? "9223372036854775807" : "9223372036854775808");
2556 
2557  const auto length = p - _ptr;
2558 
2559  if (length < 19) {
2560  _ptr = pre_sign;
2561  return parse_int(out);
2562  }
2563 
2564  if (length > 19) {
2565  _ptr = pre_sign;
2566  return parse_float(out); // Uncommon case optimization
2567  }
2568 
2569  // Compare fast:
2570  for (int i = 0; i < 19; ++i)
2571  {
2572  if (_ptr[i] > MAX_INT_STR[i]) {
2573  _ptr = pre_sign;
2574  return parse_float(out);
2575  }
2576  if (_ptr[i] < MAX_INT_STR[i]) {
2577  _ptr = pre_sign;
2578  return parse_int(out);
2579  }
2580  }
2581  _ptr = pre_sign;
2582  return parse_int(out); // Exactly max int
2583  }
2584 
2585  std::string Parser::parse_c_sharp_string()
2586  {
2587  // C# style verbatim string - everything until the next " except "" which is ":
2588  auto state = get_state();
2589  parse_assert(_options.str_csharp_verbatim, "C# @-style verbatim strings forbidden.");
2590  swallow('@');
2591  swallow('"');
2592 
2593  std::string str;
2594 
2595  for (;;) {
2596  if (_ptr[0] == 0) {
2597  set_state(state);
2598  throw_error("Unterminated verbatim string");
2599  }
2600  else if (_ptr[0] == '\n') {
2601  throw_error("Newline in verbatim string");
2602  }
2603  else if (_ptr[0] == '"' && _ptr[1] == '"') {
2604  // Escaped quote
2605  _ptr += 2;
2606  str.push_back('"');
2607  }
2608  else if (_ptr[0] == '"') {
2609  _ptr += 1;
2610  return str;
2611  }
2612  else {
2613  str += _ptr[0];
2614  _ptr += 1;
2615  }
2616  }
2617  }
2618 
2619  std::string Parser::parse_string()
2620  {
2621  if (_ptr[0] == '@') {
2622  return parse_c_sharp_string();
2623  }
2624 
2625  auto state = get_state();
2626  parse_assert(_ptr[0] == '"', "Quote (\") expected");
2627 
2628  if (_ptr[1] == '"' && _ptr[2] == '"') {
2629  // Python style multiline string - everything until the next """:
2630  parse_assert(_options.str_python_multiline, "Python \"\"\"-style multiline strings forbidden.");
2631  _ptr += 3;
2632  const char* start = _ptr;
2633  for (;;) {
2634  if (_ptr[0]==0 || _ptr[1]==0 || _ptr[2]==0) {
2635  set_state(state);
2636  throw_error("Unterminated multiline string");
2637  }
2638 
2639  if (_ptr[0] == '"' && _ptr[1] == '"' && _ptr[2] == '"' && _ptr[3] != '"') {
2640  std::string str(start, _ptr);
2641  _ptr += 3;
2642  return str;
2643  }
2644 
2645  if (_ptr[0] == '\n') {
2646  _ptr += 1;
2647  _line_nr += 1;
2648  _line_start = _ptr;
2649  } else {
2650  _ptr += 1;
2651  }
2652  }
2653  } else {
2654  // Normal string
2655  _ptr += 1; // Swallow quote
2656 
2657  std::string str;
2658 
2659  for (;;) {
2660  // Handle larges swats of safe characters at once:
2661  auto safe_end = _ptr;
2662  while (!SPECIAL_CHARACTERS[static_cast<uint8_t>(*safe_end)]) {
2663  ++safe_end;
2664  }
2665 
2666  if (_ptr != safe_end) {
2667  str.append(_ptr, safe_end - _ptr);
2668  _ptr = safe_end;
2669  }
2670 
2671  if (_ptr[0] == 0) {
2672  set_state(state);
2673  throw_error("Unterminated string");
2674  }
2675  if (_ptr[0] == '"') {
2676  _ptr += 1;
2677  return str;
2678  }
2679  if (_ptr[0] == '\n') {
2680  throw_error("Newline in string");
2681  }
2682  if (_ptr[0] == '\t') {
2683  parse_assert(_options.str_allow_tab, "Un-escaped tab not allowed in string");
2684  }
2685 
2686  if (_ptr[0] == '\\') {
2687  // Escape sequence
2688  _ptr += 1;
2689 
2690  if (_ptr[0] == '"') {
2691  str.push_back('"');
2692  _ptr += 1;
2693  } else if (_ptr[0] == '\\') {
2694  str.push_back('\\');
2695  _ptr += 1;
2696  } else if (_ptr[0] == '/') {
2697  str.push_back('/');
2698  _ptr += 1;
2699  } else if (_ptr[0] == 'b') {
2700  str.push_back('\b');
2701  _ptr += 1;
2702  } else if (_ptr[0] == 'f') {
2703  str.push_back('\f');
2704  _ptr += 1;
2705  } else if (_ptr[0] == 'n') {
2706  str.push_back('\n');
2707  _ptr += 1;
2708  } else if (_ptr[0] == 'r') {
2709  str.push_back('\r');
2710  _ptr += 1;
2711  } else if (_ptr[0] == 't') {
2712  str.push_back('\t');
2713  _ptr += 1;
2714  } else if (_ptr[0] == 'u') {
2715  // Four hexadecimal characters
2716  _ptr += 1;
2717  uint64_t codepoint = parse_hex(4);
2718 
2719  if (0xD800 <= codepoint and codepoint <= 0xDBFF)
2720  {
2721  // surrogate pair
2722  parse_assert(_ptr[0] == '\\' && _ptr[1] == 'u',
2723  "Missing second unicode surrogate.");
2724  _ptr += 2;
2725  uint64_t codepoint2 = parse_hex(4);
2726  parse_assert(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF, "Invalid second unicode surrogate");
2727  codepoint = (codepoint << 10) + codepoint2 - 0x35FDC00;
2728  }
2729 
2730  auto num_bytes_written = encode_utf8(str, codepoint);
2731  parse_assert(num_bytes_written > 0, "Bad unicode codepoint");
2732  } else if (_ptr[0] == 'U') {
2733  // Eight hexadecimal characters
2734  parse_assert(_options.str_32bit_unicode, "\\U 32 bit unicodes forbidden.");
2735  _ptr += 1;
2736  uint64_t unicode = parse_hex(8);
2737  auto num_bytes_written = encode_utf8(str, unicode);
2738  parse_assert(num_bytes_written > 0, "Bad unicode codepoint");
2739  } else {
2740  throw_error("Unknown escape character " + quote(_ptr[0]));
2741  }
2742  } else {
2743  str.push_back(_ptr[0]);
2744  _ptr += 1;
2745  }
2746  }
2747  }
2748  }
2749 
2750  uint64_t Parser::parse_hex(int count)
2751  {
2752  uint64_t ret = 0;
2753  for (int i=0; i<count; ++i) {
2754  ret *= 16;
2755  char c = _ptr[i];
2756  if ('0' <= c && c <= '9') {
2757  ret += static_cast<uint64_t>(c - '0');
2758  } else if ('a' <= c && c <= 'f') {
2759  ret += static_cast<uint64_t>(10 + c - 'a');
2760  } else if ('A' <= c && c <= 'F') {
2761  ret += static_cast<uint64_t>(10 + c - 'A');
2762  } else {
2763  throw_error("Expected hexadecimal digit, got " + quote(_ptr[0]));
2764  }
2765  }
2766  _ptr += count;
2767  return ret;
2768  }
2769 
2770  void Parser::parse_macro(Config& dst)
2771  {
2772  parse_assert(_options.allow_macro, "#macros forbidden.");
2773 
2774  swallow("#include", "Expected '#include'");
2775  skip_white_ignore_comments();
2776 
2777  bool absolute;
2778  char terminator;
2779 
2780  if (_ptr[0] == '"') {
2781  absolute = false;
2782  terminator = '"';
2783  } else if (_ptr[0] == '<') {
2784  absolute = true;
2785  terminator = '>';
2786  } else {
2787  throw_error("Expected \" or <");
2788  }
2789 
2790  auto state = get_state();
2791  _ptr += 1;
2792  auto start = _ptr;
2793  std::string path;
2794  for (;;) {
2795  if (_ptr[0] == 0) {
2796  set_state(state);
2797  throw_error("Unterminated include path");
2798  } else if (_ptr[0] == terminator) {
2799  path = std::string(start, static_cast<size_t>(_ptr - start));
2800  _ptr += 1;
2801  break;
2802  } else if (_ptr[0] == '\n') {
2803  throw_error("Newline in string");
2804  } else {
2805  _ptr += 1;
2806  }
2807  }
2808 
2809  if (!absolute) {
2810  auto my_path = _doc->filename;
2811  auto pos = my_path.find_last_of('/');
2812  if (pos != std::string::npos) {
2813  auto my_dir = my_path.substr(0, pos+1);
2814  path = my_dir + path;
2815  }
2816  }
2817 
2818  auto it = _info.parsed_files.find(path);
2819  if (it == _info.parsed_files.end()) {
2820  auto child_doc = std::make_shared<DocInfo>(path);
2821  child_doc->includers.emplace_back(_doc, _line_nr);
2822  dst = parse_file(path.c_str(), _options, child_doc, _info);
2823  _info.parsed_files[path] = dst;
2824  } else {
2825  auto child_doc = it->second.doc();
2826  child_doc->includers.emplace_back(_doc, _line_nr);
2827  dst = it->second;
2828  }
2829  }
2830 
2831  // ----------------------------------------------------------------------------------------
2832 
2833  Config parse_string(const char* str, const FormatOptions& options, DocInfo_SP doc, ParseInfo& info)
2834  {
2835  Parser p(str, options, doc, info);
2836  return p.top_level();
2837  }
2838 
2839  Config parse_string(const char* str, const FormatOptions& options, const char* name)
2840  {
2841  ParseInfo info;
2842  return parse_string(str, options, std::make_shared<DocInfo>(name), info);
2843  }
2844 
2845  std::string read_text_file(const char* path)
2846  {
2847  FILE* fp = fopen(path, "rb");
2848  if (fp == nullptr) {
2849  CONFIGURU_ONERROR(std::string("Failed to open '") + path + "' for reading: " + strerror(errno));
2850  }
2851  std::string contents;
2852  fseek(fp, 0, SEEK_END);
2853  auto size = ftell(fp);
2854  if (size < 0) {
2855  CONFIGURU_ONERROR(std::string("Failed to find out size of '") + path + "': " + strerror(errno));
2856  }
2857  contents.resize(static_cast<size_t>(size));
2858  rewind(fp);
2859  auto num_read = fread(&contents[0], 1, contents.size(), fp);
2860  fclose(fp);
2861  if (num_read != contents.size()) {
2862  CONFIGURU_ONERROR(std::string("Failed to read from '") + path + "': " + strerror(errno));
2863  }
2864  return contents;
2865  }
2866 
2867  Config parse_file(const std::string& path, const FormatOptions& options, DocInfo_SP doc, ParseInfo& info)
2868  {
2869  // auto file = util::FILEWrapper::read_text_file(path);
2870  auto file = read_text_file(path.c_str());
2871  return parse_string(file.c_str(), options, doc, info);
2872  }
2873 
2874  Config parse_file(const std::string& path, const FormatOptions& options)
2875  {
2876  ParseInfo info;
2877  return parse_file(path, options, std::make_shared<DocInfo>(path), info);
2878  }
2879 }
2880 
2881 // ----------------------------------------------------------------------------
2882 // Yb dP 88""Yb 88 888888 888888 88""Yb
2883 // Yb db dP 88__dP 88 88 88__ 88__dP
2884 // YbdPYbdP 88"Yb 88 88 88"" 88"Yb
2885 // YP YP 88 Yb 88 88 888888 88 Yb
2886 
2887 #include <cstdlib> // strtod
2888 
2889 namespace configuru
2890 {
2891  bool is_identifier(const char* p)
2892  {
2893  if (*p == '_'
2894  || ('a' <= *p && *p <= 'z')
2895  || ('A' <= *p && *p <= 'Z'))
2896  {
2897  ++p;
2898  while (*p) {
2899  if (*p == '_'
2900  || ('a' <= *p && *p <= 'z')
2901  || ('A' <= *p && *p <= 'Z')
2902  || ('0' <= *p && *p <= '9'))
2903  {
2904  ++p;
2905  } else {
2906  return false;
2907  }
2908  }
2909  return true;
2910  } else {
2911  return false;
2912  }
2913  }
2914 
2915  bool has_pre_end_brace_comments(const Config& cfg)
2916  {
2917  return cfg.has_comments() && !cfg.comments().pre_end_brace.empty();
2918  }
2919 
2920  struct Writer
2921  {
2922  std::string _out;
2923  bool _compact;
2924  FormatOptions _options;
2925  bool SAFE_CHARACTERS[256];
2926  DocInfo_SP _doc;
2927 
2928  Writer(const FormatOptions& options, DocInfo_SP doc)
2929  : _options(options), _doc(std::move(doc))
2930  {
2931  _compact = _options.compact();
2932 
2933  for (int i = 0; i < 256; ++i) {
2934  SAFE_CHARACTERS[i] = i >= 0x20;
2935  }
2936 
2937  SAFE_CHARACTERS[static_cast<uint8_t>('\\')] = false;
2938  SAFE_CHARACTERS[static_cast<uint8_t>('\"')] = false;
2939  SAFE_CHARACTERS[static_cast<uint8_t>('\0')] = false;
2940  SAFE_CHARACTERS[static_cast<uint8_t>('\b')] = false;
2941  SAFE_CHARACTERS[static_cast<uint8_t>('\f')] = false;
2942  SAFE_CHARACTERS[static_cast<uint8_t>('\n')] = false;
2943  SAFE_CHARACTERS[static_cast<uint8_t>('\r')] = false;
2944  SAFE_CHARACTERS[static_cast<uint8_t>('\t')] = false;
2945  }
2946 
2947  inline void write_indent(unsigned indent)
2948  {
2949  if (_compact) { return; }
2950  for (unsigned i=0; i<indent; ++i) {
2951  _out += _options.indentation;
2952  }
2953  }
2954 
2955  void write_prefix_comments(unsigned indent, const Comments& comments)
2956  {
2957  if (!_options.write_comments) { return; }
2958  if (!comments.empty()) {
2959  _out.push_back('\n');
2960  for (auto&& c : comments) {
2961  write_indent(indent);
2962  _out += c;
2963  _out.push_back('\n');
2964  }
2965  }
2966  }
2967 
2968  void write_prefix_comments(unsigned indent, const Config& cfg)
2969  {
2970  if (!_options.write_comments) { return; }
2971  if (cfg.has_comments()) {
2972  write_prefix_comments(indent, cfg.comments().prefix);
2973  }
2974  }
2975 
2976  void write_postfix_comments(unsigned indent, const Comments& comments)
2977  {
2978  if (!_options.write_comments) { return; }
2979  (void)indent; // TODO: reindent comments
2980  for (auto&& c : comments) {
2981  _out.push_back(' ');;
2982  _out += c;
2983  }
2984  }
2985 
2986  void write_pre_brace_comments(unsigned indent, const Comments& comments)
2987  {
2988  write_prefix_comments(indent, comments);
2989  }
2990 
2991  void write_value(unsigned indent, const Config& config,
2992  bool write_prefix, bool write_postfix)
2993  {
2994  if (_options.allow_macro && config.doc() && config.doc() != _doc) {
2995  dump_file(config.doc()->filename, config, _options);
2996  _out += "#include <";
2997  _out += config.doc()->filename;
2998  _out.push_back('>');
2999  return;
3000  }
3001 
3002  if (write_prefix) {
3003  write_prefix_comments(indent, config);
3004  }
3005 
3006  if (config.is_null()) {
3007  _out += "null";
3008  } else if (config.is_bool()) {
3009  _out += (config.as_bool() ? "true" : "false");
3010  } else if (config.is_int()) {
3011  char temp_buff[64];
3012  snprintf(temp_buff, sizeof(temp_buff), "%lld", static_cast<long long>(config));
3013  _out += temp_buff;
3014  } else if (config.is_float()) {
3015  write_number( config.as_double() );
3016  } else if (config.is_string()) {
3017  write_string(config.as_string());
3018  } else if (config.is_array()) {
3019  if (config.array_size() == 0 && !has_pre_end_brace_comments(config)) {
3020  if (_compact) {
3021  _out += "[]";
3022  } else {
3023  _out += "[ ]";
3024  }
3025  } else if (_compact || is_simple_array(config)) {
3026  _out.push_back('[');
3027  if (!_compact) {
3028  _out.push_back(' ');
3029  }
3030  auto&& array = config.as_array();
3031  for (size_t i = 0; i < array.size(); ++i) {
3032  write_value(indent + 1, array[i], false, true);
3033  if (_compact) {
3034  if (i + 1 < array.size()) {
3035  _out.push_back(',');
3036  }
3037  } else if (_options.array_omit_comma || i + 1 == array.size()) {
3038  _out.push_back(' ');
3039  } else {
3040  _out += ", ";
3041  }
3042  }
3043  write_pre_brace_comments(indent + 1, config.comments().pre_end_brace);
3044  _out += "]";
3045  } else {
3046  _out += "[\n";
3047  auto&& array = config.as_array();
3048  for (size_t i = 0; i < array.size(); ++i) {
3049  write_prefix_comments(indent + 1, array[i]);
3050  write_indent(indent + 1);
3051  write_value(indent + 1, array[i], false, true);
3052  if (_options.array_omit_comma || i + 1 == array.size()) {
3053  _out.push_back('\n');
3054  } else {
3055  _out += ",\n";
3056  }
3057  }
3058  write_pre_brace_comments(indent + 1, config.comments().pre_end_brace);
3059  write_indent(indent);
3060  _out += "]";
3061  }
3062  } else if (config.is_object()) {
3063  if (config.object_size() == 0 && !has_pre_end_brace_comments(config)) {
3064  if (_compact) {
3065  _out += "{}";
3066  } else {
3067  _out += "{ }";
3068  }
3069  } else {
3070  if (_compact) {
3071  _out.push_back('{');
3072  } else {
3073  _out += "{\n";
3074  }
3075  write_object_contents(indent + 1, config);
3076  write_indent(indent);
3077  _out.push_back('}');
3078  }
3079  } else {
3080  if (_options.write_uninitialized) {
3081  _out += "UNINITIALIZED";
3082  } else {
3083  CONFIGURU_ONERROR("Failed to serialize uninitialized Config");
3084  }
3085  }
3086 
3087  if (write_postfix) {
3088  write_postfix_comments(indent, config.comments().postfix);
3089  }
3090  }
3091 
3092  void write_object_contents(unsigned indent, const Config& config)
3093  {
3094  // Write in same order as input:
3095  auto&& object = config.as_object()._impl;
3096 
3097  using ObjIterator = Config::ConfigObjectImpl::const_iterator;
3098  std::vector<ObjIterator> pairs;
3099  pairs.reserve(object.size());
3100 
3101  size_t longest_key = 0;
3102  bool align_values = !_compact && _options.object_align_values;
3103 
3104  for (auto it=object.begin(); it!=object.end(); ++it) {
3105  pairs.push_back(it);
3106  if (align_values) {
3107  longest_key = std::max(longest_key, it->first.size());
3108  }
3109  }
3110 
3111  if (_options.sort_keys) {
3112  std::sort(begin(pairs), end(pairs), [](const ObjIterator& a, const ObjIterator& b) {
3113  return a->first < b->first;
3114  });
3115  } else {
3116  std::sort(begin(pairs), end(pairs), [](const ObjIterator& a, const ObjIterator& b) {
3117  return a->second._nr < b->second._nr;
3118  });
3119  }
3120 
3121  size_t i = 0;
3122  for (auto&& it : pairs) {
3123  auto&& value = it->second._value;
3124  write_prefix_comments(indent, value);
3125  write_indent(indent);
3126  write_key(it->first);
3127  if (_compact) {
3128  _out.push_back(':');
3129  } else if (_options.omit_colon_before_object && value.is_object() && value.object_size() != 0) {
3130  _out.push_back(' ');
3131  } else {
3132  _out += ": ";
3133  if (align_values) {
3134  for (size_t j=it->first.size(); j<longest_key; ++j) {
3135  _out.push_back(' ');
3136  }
3137  }
3138  }
3139  write_value(indent, value, false, true);
3140  if (_compact) {
3141  if (i + 1 < pairs.size()) {
3142  _out.push_back(',');
3143  }
3144  } else if (_options.array_omit_comma || i + 1 == pairs.size()) {
3145  _out.push_back('\n');
3146  } else {
3147  _out += ",\n";
3148  }
3149  i += 1;
3150  }
3151 
3152  write_pre_brace_comments(indent, config.comments().pre_end_brace);
3153  }
3154 
3155  void write_key(const std::string& str)
3156  {
3157  if (_options.identifiers_keys && is_identifier(str.c_str())) {
3158  _out += str;
3159  } else {
3160  write_string(str);
3161  }
3162  }
3163 
3164  void write_number(double val)
3165  {
3166  if (_options.distinct_floats && val == 0 && std::signbit(val)) {
3167  _out += "-0.0";
3168  return;
3169  }
3170 
3171  const auto as_int = static_cast<long long>(val);
3172  if (static_cast<double>(as_int) == val) {
3173  char temp_buff[64];
3174  snprintf(temp_buff, sizeof(temp_buff), "%lld", as_int);
3175  _out += temp_buff;
3176  if (_options.distinct_floats) {
3177  _out += ".0";
3178  }
3179  return;
3180  }
3181 
3182  if (std::isfinite(val)) {
3183  char temp_buff[64];
3184 
3185  const auto as_float = static_cast<float>(val);
3186  if (static_cast<double>(as_float) == val) {
3187  // It's actually a float!
3188  snprintf(temp_buff, sizeof(temp_buff), "%g", as_float);
3189  if (std::strtof(temp_buff, nullptr) == as_float) {
3190 
3191  _out += temp_buff;
3192  } else {
3193  snprintf(temp_buff, sizeof(temp_buff), "%.8g", as_float);
3194  _out += temp_buff;
3195  }
3196  return;
3197  }
3198 
3199  // Try single digit of precision (for denormals):
3200  snprintf(temp_buff, sizeof(temp_buff), "%.1g", val);
3201  if (std::strtod(temp_buff, nullptr) == val) {
3202  _out += temp_buff;
3203  return;
3204  }
3205 
3206  // Try default digits of precision:
3207  snprintf(temp_buff, sizeof(temp_buff), "%g", val);
3208  if (std::strtod(temp_buff, nullptr) == val) {
3209  _out += temp_buff;
3210  return;
3211  }
3212 
3213  // Try 16 digits of precision:
3214  snprintf(temp_buff, sizeof(temp_buff), "%.16g", val);
3215  if (std::strtod(temp_buff, nullptr) == val) {
3216  _out += temp_buff;
3217  return;
3218  }
3219 
3220  // Nope, full 17 digits needed:
3221  snprintf(temp_buff, sizeof(temp_buff), "%.17g", val);
3222  _out += temp_buff;
3223  } else if (val == +std::numeric_limits<double>::infinity()) {
3224  if (!_options.inf) {
3225  CONFIGURU_ONERROR("Can't encode infinity");
3226  }
3227  _out += "+inf";
3228  } else if (val == -std::numeric_limits<double>::infinity()) {
3229  if (!_options.inf) {
3230  CONFIGURU_ONERROR("Can't encode negative infinity");
3231  }
3232  _out += "-inf";
3233  } else {
3234  if (!_options.nan) {
3235  CONFIGURU_ONERROR("Can't encode NaN");
3236  }
3237  _out += "+NaN";
3238  }
3239  }
3240 
3241  void write_string(const std::string& str)
3242  {
3243  const size_t LONG_LINE = 240;
3244 
3245  if (!_options.str_python_multiline ||
3246  str.find('\n') == std::string::npos ||
3247  str.length() < LONG_LINE ||
3248  str.find("\"\"\"") != std::string::npos)
3249  {
3250  write_quoted_string(str);
3251  } else {
3252  write_verbatim_string(str);
3253  }
3254  }
3255 
3256  void write_hex_digit(unsigned num)
3257  {
3258  CONFIGURU_ASSERT(num < 16u);
3259  if (num < 10u) { _out.push_back(char('0' + num)); }
3260  else { _out.push_back(char('a' + num - 10)); }
3261  }
3262 
3263  void write_hex_16(uint16_t n)
3264  {
3265  write_hex_digit((n >> 12) & 0x0f);
3266  write_hex_digit((n >> 8) & 0x0f);
3267  write_hex_digit((n >> 4) & 0x0f);
3268  write_hex_digit((n >> 0) & 0x0f);
3269  }
3270 
3271  void write_unicode_16(uint16_t c)
3272  {
3273  _out += "\\u";
3274  write_hex_16(c);
3275  }
3276 
3277  void write_quoted_string(const std::string& str)
3278  {
3279  _out.push_back('"');
3280 
3281  const char* ptr = str.c_str();
3282  const char* end = ptr + str.size();
3283  while (ptr < end) {
3284  // Output large swats of safe characters at once:
3285  auto start = ptr;
3286  while (SAFE_CHARACTERS[static_cast<uint8_t>(*ptr)]) {
3287  ++ptr;
3288  }
3289  if (start < ptr) {
3290  _out.append(start, ptr - start);
3291  }
3292  if (ptr == end) { break; }
3293 
3294  char c = *ptr;
3295  ++ptr;
3296  if (c == '\\') { _out += "\\\\"; }
3297  else if (c == '\"') { _out += "\\\""; }
3298  //else if (c == '\'') { _out += "\\\'"; }
3299  else if (c == '\0') { _out += "\\0"; }
3300  else if (c == '\b') { _out += "\\b"; }
3301  else if (c == '\f') { _out += "\\f"; }
3302  else if (c == '\n') { _out += "\\n"; }
3303  else if (c == '\r') { _out += "\\r"; }
3304  else if (c == '\t') { _out += "\\t"; }
3305  else /*if (0 <= c && c < 0x20)*/ { write_unicode_16(static_cast<uint16_t>(c)); }
3306  }
3307 
3308  _out.push_back('"');
3309  }
3310 
3311  void write_verbatim_string(const std::string& str)
3312  {
3313  _out += "\"\"\"";
3314  _out += str;
3315  _out += "\"\"\"";
3316  }
3317 
3318  bool is_simple(const Config& var)
3319  {
3320  if (var.is_array() && var.array_size() > 0) { return false; }
3321  if (var.is_object() && var.object_size() > 0) { return false; }
3322  if (_options.write_comments && var.has_comments()) { return false; }
3323  return true;
3324  }
3325 
3326  bool is_all_numbers(const Config& array)
3327  {
3328  for (auto& v: array.as_array()) {
3329  if (!v.is_number()) {
3330  return false;
3331  }
3332  }
3333  return true;
3334  }
3335 
3336  bool is_simple_array(const Config& array)
3337  {
3338  if (array.array_size() <= 16 && is_all_numbers(array)) {
3339  return true; // E.g., a 4x4 matrix
3340  }
3341 
3342  if (array.array_size() > 4) { return false; }
3343  size_t estimated_width = 0;
3344  for (auto& v: array.as_array()) {
3345  if (!is_simple(v)) {
3346  return false;
3347  }
3348  if (v.is_string()) {
3349  estimated_width += 2 + v.as_string().size();
3350  } else {
3351  estimated_width += 5;
3352  }
3353  estimated_width += 2;
3354  }
3355  return estimated_width < 60;
3356  }
3357  }; // struct Writer
3358 
3359  std::string dump_string(const Config& config, const FormatOptions& options)
3360  {
3361  Writer w(options, config.doc());
3362 
3363  if (options.implicit_top_object && config.is_object()) {
3364  w.write_object_contents(0, config);
3365  } else {
3366  w.write_value(0, config, true, true);
3367 
3368  if (options.end_with_newline && !options.compact())
3369  {
3370  w._out.push_back('\n'); // Good form
3371  }
3372  }
3373 
3374  if (options.mark_accessed)
3375  {
3376  config.mark_accessed(true);
3377  }
3378  return std::move(w._out);
3379  }
3380 
3381  static void write_text_file(const char* path, const std::string& data)
3382  {
3383  auto fp = fopen(path, "wb");
3384  if (fp == nullptr) {
3385  CONFIGURU_ONERROR(std::string("Failed to open '") + path + "' for writing: " + strerror(errno));
3386  }
3387  auto num_bytes_written = fwrite(data.data(), 1, data.size(), fp);
3388  fclose(fp);
3389  if (num_bytes_written != data.size()) {
3390  CONFIGURU_ONERROR(std::string("Failed to write to '") + path + "': " + strerror(errno));
3391  }
3392  }
3393 
3394  void dump_file(const std::string& path, const configuru::Config& config, const FormatOptions& options)
3395  {
3396  auto str = dump_string(config, options);
3397  write_text_file(path.c_str(), str);
3398  }
3399 } // namespace configuru
3400 
3401 // ----------------------------------------------------------------------------
3402 
3403 #endif // CONFIGURU_IMPLEMENTATION
Accessing a Config of this type is always an error.
Definition: configuru.hpp:199
void tag(const DocInfo_SP &doc, Index line, Index column)
Used by the parser - no need to use directly.
bool enforce_indentation
Must have correct indentation?
Definition: configuru.hpp:972
Config()
Creates an uninitialized Config.
Definition: configuru.hpp:221
void dump_file(const std::string &path, const Config &config, const FormatOptions &options)
bool hexadecimal_integers
Allow 0xff.
Definition: configuru.hpp:988
bool allow_macro
Allow #include "some_other_file.cfg"
Definition: configuru.hpp:1014
Helper: value in an object.
Definition: configuru.hpp:141
size_t array_size() const
Length of an array.
Definition: configuru.hpp:529
const ConfigObject & as_object() const
Definition: configuru.hpp:586
Config(const std::map< std::string, T > &values)
Object constructor.
Definition: configuru.hpp:286
void swap(Config &o) noexcept
Swaps file/line too.
Config parse_string(const char *str, const FormatOptions &options, const char *name)
void mark_accessed(bool v) const
Set the &#39;access&#39; flag recursively,.
Type
Definition: configuru.hpp:197
static bool deep_eq(const Config &a, const Config &b)
Compare Config values recursively.
std::string indentation
Definition: configuru.hpp:971
bool array_trailing_comma
Allow [1, 2, 3,].
Definition: configuru.hpp:995
const ConfigArrayImpl & as_array() const
Only use this for iterating over an array: for (Config& e : cfg.as_array()) { ... } ...
Definition: configuru.hpp:542
bool identifiers_keys
{ is_this_ok: true }
Definition: configuru.hpp:998
bool erase(const std::string &key)
Erase a key from an object.
bool sort_keys
Sort keys lexicographically. If false, sort by order they where added.
Definition: configuru.hpp:1020
ConfigComments & comments()
Read/write of comments.
Definition: configuru.hpp:676
void visit_dangling(const std::function< void(const std::string &key, const Config &value)> &visitor) const
Visit dangling (unaccessed) object keys recursively.
bool binary_integers
Allow 0b1010.
Definition: configuru.hpp:989
We are the result of a key-lookup in a Object with no hit. We are in effect write-only.
Definition: configuru.hpp:200
std::ostream & operator<<(std::ostream &os, const Config &cfg)
Prints in JSON but in a fail-safe mannor, allowing uninitalized keys and inf/nan. ...
Index line() const
BAD_INDEX if not set.
Definition: configuru.hpp:366
bool allow_space_before_colon
{ "is_this_ok" : true }
Definition: configuru.hpp:1000
Definition: configuru.hpp:745
bool emplace(std::string key, Config value)
Returns true iff the value was inserted, false if they key was already there.
static Config array()
Preferred way to create an empty array.
ConfigObject & as_object()
Definition: configuru.hpp:578
Definition: configuru.hpp:208
Config(const std::vector< bool > &values)
Array constructor.
Definition: configuru.hpp:275
const DocInfo_SP & doc() const
Handle to document.
Definition: configuru.hpp:369
size_t count(const std::string &key) const
Like has_key, but STL compatible.
Definition: configuru.hpp:608
std::string dump_string(const Config &config, const FormatOptions &options)
ConfigComments()
Before the closing } or ].
Definition: configuru.hpp:163
T get() const
Extract the value of this Config.
static Config object()
Preferred way to create an empty object.
bool str_csharp_verbatim
Allow "Verbatim".
Definition: configuru.hpp:1008
void check_dangling() const
Will check for dangling (unaccessed) object keys recursively and call CONFIGURU_ON_DANGLING on all fo...
bool str_allow_tab
Allow unescaped tab in string.
Definition: configuru.hpp:1011
void push_back(Config value)
Append a value to this array.
Definition: configuru.hpp:565
std::string where() const
Returns file:line iff available.
static Config array(const Container &container)
Preferred way to create an array from an STL container.
Definition: configuru.hpp:317
bool omit_colon_before_object
{ "nested_object" { } }
Definition: configuru.hpp:1001
bool unary_plus
Allow +42.
Definition: configuru.hpp:990
FormatOptions make_json_options()
Returns FormatOptions that are describe a JSON file format.
Definition: configuru.hpp:1032
const char * what() const noexceptoverride
Will name the file name, line number, column and description.
Definition: configuru.hpp:950
This struct basically contain all the way we can tweak the file format.
Definition: configuru.hpp:966
bool single_line_comments
Allow this?
Definition: configuru.hpp:981
bool as_bool() const
The Config must be a boolean.
Definition: configuru.hpp:475
bool inf
Allow +inf, -inf.
Definition: configuru.hpp:986
ConfigArrayImpl & as_array()
Only use this for iterating over an array: for (Config& e : cfg.as_array()) { ... } ...
Definition: configuru.hpp:535
Definition: configuru.hpp:738
Config & operator[](const char(&key)[N])
For indexing with string literals:
Definition: configuru.hpp:600
bool distinct_floats
Print 9.0 as "9.0", not just "9". A must for round-tripping.
Definition: configuru.hpp:991
T as(const configuru::Config &config)
Definition: configuru.hpp:850
bool has_key(const std::string &key) const
Check if an object has a specific key.
bool end_with_newline
End each file with a newline (unless compact).
Definition: configuru.hpp:973
bool array_omit_comma
Allow [1 2 3].
Definition: configuru.hpp:994
Comments postfix
After the value, on the same line. Like this.
Definition: configuru.hpp:160
bool nesting_block_comments
/* Allow /* this? */ */
Definition: configuru.hpp:983
Thrown on a syntax error.
Definition: configuru.hpp:938
Definition: configuru.hpp:1151
bool mark_accessed
Dumping should mark the json as accessed?
Definition: configuru.hpp:1026
bool empty_file
If true, an empty file is an empty object.
Definition: configuru.hpp:976
const char * debug_descr() const
Returns either "true", "false", the constained string, or the type name.
bool implicit_top_array
Ok with several values top-level?
Definition: configuru.hpp:978
Config & operator[](size_t ix)
Array indexing.
Definition: configuru.hpp:549
The Configuru namespace.
Definition: configuru.hpp:108
static const char * type_str(Type t)
Human-readable version of the type ("integer", "bool", etc).
Captures the comments related to a Config value.
Definition: configuru.hpp:155
bool str_python_multiline
Allow """ Python\nverbatim strings """.
Definition: configuru.hpp:1009
Comments prefix
Definition: configuru.hpp:159
Definition: configuru.hpp:116
Definition: configuru.hpp:194
FormatOptions make_forgiving_options()
Returns format options that allow us parsing most files.
Definition: configuru.hpp:1088
bool object_align_values
Add spaces after keys to align subsequent values.
Definition: configuru.hpp:1005
bool object_trailing_comma
Allow {a:1, b:2,}.
Definition: configuru.hpp:1003
const ConfigComments & comments() const
Read comments.
Definition: configuru.hpp:685
bool object_duplicate_keys
Allow {"a":1, "a":2}.
Definition: configuru.hpp:1004
bool str_32bit_unicode
Allow "\U0030dbfd".
Definition: configuru.hpp:1010
Config(const std::vector< T > &values)
Array constructor.
Definition: configuru.hpp:265
std::string get_or(const std::string &key, const char *default_value) const
Look for the given key in this object, and return default_value on failure.
Definition: configuru.hpp:631
bool implicit_top_object
Ok with key-value pairs top-level?
Definition: configuru.hpp:977
Helper for describing a document.
Definition: configuru.hpp:126
bool object_omit_comma
Allow {a:1 b:2}.
Definition: configuru.hpp:1002
void visit_configs(Config &&config, Visitor &&visitor)
Recursively visit all values in a config.
Definition: configuru.hpp:893
bool nan
Allow +NaN.
Definition: configuru.hpp:987
bool has_comments() const
Was there any comments about this value in the input?
Definition: configuru.hpp:670
bool object_separator_equal
{ "is_this_ok" = true }
Definition: configuru.hpp:999
const Config & operator[](size_t ix) const
Array indexing.
Definition: configuru.hpp:557
std::string get_or(std::initializer_list< std::string > keys, const char *default_value) const
obj.get_or({"a", "b". "c"}, 42) - like obj["a"]["b"]["c"], but returns 42 if any of the keys are miss...
Definition: configuru.hpp:641
void make_object()
Used by the parser - no need to use directly.
Config deep_clone() const
Copy this Config value recursively.
void insert_or_assign(const std::string &key, Config &&value)
Like foo[key] = value, but faster.
bool write_uninitialized
When printing, write uninitialized values as UNINITIALIZED. Useful for debugging. ...
Definition: configuru.hpp:1023
void make_array()
Used by the parser - no need to use directly.
size_t object_size() const
Number of elementsi n this object.
T get_or(const T &default_value) const
Returns the value or default_value if this is the result of a bad lookup.
Definition: configuru.hpp:516