1 /* 2 * Copyright (c) 2015, Vsevolod Stakhov 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * * Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * * Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25 #pragma once 26 #include <string> 27 #include <vector> 28 #include <map> 29 #include <set> 30 #include <memory> 31 #include <iostream> 32 33 #include "ucl.h" 34 35 // C++11 API inspired by json11: https://github.com/dropbox/json11/ 36 37 namespace ucl { 38 39 struct ucl_map_construct_t { }; 40 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t(); 41 struct ucl_array_construct_t { }; 42 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t(); 43 44 class Ucl final { 45 private: 46 47 struct ucl_deleter { 48 void operator() (ucl_object_t *obj) { 49 ucl_object_unref (obj); 50 } 51 }; 52 53 static int 54 append_char (unsigned char c, size_t nchars, void *ud) 55 { 56 std::string *out = reinterpret_cast<std::string *>(ud); 57 58 out->append (nchars, (char)c); 59 60 return nchars; 61 } 62 static int 63 append_len (unsigned const char *str, size_t len, void *ud) 64 { 65 std::string *out = reinterpret_cast<std::string *>(ud); 66 67 out->append ((const char *)str, len); 68 69 return len; 70 } 71 static int 72 append_int (int64_t elt, void *ud) 73 { 74 std::string *out = reinterpret_cast<std::string *>(ud); 75 auto nstr = std::to_string (elt); 76 77 out->append (nstr); 78 79 return nstr.size (); 80 } 81 static int 82 append_double (double elt, void *ud) 83 { 84 std::string *out = reinterpret_cast<std::string *>(ud); 85 auto nstr = std::to_string (elt); 86 87 out->append (nstr); 88 89 return nstr.size (); 90 } 91 92 static struct ucl_emitter_functions default_emit_funcs() 93 { 94 struct ucl_emitter_functions func = { 95 Ucl::append_char, 96 Ucl::append_len, 97 Ucl::append_int, 98 Ucl::append_double, 99 nullptr, 100 nullptr 101 }; 102 103 return func; 104 }; 105 106 static bool ucl_variable_getter(const unsigned char *data, size_t len, 107 unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void* ud) 108 { 109 *need_free = false; 110 111 auto vars = reinterpret_cast<std::set<std::string> *>(ud); 112 if (vars && data && len != 0) { 113 vars->emplace (data, data + len); 114 } 115 return false; 116 } 117 118 static bool ucl_variable_replacer (const unsigned char *data, size_t len, 119 unsigned char **replace, size_t *replace_len, bool *need_free, void* ud) 120 { 121 *need_free = false; 122 123 auto replacer = reinterpret_cast<variable_replacer *>(ud); 124 if (!replacer) { 125 return false; 126 } 127 128 std::string var_name (data, data + len); 129 if (!replacer->is_variable (var_name)) { 130 return false; 131 } 132 133 std::string var_value = replacer->replace (var_name); 134 if (var_value.empty ()) { 135 return false; 136 } 137 138 *replace = (unsigned char *)UCL_ALLOC (var_value.size ()); 139 memcpy (*replace, var_value.data (), var_value.size ()); 140 141 *replace_len = var_value.size (); 142 *need_free = true; 143 144 return true; 145 } 146 147 template <typename C, typename P> 148 static Ucl parse_with_strategy_function (C config_func, P parse_func, std::string &err) 149 { 150 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT); 151 152 config_func (parser); 153 154 if (!parse_func (parser)) { 155 err.assign (ucl_parser_get_error (parser)); 156 ucl_parser_free (parser); 157 158 return nullptr; 159 } 160 161 auto obj = ucl_parser_get_object (parser); 162 ucl_parser_free (parser); 163 164 // Obj will handle ownership 165 return Ucl (obj); 166 } 167 168 std::unique_ptr<ucl_object_t, ucl_deleter> obj; 169 170 public: 171 class const_iterator { 172 private: 173 struct ucl_iter_deleter { 174 void operator() (ucl_object_iter_t it) { 175 ucl_object_iterate_free (it); 176 } 177 }; 178 std::shared_ptr<void> it; 179 std::unique_ptr<Ucl> cur; 180 public: 181 typedef std::forward_iterator_tag iterator_category; 182 183 const_iterator(const Ucl &obj) { 184 it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()), 185 ucl_iter_deleter()); 186 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); 187 if (cur->type() == UCL_NULL) { 188 it.reset (); 189 cur.reset (); 190 } 191 } 192 193 const_iterator() {} 194 const_iterator(const const_iterator &other) = delete; 195 const_iterator(const_iterator &&other) = default; 196 ~const_iterator() {} 197 198 const_iterator& operator=(const const_iterator &other) = delete; 199 const_iterator& operator=(const_iterator &&other) = default; 200 201 bool operator==(const const_iterator &other) const 202 { 203 if (cur && other.cur) { 204 return cur->obj.get() == other.cur->obj.get(); 205 } 206 207 return !cur && !other.cur; 208 } 209 210 bool operator!=(const const_iterator &other) const 211 { 212 return !(*this == other); 213 } 214 215 const_iterator& operator++() 216 { 217 if (it) { 218 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); 219 } 220 221 if (cur && cur->type() == UCL_NULL) { 222 it.reset (); 223 cur.reset (); 224 } 225 226 return *this; 227 } 228 229 const Ucl& operator*() const 230 { 231 return *cur; 232 } 233 const Ucl* operator->() const 234 { 235 return cur.get(); 236 } 237 }; 238 239 struct variable_replacer { 240 virtual ~variable_replacer() {} 241 242 virtual bool is_variable (const std::string &str) const 243 { 244 return !str.empty (); 245 } 246 247 virtual std::string replace (const std::string &var) const = 0; 248 }; 249 250 // We grab ownership if get non-const ucl_object_t 251 Ucl(ucl_object_t *other) { 252 obj.reset (other); 253 } 254 255 // Shared ownership 256 Ucl(const ucl_object_t *other) { 257 obj.reset (ucl_object_ref (other)); 258 } 259 260 Ucl(const Ucl &other) { 261 obj.reset (ucl_object_ref (other.obj.get())); 262 } 263 264 Ucl(Ucl &&other) { 265 obj.swap (other.obj); 266 } 267 268 Ucl() noexcept { 269 obj.reset (ucl_object_typed_new (UCL_NULL)); 270 } 271 Ucl(std::nullptr_t) noexcept { 272 obj.reset (ucl_object_typed_new (UCL_NULL)); 273 } 274 Ucl(double value) { 275 obj.reset (ucl_object_typed_new (UCL_FLOAT)); 276 obj->value.dv = value; 277 } 278 Ucl(int64_t value) { 279 obj.reset (ucl_object_typed_new (UCL_INT)); 280 obj->value.iv = value; 281 } 282 Ucl(bool value) { 283 obj.reset (ucl_object_typed_new (UCL_BOOLEAN)); 284 obj->value.iv = static_cast<int64_t>(value); 285 } 286 Ucl(const std::string &value) { 287 obj.reset (ucl_object_fromstring_common (value.data (), value.size (), 288 UCL_STRING_RAW)); 289 } 290 Ucl(const char *value) { 291 obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW)); 292 } 293 294 // Implicit constructor: anything with a to_json() function. 295 template <class T, class = decltype(&T::to_ucl)> 296 Ucl(const T &t) : Ucl(t.to_ucl()) {} 297 298 // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) 299 template <class M, typename std::enable_if< 300 std::is_constructible<std::string, typename M::key_type>::value 301 && std::is_constructible<Ucl, typename M::mapped_type>::value, 302 int>::type = 0> 303 Ucl(const M &m) { 304 obj.reset (ucl_object_typed_new (UCL_OBJECT)); 305 auto cobj = obj.get (); 306 307 for (const auto &e : m) { 308 ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()), 309 e.first.data (), e.first.size (), true); 310 } 311 } 312 313 // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) 314 template <class V, typename std::enable_if< 315 std::is_constructible<Ucl, typename V::value_type>::value, 316 int>::type = 0> 317 Ucl(const V &v) { 318 obj.reset (ucl_object_typed_new (UCL_ARRAY)); 319 auto cobj = obj.get (); 320 321 for (const auto &e : v) { 322 ucl_array_append (cobj, ucl_object_ref (e.obj.get())); 323 } 324 } 325 326 ucl_type_t type () const { 327 if (obj) { 328 return ucl_object_type (obj.get ()); 329 } 330 return UCL_NULL; 331 } 332 333 const std::string key () const { 334 std::string res; 335 336 if (obj->key) { 337 res.assign (obj->key, obj->keylen); 338 } 339 340 return res; 341 } 342 343 double number_value (const double default_val = 0.0) const 344 { 345 double res; 346 347 if (ucl_object_todouble_safe(obj.get(), &res)) { 348 return res; 349 } 350 351 return default_val; 352 } 353 354 int64_t int_value (const int64_t default_val = 0) const 355 { 356 int64_t res; 357 358 if (ucl_object_toint_safe(obj.get(), &res)) { 359 return res; 360 } 361 362 return default_val; 363 } 364 365 bool bool_value (const bool default_val = false) const 366 { 367 bool res; 368 369 if (ucl_object_toboolean_safe(obj.get(), &res)) { 370 return res; 371 } 372 373 return default_val; 374 } 375 376 const std::string string_value (const std::string& default_val = "") const 377 { 378 const char* res = nullptr; 379 380 if (ucl_object_tostring_safe(obj.get(), &res)) { 381 return res; 382 } 383 384 return default_val; 385 } 386 387 const Ucl at (size_t i) const 388 { 389 if (type () == UCL_ARRAY) { 390 return Ucl (ucl_array_find_index (obj.get(), i)); 391 } 392 393 return Ucl (nullptr); 394 } 395 396 const Ucl lookup (const std::string &key) const 397 { 398 if (type () == UCL_OBJECT) { 399 return Ucl (ucl_object_lookup_len (obj.get(), 400 key.data (), key.size ())); 401 } 402 403 return Ucl (nullptr); 404 } 405 406 inline const Ucl operator[] (size_t i) const 407 { 408 return at(i); 409 } 410 411 inline const Ucl operator[](const std::string &key) const 412 { 413 return lookup(key); 414 } 415 // Serialize. 416 void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const 417 { 418 struct ucl_emitter_functions cbdata; 419 420 cbdata = Ucl::default_emit_funcs(); 421 cbdata.ud = reinterpret_cast<void *>(&out); 422 423 ucl_object_emit_full (obj.get(), type, &cbdata, nullptr); 424 } 425 426 std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const 427 { 428 std::string out; 429 430 dump (out, type); 431 432 return out; 433 } 434 435 static Ucl parse (const std::string &in, std::string &err) 436 { 437 return parse (in, std::map<std::string, std::string>(), err); 438 } 439 440 static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars, std::string &err) 441 { 442 auto config_func = [&vars] (ucl_parser *parser) { 443 for (const auto & item : vars) { 444 ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ()); 445 } 446 }; 447 448 auto parse_func = [&in] (ucl_parser *parser) { 449 return ucl_parser_add_chunk (parser, (unsigned char *)in.data (), in.size ()); 450 }; 451 452 return parse_with_strategy_function (config_func, parse_func, err); 453 } 454 455 static Ucl parse (const std::string &in, const variable_replacer &replacer, std::string &err) 456 { 457 auto config_func = [&replacer] (ucl_parser *parser) { 458 ucl_parser_set_variables_handler (parser, ucl_variable_replacer, 459 &const_cast<variable_replacer &>(replacer)); 460 }; 461 462 auto parse_func = [&in] (ucl_parser *parser) { 463 return ucl_parser_add_chunk (parser, (unsigned char *) in.data (), in.size ()); 464 }; 465 466 return parse_with_strategy_function (config_func, parse_func, err); 467 } 468 469 static Ucl parse (const char *in, std::string &err) 470 { 471 return parse (in, std::map<std::string, std::string>(), err); 472 } 473 474 static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err) 475 { 476 if (!in) { 477 err = "null input"; 478 return nullptr; 479 } 480 return parse (std::string (in), vars, err); 481 } 482 483 static Ucl parse (const char *in, const variable_replacer &replacer, std::string &err) 484 { 485 if (!in) { 486 err = "null input"; 487 return nullptr; 488 } 489 return parse (std::string(in), replacer, err); 490 } 491 492 static Ucl parse_from_file (const std::string &filename, std::string &err) 493 { 494 return parse_from_file (filename, std::map<std::string, std::string>(), err); 495 } 496 497 static Ucl parse_from_file (const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err) 498 { 499 auto config_func = [&vars] (ucl_parser *parser) { 500 for (const auto & item : vars) { 501 ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ()); 502 } 503 }; 504 505 auto parse_func = [&filename] (ucl_parser *parser) { 506 return ucl_parser_add_file (parser, filename.c_str ()); 507 }; 508 509 return parse_with_strategy_function (config_func, parse_func, err); 510 } 511 512 static Ucl parse_from_file (const std::string &filename, const variable_replacer &replacer, std::string &err) 513 { 514 auto config_func = [&replacer] (ucl_parser *parser) { 515 ucl_parser_set_variables_handler (parser, ucl_variable_replacer, 516 &const_cast<variable_replacer &>(replacer)); 517 }; 518 519 auto parse_func = [&filename] (ucl_parser *parser) { 520 return ucl_parser_add_file (parser, filename.c_str ()); 521 }; 522 523 return parse_with_strategy_function (config_func, parse_func, err); 524 } 525 526 static std::vector<std::string> find_variable (const std::string &in) 527 { 528 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT); 529 530 std::set<std::string> vars; 531 ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars); 532 ucl_parser_add_chunk (parser, (const unsigned char *)in.data (), in.size ()); 533 ucl_parser_free (parser); 534 535 std::vector<std::string> result; 536 std::move (vars.begin (), vars.end (), std::back_inserter (result)); 537 return result; 538 } 539 540 static std::vector<std::string> find_variable (const char *in) 541 { 542 if (!in) { 543 return std::vector<std::string>(); 544 } 545 return find_variable (std::string (in)); 546 } 547 548 static std::vector<std::string> find_variable_from_file (const std::string &filename) 549 { 550 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT); 551 552 std::set<std::string> vars; 553 ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars); 554 ucl_parser_add_file (parser, filename.c_str ()); 555 ucl_parser_free (parser); 556 557 std::vector<std::string> result; 558 std::move (vars.begin (), vars.end (), std::back_inserter (result)); 559 return std::move (result); 560 } 561 562 Ucl& operator= (Ucl rhs) 563 { 564 obj.swap (rhs.obj); 565 return *this; 566 } 567 568 bool operator== (const Ucl &rhs) const 569 { 570 return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0; 571 } 572 bool operator< (const Ucl &rhs) const 573 { 574 return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0; 575 } 576 bool operator!= (const Ucl &rhs) const { return !(*this == rhs); } 577 bool operator<= (const Ucl &rhs) const { return !(rhs < *this); } 578 bool operator> (const Ucl &rhs) const { return (rhs < *this); } 579 bool operator>= (const Ucl &rhs) const { return !(*this < rhs); } 580 581 explicit operator bool () const 582 { 583 if (!obj || type() == UCL_NULL) { 584 return false; 585 } 586 587 if (type () == UCL_BOOLEAN) { 588 return bool_value (); 589 } 590 591 return true; 592 } 593 594 const_iterator begin() const 595 { 596 return const_iterator(*this); 597 } 598 const_iterator cbegin() const 599 { 600 return const_iterator(*this); 601 } 602 const_iterator end() const 603 { 604 return const_iterator(); 605 } 606 const_iterator cend() const 607 { 608 return const_iterator(); 609 } 610 }; 611 612 }; 613