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 <memory> 28 #include <iostream> 29 #include <strstream> 30 31 #include "ucl.h" 32 33 // C++11 API inspired by json11: https://github.com/dropbox/json11/ 34 35 namespace ucl { 36 37 struct ucl_map_construct_t { }; 38 constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t(); 39 struct ucl_array_construct_t { }; 40 constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t(); 41 42 class Ucl final { 43 private: 44 45 struct ucl_deleter { 46 void operator() (ucl_object_t *obj) { 47 ucl_object_unref (obj); 48 } 49 }; 50 51 static int 52 append_char (unsigned char c, size_t nchars, void *ud) 53 { 54 std::string *out = reinterpret_cast<std::string *>(ud); 55 56 out->append (nchars, (char)c); 57 58 return nchars; 59 } 60 static int 61 append_len (unsigned const char *str, size_t len, void *ud) 62 { 63 std::string *out = reinterpret_cast<std::string *>(ud); 64 65 out->append ((const char *)str, len); 66 67 return len; 68 } 69 static int 70 append_int (int64_t elt, void *ud) 71 { 72 std::string *out = reinterpret_cast<std::string *>(ud); 73 auto nstr = std::to_string (elt); 74 75 out->append (nstr); 76 77 return nstr.size (); 78 } 79 static int 80 append_double (double elt, void *ud) 81 { 82 std::string *out = reinterpret_cast<std::string *>(ud); 83 auto nstr = std::to_string (elt); 84 85 out->append (nstr); 86 87 return nstr.size (); 88 } 89 90 static struct ucl_emitter_functions default_emit_funcs() 91 { 92 struct ucl_emitter_functions func = { 93 Ucl::append_char, 94 Ucl::append_len, 95 Ucl::append_int, 96 Ucl::append_double, 97 nullptr, 98 nullptr 99 }; 100 101 return func; 102 }; 103 104 std::unique_ptr<ucl_object_t, ucl_deleter> obj; 105 106 public: 107 class const_iterator { 108 private: 109 struct ucl_iter_deleter { 110 void operator() (ucl_object_iter_t it) { 111 ucl_object_iterate_free (it); 112 } 113 }; 114 std::shared_ptr<void> it; 115 std::unique_ptr<Ucl> cur; 116 public: 117 typedef std::forward_iterator_tag iterator_category; 118 119 const_iterator(const Ucl &obj) { 120 it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()), 121 ucl_iter_deleter()); 122 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); 123 } 124 125 const_iterator() {} 126 const_iterator(const const_iterator &other) { 127 it = other.it; 128 } 129 ~const_iterator() {} 130 131 const_iterator& operator=(const const_iterator &other) { 132 it = other.it; 133 return *this; 134 } 135 136 bool operator==(const const_iterator &other) const 137 { 138 if (cur && other.cur) { 139 return cur->obj.get() == other.cur->obj.get(); 140 } 141 142 return !cur && !other.cur; 143 } 144 145 bool operator!=(const const_iterator &other) const 146 { 147 return !(*this == other); 148 } 149 150 const_iterator& operator++() 151 { 152 if (it) { 153 cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); 154 } 155 156 if (!*cur) { 157 it.reset (); 158 cur.reset (); 159 } 160 161 return *this; 162 } 163 164 const Ucl& operator*() const 165 { 166 return *cur; 167 } 168 const Ucl* operator->() const 169 { 170 return cur.get(); 171 } 172 }; 173 174 // We grab ownership if get non-const ucl_object_t 175 Ucl(ucl_object_t *other) { 176 obj.reset (other); 177 } 178 179 // Shared ownership 180 Ucl(const ucl_object_t *other) { 181 obj.reset (ucl_object_ref (other)); 182 } 183 184 Ucl(const Ucl &other) { 185 obj.reset (ucl_object_ref (other.obj.get())); 186 } 187 188 Ucl(Ucl &&other) { 189 obj.swap (other.obj); 190 } 191 192 Ucl() noexcept { 193 obj.reset (ucl_object_typed_new (UCL_NULL)); 194 } 195 Ucl(std::nullptr_t) noexcept { 196 obj.reset (ucl_object_typed_new (UCL_NULL)); 197 } 198 Ucl(double value) { 199 obj.reset (ucl_object_typed_new (UCL_FLOAT)); 200 obj->value.dv = value; 201 } 202 Ucl(int64_t value) { 203 obj.reset (ucl_object_typed_new (UCL_INT)); 204 obj->value.iv = value; 205 } 206 Ucl(bool value) { 207 obj.reset (ucl_object_typed_new (UCL_BOOLEAN)); 208 obj->value.iv = static_cast<int64_t>(value); 209 } 210 Ucl(const std::string &value) { 211 obj.reset (ucl_object_fromstring_common (value.data (), value.size (), 212 UCL_STRING_RAW)); 213 } 214 Ucl(const char * value) { 215 obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW)); 216 } 217 218 // Implicit constructor: anything with a to_json() function. 219 template <class T, class = decltype(&T::to_ucl)> 220 Ucl(const T & t) : Ucl(t.to_ucl()) {} 221 222 // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) 223 template <class M, typename std::enable_if< 224 std::is_constructible<std::string, typename M::key_type>::value 225 && std::is_constructible<Ucl, typename M::mapped_type>::value, 226 int>::type = 0> 227 Ucl(const M & m) { 228 obj.reset (ucl_object_typed_new (UCL_OBJECT)); 229 auto cobj = obj.get (); 230 231 for (const auto &e : m) { 232 ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()), 233 e.first.data (), e.first.size (), true); 234 } 235 } 236 237 // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) 238 template <class V, typename std::enable_if< 239 std::is_constructible<Ucl, typename V::value_type>::value, 240 int>::type = 0> 241 Ucl(const V & v) { 242 obj.reset (ucl_object_typed_new (UCL_ARRAY)); 243 auto cobj = obj.get (); 244 245 for (const auto &e : v) { 246 ucl_array_append (cobj, ucl_object_ref (e.obj.get())); 247 } 248 } 249 250 ucl_type_t type () const { 251 if (obj) { 252 return ucl_object_type (obj.get ()); 253 } 254 return UCL_NULL; 255 } 256 257 const std::string key () const { 258 std::string res; 259 260 if (obj->key) { 261 res.assign (obj->key, obj->keylen); 262 } 263 264 return res; 265 } 266 267 double number_value () const 268 { 269 if (obj) { 270 return ucl_object_todouble (obj.get()); 271 } 272 273 return 0.0; 274 } 275 276 int64_t int_value () const 277 { 278 if (obj) { 279 return ucl_object_toint (obj.get()); 280 } 281 282 return 0; 283 } 284 285 bool bool_value () const 286 { 287 if (obj) { 288 return ucl_object_toboolean (obj.get()); 289 } 290 291 return false; 292 } 293 294 const std::string string_value () const 295 { 296 std::string res; 297 298 if (obj) { 299 res.assign (ucl_object_tostring (obj.get())); 300 } 301 302 return res; 303 } 304 305 const Ucl operator[] (size_t i) const 306 { 307 if (type () == UCL_ARRAY) { 308 return Ucl (ucl_array_find_index (obj.get(), i)); 309 } 310 311 return Ucl (nullptr); 312 } 313 314 const Ucl operator[](const std::string &key) const 315 { 316 if (type () == UCL_OBJECT) { 317 return Ucl (ucl_object_find_keyl (obj.get(), 318 key.data (), key.size ())); 319 } 320 321 return Ucl (nullptr); 322 } 323 // Serialize. 324 void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const 325 { 326 struct ucl_emitter_functions cbdata; 327 328 cbdata = Ucl::default_emit_funcs(); 329 cbdata.ud = reinterpret_cast<void *>(&out); 330 331 ucl_object_emit_full (obj.get(), type, &cbdata); 332 } 333 334 std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const 335 { 336 std::string out; 337 338 dump (out, type); 339 340 return out; 341 } 342 343 static Ucl parse (const std::string & in, std::string & err) 344 { 345 auto parser = ucl_parser_new (UCL_PARSER_DEFAULT); 346 347 if (!ucl_parser_add_chunk (parser, (const unsigned char *)in.data (), 348 in.size ())) { 349 err.assign (ucl_parser_get_error (parser)); 350 ucl_parser_free (parser); 351 352 return nullptr; 353 } 354 355 auto obj = ucl_parser_get_object (parser); 356 ucl_parser_free (parser); 357 358 // Obj will handle ownership 359 return Ucl (obj); 360 } 361 362 static Ucl parse (const char * in, std::string & err) 363 { 364 if (in) { 365 return parse (std::string(in), err); 366 } else { 367 err = "null input"; 368 return nullptr; 369 } 370 } 371 372 static Ucl parse (std::istream &ifs, std::string &err) 373 { 374 return Ucl::parse (std::string(std::istreambuf_iterator<char>(ifs), 375 std::istreambuf_iterator<char>()), err); 376 } 377 378 bool operator== (const Ucl &rhs) const 379 { 380 return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0; 381 } 382 bool operator< (const Ucl &rhs) const 383 { 384 return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0; 385 } 386 bool operator!= (const Ucl &rhs) const { return !(*this == rhs); } 387 bool operator<= (const Ucl &rhs) const { return !(rhs < *this); } 388 bool operator> (const Ucl &rhs) const { return (rhs < *this); } 389 bool operator>= (const Ucl &rhs) const { return !(*this < rhs); } 390 391 operator bool () const 392 { 393 if (!obj || type() == UCL_NULL) { 394 return false; 395 } 396 397 if (type () == UCL_BOOLEAN) { 398 return bool_value (); 399 } 400 401 return true; 402 } 403 404 const_iterator begin() const 405 { 406 return const_iterator(*this); 407 } 408 const_iterator cbegin() const 409 { 410 return const_iterator(*this); 411 } 412 const_iterator end() const 413 { 414 return const_iterator(); 415 } 416 const_iterator cend() const 417 { 418 return const_iterator(); 419 } 420 }; 421 422 }; 423