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#include <tuple>
33
34#include "ucl.h"
35
36// C++11 API inspired by json11: https://github.com/dropbox/json11/
37
38namespace ucl {
39
40struct ucl_map_construct_t { };
41constexpr ucl_map_construct_t ucl_map_construct = ucl_map_construct_t();
42struct ucl_array_construct_t { };
43constexpr ucl_array_construct_t ucl_array_construct = ucl_array_construct_t();
44
45class Ucl final {
46private:
47
48	struct ucl_deleter {
49		void operator() (ucl_object_t *obj) {
50			ucl_object_unref (obj);
51		}
52	};
53
54	static int
55	append_char (unsigned char c, size_t nchars, void *ud)
56	{
57		std::string *out = reinterpret_cast<std::string *>(ud);
58
59		out->append (nchars, (char)c);
60
61		return nchars;
62	}
63	static int
64	append_len (unsigned const char *str, size_t len, void *ud)
65	{
66		std::string *out = reinterpret_cast<std::string *>(ud);
67
68		out->append ((const char *)str, len);
69
70		return len;
71	}
72	static int
73	append_int (int64_t elt, void *ud)
74	{
75		std::string *out = reinterpret_cast<std::string *>(ud);
76		auto nstr = std::to_string (elt);
77
78		out->append (nstr);
79
80		return nstr.size ();
81	}
82	static int
83	append_double (double elt, void *ud)
84	{
85		std::string *out = reinterpret_cast<std::string *>(ud);
86		auto nstr = std::to_string (elt);
87
88		out->append (nstr);
89
90		return nstr.size ();
91	}
92
93	static struct ucl_emitter_functions default_emit_funcs()
94	{
95		struct ucl_emitter_functions func = {
96			Ucl::append_char,
97			Ucl::append_len,
98			Ucl::append_int,
99			Ucl::append_double,
100			nullptr,
101			nullptr
102		};
103
104		return func;
105	};
106
107	static bool ucl_variable_getter(const unsigned char *data, size_t len,
108			unsigned char ** /*replace*/, size_t * /*replace_len*/, bool *need_free, void* ud)
109	{
110		*need_free = false;
111
112		auto vars = reinterpret_cast<std::set<std::string> *>(ud);
113		if (vars && data && len != 0) {
114			vars->emplace (data, data + len);
115		}
116		return false;
117	}
118
119	static bool ucl_variable_replacer (const unsigned char *data, size_t len,
120			unsigned char **replace, size_t *replace_len, bool *need_free, void* ud)
121	{
122		*need_free = false;
123
124		auto replacer = reinterpret_cast<variable_replacer *>(ud);
125		if (!replacer) {
126			return false;
127		}
128
129		std::string var_name (data, data + len);
130		if (!replacer->is_variable (var_name)) {
131			return false;
132		}
133
134		std::string var_value = replacer->replace (var_name);
135		if (var_value.empty ()) {
136			return false;
137 		}
138
139		*replace = (unsigned char *)UCL_ALLOC (var_value.size ());
140		memcpy (*replace, var_value.data (), var_value.size ());
141
142		*replace_len = var_value.size ();
143		*need_free = true;
144
145		return true;
146	}
147
148	template <typename C, typename P>
149	static Ucl parse_with_strategy_function (C config_func, P parse_func, std::string &err)
150	{
151		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
152
153		config_func (parser);
154
155		if (!parse_func (parser)) {
156			const char *error = ucl_parser_get_error (parser); //Assigning here without checking result first causes a
157			if( error != NULL ) err.assign(error);             //	crash if ucl_parser_get_error returns NULL
158			ucl_parser_free (parser);
159
160			return nullptr;
161		}
162
163		auto obj = ucl_parser_get_object (parser);
164		ucl_parser_free (parser);
165
166		// Obj will handle ownership
167		return Ucl (obj);
168	}
169
170	std::unique_ptr<ucl_object_t, ucl_deleter> obj;
171
172public:
173	struct macro_handler_s {
174		ucl_macro_handler         handler;
175		ucl_context_macro_handler ctx_handler;
176	};
177
178	struct macro_userdata_s {
179		ucl_parser    *parser;
180		void          *userdata;
181	};
182
183	class const_iterator {
184	private:
185		struct ucl_iter_deleter {
186			void operator() (ucl_object_iter_t it) {
187				ucl_object_iterate_free (it);
188			}
189		};
190		std::shared_ptr<void> it;
191		std::unique_ptr<Ucl> cur;
192	public:
193		typedef std::forward_iterator_tag iterator_category;
194
195		const_iterator(const Ucl &obj) {
196			it = std::shared_ptr<void>(ucl_object_iterate_new (obj.obj.get()),
197				ucl_iter_deleter());
198			cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
199			if (!cur->obj) {
200				it.reset ();
201				cur.reset ();
202			}
203		}
204
205		const_iterator() {}
206		const_iterator(const const_iterator &other) = delete;
207		const_iterator(const_iterator &&other) = default;
208		~const_iterator() {}
209
210		const_iterator& operator=(const const_iterator &other) = delete;
211		const_iterator& operator=(const_iterator &&other) = default;
212
213		bool operator==(const const_iterator &other) const
214		{
215			if (cur && other.cur) {
216				return cur->obj.get() == other.cur->obj.get();
217			}
218
219			return !cur && !other.cur;
220		}
221
222		bool operator!=(const const_iterator &other) const
223		{
224			return !(*this == other);
225		}
226
227		const_iterator& operator++()
228		{
229			if (it) {
230				cur.reset (new Ucl(ucl_object_iterate_safe (it.get(), true)));
231			}
232
233			if (cur && !cur->obj) {
234				it.reset ();
235				cur.reset ();
236			}
237
238			return *this;
239		}
240
241		const Ucl& operator*() const
242		{
243			return *cur;
244		}
245		const Ucl* operator->() const
246		{
247			return cur.get();
248		}
249	};
250
251	struct variable_replacer {
252		virtual ~variable_replacer() {}
253
254		virtual bool is_variable (const std::string &str) const
255		{
256			return !str.empty ();
257		}
258
259		virtual std::string replace (const std::string &var) const = 0;
260	};
261
262	// We grab ownership if get non-const ucl_object_t
263	Ucl(ucl_object_t *other) {
264		obj.reset (other);
265	}
266
267	// Shared ownership
268	Ucl(const ucl_object_t *other) {
269		obj.reset (ucl_object_ref (other));
270	}
271
272	Ucl(const Ucl &other) {
273		obj.reset (ucl_object_ref (other.obj.get()));
274	}
275
276	Ucl(Ucl &&other) {
277		obj.swap (other.obj);
278	}
279
280	Ucl() noexcept {
281		obj.reset (ucl_object_typed_new (UCL_NULL));
282	}
283	Ucl(std::nullptr_t) noexcept {
284		obj.reset (ucl_object_typed_new (UCL_NULL));
285	}
286	Ucl(double value) {
287		obj.reset (ucl_object_typed_new (UCL_FLOAT));
288		obj->value.dv = value;
289	}
290	Ucl(int64_t value) {
291		obj.reset (ucl_object_typed_new (UCL_INT));
292		obj->value.iv = value;
293	}
294	Ucl(bool value) {
295		obj.reset (ucl_object_typed_new (UCL_BOOLEAN));
296		obj->value.iv = static_cast<int64_t>(value);
297	}
298	Ucl(const std::string &value) {
299		obj.reset (ucl_object_fromstring_common (value.data (), value.size (),
300				UCL_STRING_RAW));
301	}
302	Ucl(const char *value) {
303		obj.reset (ucl_object_fromstring_common (value, 0, UCL_STRING_RAW));
304	}
305
306	// Implicit constructor: anything with a to_json() function.
307	template <class T, class = decltype(&T::to_ucl)>
308	Ucl(const T &t) : Ucl(t.to_ucl()) {}
309
310	// Implicit constructor: map-like objects (std::map, std::unordered_map, etc)
311	template <class M, typename std::enable_if<
312		std::is_constructible<std::string, typename M::key_type>::value
313		&& std::is_constructible<Ucl, typename M::mapped_type>::value,
314		int>::type = 0>
315	Ucl(const M &m) {
316		obj.reset (ucl_object_typed_new (UCL_OBJECT));
317		auto cobj = obj.get ();
318
319		for (const auto &e : m) {
320			ucl_object_insert_key (cobj, ucl_object_ref (e.second.obj.get()),
321					e.first.data (), e.first.size (), true);
322		}
323	}
324
325	// Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc)
326	template <class V, typename std::enable_if<
327		std::is_constructible<Ucl, typename V::value_type>::value,
328		int>::type = 0>
329	Ucl(const V &v) {
330		obj.reset (ucl_object_typed_new (UCL_ARRAY));
331		auto cobj = obj.get ();
332
333		for (const auto &e : v) {
334			ucl_array_append (cobj, ucl_object_ref (e.obj.get()));
335		}
336	}
337
338	ucl_type_t type () const {
339		if (obj) {
340			return ucl_object_type (obj.get ());
341		}
342		return UCL_NULL;
343	}
344
345	std::string key () const {
346		std::string res;
347
348		if (obj->key) {
349			res.assign (obj->key, obj->keylen);
350		}
351
352		return res;
353	}
354
355	double number_value (const double default_val = 0.0) const
356	{
357		double res;
358
359		if (ucl_object_todouble_safe(obj.get(), &res)) {
360			return res;
361		}
362
363		return default_val;
364	}
365
366	int64_t int_value (const int64_t default_val = 0) const
367	{
368		int64_t res;
369
370		if (ucl_object_toint_safe(obj.get(), &res)) {
371			return res;
372		}
373
374		return default_val;
375	}
376
377	bool bool_value (const bool default_val = false) const
378	{
379		bool res;
380
381		if (ucl_object_toboolean_safe(obj.get(), &res)) {
382			return res;
383		}
384
385		return default_val;
386	}
387
388	std::string string_value (const std::string& default_val = "") const
389	{
390		const char* res = nullptr;
391
392		if (ucl_object_tostring_safe(obj.get(), &res)) {
393			return res;
394		}
395
396		return default_val;
397	}
398
399	size_t size () const
400	{
401		if (type () == UCL_ARRAY) {
402			return ucl_array_size (obj.get());
403		}
404
405		return 0;
406	}
407
408	Ucl at (size_t i) const
409	{
410		if (type () == UCL_ARRAY) {
411			return Ucl (ucl_array_find_index (obj.get(), i));
412		}
413
414		return Ucl (nullptr);
415	}
416
417	Ucl lookup (const std::string &key) const
418	{
419		if (type () == UCL_OBJECT) {
420			return Ucl (ucl_object_lookup_len (obj.get(),
421					key.data (), key.size ()));
422		}
423
424		return Ucl (nullptr);
425	}
426
427	inline Ucl operator[] (size_t i) const
428	{
429		return at(i);
430	}
431
432	inline Ucl operator[](const std::string &key) const
433	{
434		return lookup(key);
435	}
436	// Serialize.
437	void dump (std::string &out, ucl_emitter_t type = UCL_EMIT_JSON) const
438	{
439		struct ucl_emitter_functions cbdata;
440
441		cbdata = Ucl::default_emit_funcs();
442		cbdata.ud = reinterpret_cast<void *>(&out);
443
444		ucl_object_emit_full (obj.get(), type, &cbdata, nullptr);
445	}
446
447	std::string dump (ucl_emitter_t type = UCL_EMIT_JSON) const
448	{
449		std::string out;
450
451		dump (out, type);
452
453		return out;
454	}
455
456	static Ucl parse (const std::string &in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
457	{
458		return parse (in, std::map<std::string, std::string>(), err, duplicate_strategy);
459	}
460
461	static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars,
462			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
463	{
464		std::vector< std::tuple< std::string, macro_handler_s, void * > > emptyVector;
465		return parse ( in, vars, emptyVector, err, duplicate_strategy );
466	}
467
468	//Macro handler will receive a macro_userdata_s as void *ud
469	static Ucl parse (const std::string &in,
470			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
471			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
472	{
473		return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
474	}
475
476	//Macro handler will receive a macro_userdata_s as void *ud
477	static Ucl parse (const std::string &in, const std::map<std::string, std::string> &vars,
478			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
479			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
480	{
481		//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
482		std::vector<macro_userdata_s> userdata_list;
483		userdata_list.reserve (macros.size());
484		auto config_func = [&userdata_list, &vars, &macros] (ucl_parser *parser) {
485			for (const auto & item : vars) {
486				ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
487			}
488			for (auto & macro : macros) {
489				userdata_list.push_back ({parser, std::get<2>(macro)});
490				if (std::get<1>(macro).handler != NULL) {
491					ucl_parser_register_macro (parser,
492								std::get<0>(macro).c_str(),
493								std::get<1>(macro).handler,
494								reinterpret_cast<void*>(&userdata_list.back()));
495				}
496				else if (std::get<1>(macro).ctx_handler != NULL) {
497					ucl_parser_register_context_macro (parser,
498									std::get<0>(macro).c_str(),
499									std::get<1>(macro).ctx_handler,
500									reinterpret_cast<void*>(&userdata_list.back()));
501				}
502			}
503		};
504
505		auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
506			return ucl_parser_add_chunk_full (parser,
507							(unsigned char *) in.data (),
508							in.size (),
509							(unsigned int)ucl_parser_get_default_priority (parser),
510							duplicate_strategy,
511							UCL_PARSE_UCL);
512		};
513
514		return parse_with_strategy_function (config_func, parse_func, err);
515	}
516
517	static Ucl parse (const std::string &in, const variable_replacer &replacer,
518			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
519	{
520		std::vector< std::tuple< std::string, macro_handler_s, void * > > emptyVector;
521		return parse ( in, replacer, emptyVector, err, duplicate_strategy );
522	}
523
524	//Macro handler will receive a macro_userdata_s as void *ud
525	static Ucl parse (const std::string &in, const variable_replacer &replacer,
526			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
527			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
528	{
529		//Preserve macro_userdata_s memory for later use in parse_with_strategy_function()
530		std::vector<macro_userdata_s> userdata_list;
531		userdata_list.reserve (macros.size());
532		auto config_func = [&userdata_list, &replacer, &macros] (ucl_parser *parser) {
533			ucl_parser_set_variables_handler (parser, ucl_variable_replacer, &const_cast<variable_replacer &>(replacer));
534			for (auto & macro : macros) {
535				userdata_list.push_back ({parser, std::get<2>(macro)});
536				if (std::get<1>(macro).handler != NULL) {
537					ucl_parser_register_macro (parser,
538								std::get<0>(macro).c_str(),
539								std::get<1>(macro).handler,
540								reinterpret_cast<void*>(&userdata_list.back()));
541				}
542				else if (std::get<1>(macro).ctx_handler != NULL) {
543					ucl_parser_register_context_macro (parser,
544									std::get<0>(macro).c_str(),
545									std::get<1>(macro).ctx_handler,
546									reinterpret_cast<void*>(&userdata_list.back()));
547				}
548			}
549		};
550
551		auto parse_func = [&in, &duplicate_strategy] (struct ucl_parser *parser) -> bool {
552			return ucl_parser_add_chunk_full (parser,
553							(unsigned char *) in.data (),
554							in.size (),
555							(unsigned int)ucl_parser_get_default_priority (parser),
556							duplicate_strategy,
557							UCL_PARSE_UCL);
558		};
559
560		return parse_with_strategy_function (config_func, parse_func, err);
561	}
562
563	static Ucl parse (const char *in, std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
564	{
565		return parse (in, std::map<std::string, std::string>(), err, duplicate_strategy);
566	}
567
568	static Ucl parse (const char *in, const std::map<std::string, std::string> &vars, std::string &err)
569	{
570		if (!in) {
571			err = "null input";
572			return nullptr;
573		}
574		return parse (std::string (in), vars, err);
575	}
576
577	//Macro handler will receive a macro_userdata_s as void *ud
578	static Ucl parse (const char *in,
579			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
580			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
581	{
582		return parse (in, std::map<std::string, std::string>(), macros, err, duplicate_strategy);
583	}
584
585	//Macro handler will receive a macro_userdata_s as void *ud
586	static Ucl parse (const char *in, const std::map<std::string, std::string> &vars,
587			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
588			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
589	{
590		if (!in) {
591			err = "null input";
592			return nullptr;
593		}
594		return parse (std::string (in), vars, macros, err, duplicate_strategy);
595	}
596
597	static Ucl parse (const char *in, const variable_replacer &replacer,
598			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
599	{
600		if (!in) {
601			err = "null input";
602			return nullptr;
603		}
604		return parse (std::string(in), replacer, err, duplicate_strategy);
605	}
606
607	//Macro handler will receive a macro_userdata_s as void *ud
608	static Ucl parse (const char *in, const variable_replacer &replacer,
609			std::vector< std::tuple< std::string /*name*/, macro_handler_s, void * /*userdata*/ > > &macros,
610			std::string &err, enum ucl_duplicate_strategy duplicate_strategy = UCL_DUPLICATE_APPEND)
611	{
612		if (!in) {
613			err = "null input";
614			return nullptr;
615		}
616		return parse (std::string (in), replacer, macros, err, duplicate_strategy);
617	}
618
619	static Ucl parse_from_file (const std::string &filename, std::string &err)
620	{
621		return parse_from_file (filename, std::map<std::string, std::string>(), err);
622	}
623
624	static Ucl parse_from_file (const std::string &filename, const std::map<std::string, std::string> &vars, std::string &err)
625	{
626		auto config_func = [&vars] (ucl_parser *parser) {
627			for (const auto & item : vars) {
628				ucl_parser_register_variable (parser, item.first.c_str (), item.second.c_str ());
629            }
630		};
631
632		auto parse_func = [&filename] (ucl_parser *parser) {
633			return ucl_parser_add_file (parser, filename.c_str ());
634		};
635
636		return parse_with_strategy_function (config_func, parse_func, err);
637	}
638
639	static Ucl parse_from_file (const std::string &filename, const variable_replacer &replacer, std::string &err)
640	{
641		auto config_func = [&replacer] (ucl_parser *parser) {
642			ucl_parser_set_variables_handler (parser, ucl_variable_replacer,
643				&const_cast<variable_replacer &>(replacer));
644		};
645
646		auto parse_func = [&filename] (ucl_parser *parser) {
647			return ucl_parser_add_file (parser, filename.c_str ());
648		};
649
650		return parse_with_strategy_function (config_func, parse_func, err);
651	}
652
653	static std::vector<std::string> find_variable (const std::string &in)
654	{
655		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
656
657		std::set<std::string> vars;
658		ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars);
659		ucl_parser_add_chunk (parser, (const unsigned char *)in.data (), in.size ());
660		ucl_parser_free (parser);
661
662		std::vector<std::string> result;
663		std::move (vars.begin (), vars.end (), std::back_inserter (result));
664		return result;
665	}
666
667	static std::vector<std::string> find_variable (const char *in)
668	{
669		if (!in) {
670			return std::vector<std::string>();
671		}
672		return find_variable (std::string (in));
673	}
674
675	static std::vector<std::string> find_variable_from_file (const std::string &filename)
676	{
677		auto parser = ucl_parser_new (UCL_PARSER_DEFAULT);
678
679		std::set<std::string> vars;
680		ucl_parser_set_variables_handler (parser, ucl_variable_getter, &vars);
681		ucl_parser_add_file (parser, filename.c_str ());
682		ucl_parser_free (parser);
683
684		std::vector<std::string> result;
685		std::move (vars.begin (), vars.end (), std::back_inserter (result));
686		return result;
687	}
688
689	Ucl& operator= (Ucl rhs)
690	{
691		obj.swap (rhs.obj);
692		return *this;
693	}
694
695	bool operator== (const Ucl &rhs) const
696	{
697		return ucl_object_compare (obj.get(), rhs.obj.get ()) == 0;
698	}
699	bool operator< (const Ucl &rhs) const
700	{
701		return ucl_object_compare (obj.get(), rhs.obj.get ()) < 0;
702	}
703	bool operator!= (const Ucl &rhs) const { return !(*this == rhs); }
704	bool operator<= (const Ucl &rhs) const { return !(rhs < *this); }
705	bool operator> (const Ucl &rhs) const { return (rhs < *this); }
706	bool operator>= (const Ucl &rhs) const { return !(*this < rhs); }
707
708	explicit operator bool () const
709	{
710		if (!obj || type() == UCL_NULL) {
711			return false;
712		}
713
714		if (type () == UCL_BOOLEAN) {
715			return bool_value ();
716		}
717
718		return true;
719	}
720
721	const_iterator begin() const
722	{
723		return const_iterator(*this);
724	}
725	const_iterator cbegin() const
726	{
727		return const_iterator(*this);
728	}
729	const_iterator end() const
730	{
731		return const_iterator();
732	}
733	const_iterator cend() const
734	{
735		return const_iterator();
736	}
737};
738
739};
740