mini.h 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. /*
  2. * Copyright (c) 2015 r-lyeh (https://github.com/r-lyeh)
  3. * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
  4. *
  5. * This software is provided 'as-is', without any express or implied
  6. * warranty. In no event will the authors be held liable for any damages
  7. * arising from the use of this software.
  8. *
  9. * Permission is granted to anyone to use this software for any purpose,
  10. * including commercial applications, and to alter it and redistribute it
  11. * freely, subject to the following restrictions:
  12. *
  13. * 1. The origin of this software must not be misrepresented; you must not
  14. * claim that you wrote the original software. If you use this software
  15. * in a product, an acknowledgment in the product documentation would be
  16. * appreciated but is not required.
  17. * 2. Altered source versions must be plainly marked as such, and must not be
  18. * misrepresented as being the original software.
  19. * 3. This notice may not be removed or altered from any source distribution.
  20. */
  21. #ifndef UTIL_MINI_H
  22. #define UTIL_MINI_H
  23. #include <cctype>
  24. #include <map>
  25. #include <string>
  26. #include <vector>
  27. #include <fstream>
  28. #include "util.h"
  29. namespace toolkit {
  30. template<typename key, typename variant>
  31. class mINI_basic : public std::map<key, variant> {
  32. // Public API : existing map<> interface plus following methods
  33. public:
  34. void parse(const std::string &text) {
  35. // reset, split lines and parse
  36. std::vector<std::string> lines = tokenize(text, "\n");
  37. std::string symbol, tag;
  38. for (auto &line : lines) {
  39. // trim blanks
  40. line = trim(line);
  41. // split line into tokens and parse tokens
  42. if (line.empty() || line.front() == ';' || line.front() == '#') {
  43. continue;
  44. }
  45. if (line.size() >= 3 && line.front() == '[' && line.back() == ']') {
  46. tag = trim(line.substr(1, line.size() - 2));
  47. } else {
  48. auto at = line.find('=');
  49. symbol = trim(tag + "." + line.substr(0, at));
  50. (*this)[symbol] = (at == std::string::npos ? std::string() : trim(line.substr(at + 1)));
  51. }
  52. }
  53. }
  54. void parseFile(const std::string &fileName = exePath() + ".ini") {
  55. std::ifstream in(fileName, std::ios::in | std::ios::binary | std::ios::ate);
  56. if (!in.good()) {
  57. throw std::invalid_argument("Invalid ini file: " + fileName);
  58. }
  59. auto size = in.tellg();
  60. in.seekg(0, std::ios::beg);
  61. std::string buf;
  62. buf.resize(size);
  63. in.read((char *) buf.data(), size);
  64. parse(buf);
  65. }
  66. std::string dump(const std::string &header = "; auto-generated by mINI class {",
  67. const std::string &footer = "; } ---") const {
  68. std::string front(header + (header.empty() ? "" : "\r\n")), output, tag;
  69. std::vector<std::string> kv;
  70. for (auto &pr : *this) {
  71. auto pos = pr.first.find('.');
  72. if (pos == std::string::npos) {
  73. kv = {"", pr.first};
  74. } else {
  75. kv = {pr.first.substr(0, pos), pr.first.substr(pos + 1)};
  76. }
  77. if (kv[0].empty()) {
  78. front += kv[1] + "=" + pr.second + "\r\n";
  79. continue;
  80. }
  81. if (tag != kv[0]) {
  82. output += "\r\n[" + (tag = kv[0]) + "]\r\n";
  83. }
  84. output += kv[1] + "=" + pr.second + "\r\n";
  85. }
  86. return front + output + "\r\n" + footer + (footer.empty() ? "" : "\r\n");
  87. }
  88. void dumpFile(const std::string &fileName = exePath() + ".ini") {
  89. std::ofstream out(fileName, std::ios::out | std::ios::binary | std::ios::trunc);
  90. auto dmp = dump();
  91. out.write(dmp.data(), dmp.size());
  92. }
  93. static mINI_basic &Instance();
  94. private:
  95. std::vector<std::string> tokenize(const std::string &self, const std::string &chars) const {
  96. std::vector<std::string> tokens(1);
  97. std::string map(256, '\0');
  98. for (char ch : chars) {
  99. map[(uint8_t) ch] = '\1';
  100. }
  101. for (char ch : self) {
  102. if (!map.at((uint8_t) ch)) {
  103. tokens.back().push_back(ch);
  104. } else if (tokens.back().size()) {
  105. tokens.push_back(std::string());
  106. }
  107. }
  108. while (tokens.size() && tokens.back().empty()) {
  109. tokens.pop_back();
  110. }
  111. return tokens;
  112. }
  113. };
  114. // handy variant class as key/values
  115. struct variant : public std::string {
  116. template<typename T>
  117. variant(const T &t) :
  118. std::string(std::to_string(t)) {
  119. }
  120. template<size_t N>
  121. variant(const char (&s)[N]) :
  122. std::string(s, N) {
  123. }
  124. variant(const char *cstr) :
  125. std::string(cstr) {
  126. }
  127. variant(const std::string &other = std::string()) :
  128. std::string(other) {
  129. }
  130. template <typename T>
  131. operator T() const {
  132. return as<T>();
  133. }
  134. template<typename T>
  135. bool operator==(const T &t) const {
  136. return 0 == this->compare(variant(t));
  137. }
  138. bool operator==(const char *t) const {
  139. return this->compare(t) == 0;
  140. }
  141. template <typename T>
  142. typename std::enable_if<!std::is_class<T>::value, T>::type as() const {
  143. return as_default<T>();
  144. }
  145. template <typename T>
  146. typename std::enable_if<std::is_class<T>::value, T>::type as() const {
  147. return T((const std::string &)*this);
  148. }
  149. private:
  150. template <typename T>
  151. T as_default() const {
  152. T t;
  153. std::stringstream ss;
  154. return ss << *this && ss >> t ? t : T();
  155. }
  156. };
  157. template <>
  158. bool variant::as<bool>() const;
  159. template <>
  160. uint8_t variant::as<uint8_t>() const;
  161. using mINI = mINI_basic<std::string, variant>;
  162. } // namespace toolkit
  163. #endif //UTIL_MINI_H