diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/ucl++.h | 422 | ||||
-rw-r--r-- | include/ucl.h | 212 |
2 files changed, 622 insertions, 12 deletions
diff --git a/include/ucl++.h b/include/ucl++.h new file mode 100644 index 0000000..87d2041 --- /dev/null +++ b/include/ucl++.h @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2015, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once +#include <string> +#include <memory> +#include <iostream> +#include <strstream> + +#include "ucl.h" + +// C++11 API inspired by json11: https://github.com/dropbox/json11/ + +namespace ucl { + +struct ucl_map_construct_t { }; +constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t(); +struct ucl_array_construct_t { }; +constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t(); + +class Ucl final { +private: + + struct ucl_deleter { + void operator() (ucl_object_t *obj) { + ucl_object_unref (obj); + } + }; + + static int + append_char (unsigned char c, size_t nchars, void *ud) + { + std::string *out = reinterpret_cast<std::string *>(ud); + + out->append (nchars, (char)c); + + return nchars; + } + static int + append_len (unsigned const char *str, size_t len, void *ud) + { + std::string *out = reinterpret_cast<std::string *>(ud); + + out->append ((const char *)str, len); + + return len; + } + static int + append_int (int64_t elt, void *ud) + { + std::string *out = reinterpret_cast<std::string *>(ud); + auto nstr = std::to_string (elt); + + out->append (nstr); + + return nstr.size (); + } + static int + append_double (double elt, void *ud) + { + std::string *out = reinterpret_cast<std::string *>(ud); + auto nstr = std::to_string (elt); + + out->append (nstr); + + return nstr.size (); + } + + static struct ucl_emitter_functions default_emit_funcs() + { + struct ucl_emitter_functions func = { + Ucl::append_char, + Ucl::append_len, + Ucl::append_int, + Ucl::append_double, + nullptr, + nullptr + }; + + return func; + }; + + std::unique_ptr<ucl_object_t, ucl_deleter> obj; + +public: + class const_iterator { + private: + struct ucl_iter_deleter { + void operator() (ucl_object_iter_t it) { + ucl_object_iterate_free (it); + } + }; + std::shared_ptr<void> it; + std::unique_ptr<Ucl> cur; + public: + typedef std::forward_iterator_tag iterator_category; + + const_iterator(const Ucl &obj) { + it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()), + ucl_iter_deleter()); + cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); + } + + const_iterator() {} + const_iterator(const const_iterator &other) { + it = other.it; + } + ~const_iterator() {} + + const_iterator& operator=(const const_iterator &other) { + it = other.it; + return *this; + } + + bool operator==(const const_iterator &other) const + { + if (cur && other.cur) { + return cur->obj.get() == other.cur->obj.get(); + } + + return !cur && !other.cur; + } + + bool operator!=(const const_iterator &other) const + { + return !(*this == other); + } + + const_iterator& operator++() + { + if (it) { + cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true))); + } + + if (!*cur) { + it.reset (); + cur.reset (); + } + + return *this; + } + + const Ucl& operator*() const + { + return *cur; + } + const Ucl* operator->() const + { + return cur.get(); + } + }; + + // We grab ownership if get non-const ucl_object_t + Ucl(ucl_object_t *other) { + obj.reset (other); + } + + // Shared ownership + Ucl(const ucl_object_t *other) { + obj.reset (ucl_object_ref (other)); + } + + Ucl(const Ucl &other) { + obj.reset (ucl_object_ref (other.obj.get())); + } + + Ucl(Ucl &&other) { + obj.swap (other.obj); + } + + Ucl() noexcept { + obj.reset (ucl_object_typed_new (UCL_NULL)); + } + Ucl(std::nullptr_t) noexcept { + obj.reset (ucl_object_typed_new (UCL_NULL)); + } + Ucl(double value) { + obj.reset (ucl_object_typed_new (UCL_FLOAT)); + obj->value.dv = value; + } + Ucl(int64_t value) { + obj.reset (ucl_object_typed_new (UCL_INT)); + obj->value.iv = value; + } + Ucl(bool value) { + obj.reset (ucl_object_typed_new (UCL_BOOLEAN)); + obj->value.iv = static_cast<int64_t>(value); + } + Ucl(const std::string &value) { + obj.reset (ucl_object_fromstring_common (value.data (), value.size (), + UCL_STRING_RAW)); + } + Ucl(const char * value) { + obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW)); + } + + // Implicit constructor: anything with a to_json() function. + template <class T, class = decltype(&T::to_ucl)> + Ucl(const T & t) : Ucl(t.to_ucl()) {} + + // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) + template <class M, typename std::enable_if< + std::is_constructible<std::string, typename M::key_type>::value + && std::is_constructible<Ucl, typename M::mapped_type>::value, + int>::type = 0> + Ucl(const M & m) { + obj.reset (ucl_object_typed_new (UCL_OBJECT)); + auto cobj = obj.get (); + + for (const auto &e : m) { + ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()), + e.first.data (), e.first.size (), true); + } + } + + // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) + template <class V, typename std::enable_if< + std::is_constructible<Ucl, typename V::value_type>::value, + int>::type = 0> + Ucl(const V & v) { + obj.reset (ucl_object_typed_new (UCL_ARRAY)); + auto cobj = obj.get (); + + for (const auto &e : v) { + ucl_array_append (cobj, ucl_object_ref (e.obj.get())); + } + } + + ucl_type_t type () const { + if (obj) { + return ucl_object_type (obj.get ()); + } + return UCL_NULL; + } + + const std::string key () const { + std::string res; + + if (obj->key) { + res.assign (obj->key, obj->keylen); + } + + return res; + } + + double number_value () const + { + if (obj) { + return ucl_object_todouble (obj.get()); + } + + return 0.0; + } + + int64_t int_value () const + { + if (obj) { + return ucl_object_toint (obj.get()); + } + + return 0; + } + + bool bool_value () const + { + if (obj) { + return ucl_object_toboolean (obj.get()); + } + + return false; + } + + const std::string string_value () const + { + std::string res; + + if (obj) { + res.assign (ucl_object_tostring (obj.get())); + } + + return res; + } + + const Ucl operator[] (size_t i) const + { + if (type () == UCL_ARRAY) { + return Ucl (ucl_array_find_index (obj.get(), i)); + } + + return Ucl (nullptr); + } + + const Ucl operator[](const std::string &key) const + { + if (type () == UCL_OBJECT) { + return Ucl (ucl_object_find_keyl (obj.get(), + key.data (), key.size ())); + } + + return Ucl (nullptr); + } + // Serialize. + void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const + { + struct ucl_emitter_functions cbdata; + + cbdata = Ucl::default_emit_funcs(); + cbdata.ud = reinterpret_cast<void *>(&out); + + ucl_object_emit_full (obj.get(), type, &cbdata); + } + + std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const + { + std::string out; + + dump (out, type); + + return out; + } + + static Ucl parse (const std::string & in, std::string & err) + { + auto parser = ucl_parser_new (UCL_PARSER_DEFAULT); + + if (!ucl_parser_add_chunk (parser, (const unsigned char *)in.data (), + in.size ())) { + err.assign (ucl_parser_get_error (parser)); + ucl_parser_free (parser); + + return nullptr; + } + + auto obj = ucl_parser_get_object (parser); + ucl_parser_free (parser); + + // Obj will handle ownership + return Ucl (obj); + } + + static Ucl parse (const char * in, std::string & err) + { + if (in) { + return parse (std::string(in), err); + } else { + err = "null input"; + return nullptr; + } + } + + static Ucl parse (std::istream &ifs, std::string &err) + { + return Ucl::parse (std::string(std::istreambuf_iterator<char>(ifs), + std::istreambuf_iterator<char>()), err); + } + + bool operator== (const Ucl &rhs) const + { + return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0; + } + bool operator< (const Ucl &rhs) const + { + return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0; + } + bool operator!= (const Ucl &rhs) const { return !(*this == rhs); } + bool operator<= (const Ucl &rhs) const { return !(rhs < *this); } + bool operator> (const Ucl &rhs) const { return (rhs < *this); } + bool operator>= (const Ucl &rhs) const { return !(*this < rhs); } + + operator bool () const + { + if (!obj || type() == UCL_NULL) { + return false; + } + + if (type () == UCL_BOOLEAN) { + return bool_value (); + } + + return true; + } + + const_iterator begin() const + { + return const_iterator(*this); + } + const_iterator cbegin() const + { + return const_iterator(*this); + } + const_iterator end() const + { + return const_iterator(); + } + const_iterator cend() const + { + return const_iterator(); + } +}; + +}; diff --git a/include/ucl.h b/include/ucl.h index 823ac8d..82a1fd1 100644 --- a/include/ucl.h +++ b/include/ucl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013, Vsevolod Stakhov +/* Copyright (c) 2013-2015, Vsevolod Stakhov * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -135,7 +135,9 @@ typedef enum ucl_emitter { UCL_EMIT_JSON = 0, /**< Emit fine formatted JSON */ UCL_EMIT_JSON_COMPACT, /**< Emit compacted JSON */ UCL_EMIT_CONFIG, /**< Emit human readable config format */ - UCL_EMIT_YAML /**< Emit embedded YAML format */ + UCL_EMIT_YAML, /**< Emit embedded YAML format */ + UCL_EMIT_MSGPACK, /**< Emit msgpack output */ + UCL_EMIT_MAX /**< Unsupported emitter type */ } ucl_emitter_t; /** @@ -145,6 +147,7 @@ typedef enum ucl_emitter { * UCL still has to perform copying implicitly. */ typedef enum ucl_parser_flags { + UCL_PARSER_DEFAULT = 0x0, /**< No special flags */ UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */ UCL_PARSER_ZEROCOPY = 0x2, /**< Parse input in zero-copy mode if possible */ UCL_PARSER_NO_TIME = 0x4, /**< Do not parse time and treat time values as strings */ @@ -155,6 +158,7 @@ typedef enum ucl_parser_flags { * String conversion flags, that are used in #ucl_object_fromstring_common function. */ typedef enum ucl_string_flags { + UCL_STRING_RAW = 0x0, /**< Treat string as is */ UCL_STRING_ESCAPE = 0x1, /**< Perform JSON escape */ UCL_STRING_TRIM = 0x2, /**< Trim leading and trailing whitespaces */ UCL_STRING_PARSE_BOOLEAN = 0x4, /**< Parse passed string and detect boolean */ @@ -172,15 +176,36 @@ typedef enum ucl_string_flags { * Basic flags for an object */ typedef enum ucl_object_flags { - UCL_OBJECT_ALLOCATED_KEY = 0x1, /**< An object has key allocated internally */ - UCL_OBJECT_ALLOCATED_VALUE = 0x2, /**< An object has a string value allocated internally */ - UCL_OBJECT_NEED_KEY_ESCAPE = 0x4, /**< The key of an object need to be escaped on output */ - UCL_OBJECT_EPHEMERAL = 0x8, /**< Temporary object that does not need to be freed really */ - UCL_OBJECT_MULTILINE = 0x10, /**< String should be displayed as multiline string */ - UCL_OBJECT_MULTIVALUE = 0x20 /**< Object is a key with multiple values */ + UCL_OBJECT_ALLOCATED_KEY = (1 << 0), /**< An object has key allocated internally */ + UCL_OBJECT_ALLOCATED_VALUE = (1 << 1), /**< An object has a string value allocated internally */ + UCL_OBJECT_NEED_KEY_ESCAPE = (1 << 2), /**< The key of an object need to be escaped on output */ + UCL_OBJECT_EPHEMERAL = (1 << 3), /**< Temporary object that does not need to be freed really */ + UCL_OBJECT_MULTILINE = (1 << 4), /**< String should be displayed as multiline string */ + UCL_OBJECT_MULTIVALUE = (1 << 5), /**< Object is a key with multiple values */ + UCL_OBJECT_INHERITED = (1 << 6), /**< Object has been inherited from another */ + UCL_OBJECT_BINARY = (1 << 7) /**< Object contains raw binary data */ } ucl_object_flags_t; /** + * Duplicate policy types + */ +enum ucl_duplicate_strategy { + UCL_DUPLICATE_APPEND = 0, /**< Default policy to merge based on priorities */ + UCL_DUPLICATE_MERGE, /**< Merge new object with old one */ + UCL_DUPLICATE_REWRITE, /**< Rewrite old keys */ + UCL_DUPLICATE_ERROR /**< Stop parsing on duplicate found */ +}; + +/** + * Input format type + */ +enum ucl_parse_type { + UCL_PARSE_UCL = 0, /**< Default ucl format */ + UCL_PARSE_MSGPACK, /**< Message pack input format */ + UCL_PARSE_CSEXP /**< Canonical S-expressions */ +}; + +/** * UCL object structure. Please mention that the most of fields should not be touched by * UCL users. In future, this structure may be converted to private one. */ @@ -190,7 +215,7 @@ typedef struct ucl_object_s { */ union { int64_t iv; /**< Int value of an object */ - const char *sv; /**< String value of an object */ + const char *sv; /**< String value of an object */ double dv; /**< Double value of an object */ void *av; /**< Array */ void *ov; /**< Object */ @@ -496,6 +521,15 @@ UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top, unsigned int index); /** + * Return the index of `elt` in the array `top` + * @param top object to get a key from (must be of type UCL_ARRAY) + * @param elt element to find index of (must NOT be NULL) + * @return index of `elt` in the array `top or (unsigned int)-1 if `elt` is not found + */ +UCL_EXTERN unsigned int ucl_array_index_of (ucl_object_t *top, + ucl_object_t *elt); + +/** * Replace an element in an array with a different element, returning the object * that was replaced. This object is not released, caller must unref the * returned object when it is no longer needed. @@ -612,6 +646,19 @@ UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj, const char *key); /** + * Return object identified by a key in the specified object, if the first key is + * not found then look for the next one. This process is repeated unless + * the next argument in the list is not NULL. So, `ucl_object_find_any_key(obj, key, NULL)` + * is equal to `ucl_object_find_key(obj, key)` + * @param obj object to get a key from (must be of type UCL_OBJECT) + * @param key key to search + * @param ... list of alternative keys to search (NULL terminated) + * @return object matching the specified key or NULL if key was not found + */ +UCL_EXTERN const ucl_object_t* ucl_object_find_any_key (const ucl_object_t *obj, + const char *key, ...); + +/** * Return object identified by a fixed size key in the specified object * @param obj object to get a key from (must be of type UCL_OBJECT) * @param key key to search @@ -631,6 +678,16 @@ UCL_EXTERN const ucl_object_t *ucl_lookup_path (const ucl_object_t *obj, const char *path); /** + * Return object identified by object notation string using arbitrary delimiter + * @param obj object to search in + * @param path dot.notation.path to the path to lookup. May use numeric .index on arrays + * @param sep the sepatorator to use in place of . (incase keys have . in them) + * @return object matched the specified path or NULL if path is not found + */ +UCL_EXTERN const ucl_object_t *ucl_lookup_path_char (const ucl_object_t *obj, + const char *path, char sep); + +/** * Returns a key of an object as a NULL terminated string * @param obj CL object * @return key or NULL if there is no key @@ -683,7 +740,7 @@ UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1, * @param cmp */ UCL_EXTERN void ucl_object_array_sort (ucl_object_t *ar, - int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2)); + int (*cmp)(const ucl_object_t **o1, const ucl_object_t **o2)); /** * Get the priority for specific UCL object @@ -769,6 +826,21 @@ typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, const ucl_object_t *arguments, void* ud); +/** + * Context dependent macro handler for a parser + * @param data the content of macro + * @param len the length of content + * @param arguments arguments object + * @param context previously parsed context + * @param ud opaque user data + * @param err error pointer + * @return true if macro has been parsed + */ +typedef bool (*ucl_context_macro_handler) (const unsigned char *data, size_t len, + const ucl_object_t *arguments, + const ucl_object_t *context, + void* ud); + /* Opaque parser */ struct ucl_parser; @@ -780,16 +852,38 @@ struct ucl_parser; UCL_EXTERN struct ucl_parser* ucl_parser_new (int flags); /** + * Sets the default priority for the parser applied to chunks that does not + * specify priority explicitly + * @param parser parser object + * @param prio default priority (0 .. 16) + * @return true if parser's default priority was set + */ +UCL_EXTERN bool ucl_parser_set_default_priority (struct ucl_parser *parser, + unsigned prio); +/** * Register new handler for a macro * @param parser parser object * @param macro macro name (without leading dot) * @param handler handler (it is called immediately after macro is parsed) * @param ud opaque user data for a handler */ -UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, const char *macro, +UCL_EXTERN void ucl_parser_register_macro (struct ucl_parser *parser, + const char *macro, ucl_macro_handler handler, void* ud); /** + * Register new context dependent handler for a macro + * @param parser parser object + * @param macro macro name (without leading dot) + * @param handler handler (it is called immediately after macro is parsed) + * @param ud opaque user data for a handler + */ +UCL_EXTERN void ucl_parser_register_context_macro (struct ucl_parser *parser, + const char *macro, + ucl_context_macro_handler handler, + void* ud); + +/** * Handler to detect unregistered variables * @param data variable data * @param len length of variable @@ -843,6 +937,21 @@ UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data, size_t len, unsigned priority); /** + * Full version of ucl_add_chunk with priority and duplicate strategy + * @param parser parser structure + * @param data the pointer to the beginning of a chunk + * @param len the length of a chunk + * @param priority the desired priority of a chunk (only 4 least significant bits + * are considered for this parameter) + * @param strat duplicates merging strategy + * @param parse_type input format + * @return true if chunk has been added and false in case of error + */ +UCL_EXTERN bool ucl_parser_add_chunk_full (struct ucl_parser *parser, + const unsigned char *data, size_t len, unsigned priority, + enum ucl_duplicate_strategy strat, enum ucl_parse_type parse_type); + +/** * Load ucl object from a string * @param parser parser structure * @param data the pointer to the string @@ -853,6 +962,18 @@ UCL_EXTERN bool ucl_parser_add_string (struct ucl_parser *parser, const char *data,size_t len); /** + * Load ucl object from a string + * @param parser parser structure + * @param data the pointer to the string + * @param len the length of the string, if `len` is 0 then `data` must be zero-terminated string + * @param priority the desired priority of a chunk (only 4 least significant bits + * are considered for this parameter) + * @return true if string has been added and false in case of error + */ +UCL_EXTERN bool ucl_parser_add_string_priority (struct ucl_parser *parser, + const char *data, size_t len, unsigned priority); + +/** * Load and add data from a file * @param parser parser structure * @param filename the name of file @@ -863,6 +984,18 @@ UCL_EXTERN bool ucl_parser_add_file (struct ucl_parser *parser, const char *filename); /** + * Load and add data from a file + * @param parser parser structure + * @param filename the name of file + * @param err if *err is NULL it is set to parser error + * @param priority the desired priority of a chunk (only 4 least significant bits + * are considered for this parameter) + * @return true if chunk has been added and false in case of error + */ +UCL_EXTERN bool ucl_parser_add_file_priority (struct ucl_parser *parser, + const char *filename, unsigned priority); + +/** * Load and add data from a file descriptor * @param parser parser structure * @param filename the name of file @@ -873,6 +1006,28 @@ UCL_EXTERN bool ucl_parser_add_fd (struct ucl_parser *parser, int fd); /** + * Load and add data from a file descriptor + * @param parser parser structure + * @param filename the name of file + * @param err if *err is NULL it is set to parser error + * @param priority the desired priority of a chunk (only 4 least significant bits + * are considered for this parameter) + * @return true if chunk has been added and false in case of error + */ +UCL_EXTERN bool ucl_parser_add_fd_priority (struct ucl_parser *parser, + int fd, unsigned priority); + +/** + * Provide a UCL_ARRAY of paths to search for include files. The object is + * copied so caller must unref the object. + * @param parser parser structure + * @param paths UCL_ARRAY of paths to search + * @return true if the path search array was replaced in the parser + */ +UCL_EXTERN bool ucl_set_include_path (struct ucl_parser *parser, + ucl_object_t *paths); + +/** * Get a top object for a parser (refcount is increased) * @param parser parser structure * @param err if *err is NULL it is set to parser error @@ -881,12 +1036,34 @@ UCL_EXTERN bool ucl_parser_add_fd (struct ucl_parser *parser, UCL_EXTERN ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser); /** - * Get the error string if failing + * Get the error string if parsing has been failed * @param parser parser object + * @return error description */ UCL_EXTERN const char *ucl_parser_get_error(struct ucl_parser *parser); /** + * Get the code of the last error + * @param parser parser object + * @return error code + */ +UCL_EXTERN int ucl_parser_get_error_code(struct ucl_parser *parser); + +/** + * Get the current column number within parser + * @param parser parser object + * @return current column number + */ +UCL_EXTERN unsigned ucl_parser_get_column(struct ucl_parser *parser); + +/** + * Get the current line number within parser + * @param parser parser object + * @return current line number + */ +UCL_EXTERN unsigned ucl_parser_get_linenum(struct ucl_parser *parser); + +/** * Clear the error in the parser * @param parser parser object */ @@ -994,6 +1171,17 @@ UCL_EXTERN unsigned char *ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type); /** + * Emit object to a string that can contain `\0` inside + * @param obj object + * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is + * #UCL_EMIT_CONFIG then emit config like object + * @param len the resulting length + * @return dump of an object (must be freed after using) or NULL in case of error + */ +UCL_EXTERN unsigned char *ucl_object_emit_len (const ucl_object_t *obj, + enum ucl_emitter emit_type, size_t *len); + +/** * Emit object to a string * @param obj object * @param emit_type if type is #UCL_EMIT_JSON then emit json, if type is |