1 /**
2  * Copyright (c) 2013-2019, Timothy Stack
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  * * Neither the name of Timothy Stack nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * @file yajlpp.hh
30  */
31 
32 #ifndef yajlpp_hh
33 #define yajlpp_hh
34 
35 #include <string.h>
36 #include <stdarg.h>
37 #include <limits.h>
38 
39 #include <map>
40 #include <memory>
41 #include <set>
42 #include <stack>
43 #include <utility>
44 #include <vector>
45 #include <string>
46 #include <functional>
47 
48 #include "optional.hpp"
49 #include "pcrepp/pcrepp.hh"
50 #include "json_ptr.hh"
51 #include "base/lnav_log.hh"
52 #include "base/intern_string.hh"
53 #include "base/file_range.hh"
54 
55 #include "yajl/api/yajl_parse.h"
56 #include "yajl/api/yajl_gen.h"
57 
58 inline
yajl_gen_pstring(yajl_gen hand,const char * str,size_t len)59 yajl_gen_status yajl_gen_pstring(yajl_gen hand, const char *str, size_t len)
60 {
61     if (len == (size_t)-1) {
62         len = strlen(str);
63     }
64     return yajl_gen_string(hand, (const unsigned char *)str, len);
65 }
66 
67 inline
yajl_gen_string(yajl_gen hand,const std::string & str)68 yajl_gen_status yajl_gen_string(yajl_gen hand, const std::string &str)
69 {
70     return yajl_gen_string(hand,
71                            (const unsigned char *)str.c_str(),
72                            str.length());
73 }
74 
75 class yajlpp_gen_context;
76 class yajlpp_parse_context;
77 
78 struct yajlpp_provider_context {
79     pcre_extractor ypc_extractor;
80     int ypc_index;
81 
82     template<typename T>
get_substr_iyajlpp_provider_context83     intern_string_t get_substr_i(T name) const {
84         pcre_context::iterator cap = this->ypc_extractor.pe_context[name];
85         char path[cap->length() + 1];
86         size_t len = json_ptr::decode(path, this->ypc_extractor.pe_input.get_substr_start(cap), cap->length());
87 
88         return intern_string::lookup(path, len);
89     };
90 
91     template<typename T>
get_substryajlpp_provider_context92     std::string get_substr(T name) const {
93         pcre_context::iterator cap = this->ypc_extractor.pe_context[name];
94         char path[cap->length() + 1];
95         size_t len = json_ptr::decode(path, this->ypc_extractor.pe_input.get_substr_start(cap), cap->length());
96 
97         return std::string(path, len);
98     };
99 };
100 
101 class yajlpp_error : public std::exception {
102 public:
yajlpp_error(yajl_handle handle,const char * json,size_t len)103     yajlpp_error(yajl_handle handle, const char *json, size_t len) {
104         auto_mem<unsigned char> yajl_msg;
105 
106         yajl_msg = yajl_get_error(handle, 1, (const unsigned char *) json, len);
107         this->msg = (const char *) yajl_msg.in();
108     }
109 
110     ~yajlpp_error() override = default;
111 
what() const112     const char *what() const noexcept override {
113         return this->msg.c_str();
114     }
115 
116 private:
117     std::string msg;
118 };
119 
120 struct json_path_container;
121 
122 struct json_path_handler_base {
123     struct enum_value_t {
124         template<typename T>
enum_value_tjson_path_handler_base::enum_value_t125         enum_value_t(const char *name, T value)
126             : first(name),
127               second((unsigned int) value) {
128         }
129 
130         const char *first;
131         unsigned int second;
132     };
133 
134     static const enum_value_t ENUM_TERMINATOR;
135 
136     json_path_handler_base(const std::string &property);
137 
138     explicit json_path_handler_base(const pcrepp& property);
139 
140     json_path_handler_base(std::string property,
141                            const pcrepp& property_re);
142 
is_arrayjson_path_handler_base143     bool is_array() const {
144         return this->jph_is_array;
145     }
146 
to_enum_valuejson_path_handler_base147     nonstd::optional<int> to_enum_value(const string_fragment &sf) const {
148         for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
149             const enum_value_t &ev = this->jph_enum_values[lpc];
150 
151             if (sf == ev.first) {
152                 return ev.second;
153             }
154         }
155 
156         return nonstd::nullopt;
157     };
158 
159     yajl_gen_status gen(yajlpp_gen_context &ygc, yajl_gen handle) const;
160     yajl_gen_status gen_schema(yajlpp_gen_context &ygc) const;
161     yajl_gen_status gen_schema_type(yajlpp_gen_context &ygc) const;
162     void walk(
163         const std::function<void(const json_path_handler_base &,
164                                  const std::string &,
165                                  void *)> &cb,
166         void *root = nullptr,
167         const std::string &base = "/") const;
168 
169     enum class schema_type_t : std::uint32_t {
170         ANY,
171         BOOLEAN,
172         INTEGER,
173         NUMBER,
174         STRING,
175         ARRAY,
176         OBJECT,
177     };
178 
get_typesjson_path_handler_base179     std::vector<schema_type_t> get_types() const {
180         std::vector<schema_type_t> retval;
181 
182         if (this->jph_callbacks.yajl_boolean) {
183             retval.push_back(schema_type_t::BOOLEAN);
184         }
185         if (this->jph_callbacks.yajl_integer) {
186             retval.push_back(schema_type_t::INTEGER);
187         }
188         if (this->jph_callbacks.yajl_double ||
189             this->jph_callbacks.yajl_number) {
190             retval.push_back(schema_type_t::NUMBER);
191         }
192         if (this->jph_callbacks.yajl_string) {
193             retval.push_back(schema_type_t::STRING);
194         }
195         if (this->jph_children) {
196             retval.push_back(schema_type_t::OBJECT);
197         }
198         if (retval.empty()) {
199             retval.push_back(schema_type_t::ANY);
200         }
201         return retval;
202     };
203 
204     std::string    jph_property;
205     pcrepp         jph_regex;
206     yajl_callbacks jph_callbacks{};
207     std::function<yajl_gen_status (yajlpp_gen_context &, const json_path_handler_base &, yajl_gen)> jph_gen_callback;
208     std::function<void (yajlpp_parse_context &ypc, const json_path_handler_base &jph)> jph_validator;
209     std::function<void *(void *root, nonstd::optional<std::string> name)> jph_field_getter;
210     std::function<void *(const yajlpp_provider_context &pe, void *root)> jph_obj_provider;
211     std::function<void (void *root, std::vector<std::string> &paths_out)> jph_path_provider;
212     const char *   jph_synopsis{""};
213     const char *   jph_description{""};
214     const json_path_container *jph_children{nullptr};
215     std::shared_ptr<pcrepp> jph_pattern;
216     const char * jph_pattern_re{nullptr};
217     std::function<void(const string_fragment &)> jph_string_validator;
218     size_t         jph_min_length{0};
219     size_t         jph_max_length{INT_MAX};
220     const enum_value_t  *jph_enum_values{nullptr};
221     long long      jph_min_value{LLONG_MIN};
222     bool           jph_optional_wrapper{false};
223     bool           jph_is_array;
224     bool           jph_is_pattern_property{false};
225     std::vector<std::string> jph_examples;
226 
227     std::function<int(yajlpp_parse_context *, int)> jph_bool_cb;
228     std::function<int(yajlpp_parse_context *, long long)> jph_integer_cb;
229     std::function<int(yajlpp_parse_context *, const unsigned char *str, size_t len)> jph_str_cb;
230 };
231 
232 struct json_path_handler;
233 
234 class yajlpp_parse_context {
235 public:
236     using error_reporter_t = std::function<
237         void(const yajlpp_parse_context &ypc,
238              lnav_log_level_t level,
239              const char *msg)>;
240 
241     yajlpp_parse_context(std::string source,
242                          const struct json_path_container *handlers = nullptr);
243 
244     const char *get_path_fragment(int offset, char *frag_in, size_t &len_out) const;
245 
get_path_fragment_i(int offset) const246     intern_string_t get_path_fragment_i(int offset) const {
247         char fragbuf[this->ypc_path.size()];
248         const char *frag;
249         size_t len;
250 
251         frag = this->get_path_fragment(offset, fragbuf, len);
252         return intern_string::lookup(frag, len);
253     };
254 
get_path_fragment(int offset) const255     std::string get_path_fragment(int offset) const {
256         char fragbuf[this->ypc_path.size()];
257         const char *frag;
258         size_t len;
259 
260         frag = this->get_path_fragment(offset, fragbuf, len);
261         return std::string(frag, len);
262     };
263 
264     const intern_string_t get_path() const;
265 
266     const intern_string_t get_full_path() const;
267 
is_level(size_t level) const268     bool is_level(size_t level) const {
269         return this->ypc_path_index_stack.size() == level;
270     };
271 
272     yajlpp_parse_context &set_path(const std::string &path);
273 
274     void reset(const struct json_path_container *handlers);
275 
276     void set_static_handler(struct json_path_handler_base &jph);
277 
278     template<typename T>
with_obj(T & obj)279     yajlpp_parse_context &with_obj(T &obj) {
280         this->ypc_obj_stack.push(&obj);
281         return *this;
282     };
283 
with_handle(yajl_handle handle)284     yajlpp_parse_context &with_handle(yajl_handle handle) {
285         this->ypc_handle = handle;
286         return *this;
287     };
288 
with_error_reporter(error_reporter_t err)289     yajlpp_parse_context &with_error_reporter(error_reporter_t err) {
290         this->ypc_error_reporter = err;
291         return *this;
292     }
293 
with_ignore_unused(bool ignore)294     yajlpp_parse_context &with_ignore_unused(bool ignore) {
295         this->ypc_ignore_unused = ignore;
296         return *this;
297     }
298 
299     yajl_status parse(const unsigned char *jsonText, size_t jsonTextLen);
300 
parse(const string_fragment & sf)301     yajl_status parse(const string_fragment& sf) {
302         return this->parse((const unsigned char *) sf.data(), sf.length());
303     }
304 
305     int get_line_number() const;
306 
307     yajl_status complete_parse();
308 
report_error(lnav_log_level_t level,const char * format,...) const309     void report_error(lnav_log_level_t level, const char *format, ...) const {
310         va_list args;
311 
312         va_start(args, format);
313         if (this->ypc_error_reporter) {
314             char buffer[1024];
315 
316             vsnprintf(buffer, sizeof(buffer), format, args);
317 
318             this->ypc_error_reporter(*this, level, buffer);
319         }
320         va_end(args);
321     }
322 
323     template<typename T>
get_lvalue(std::map<std::string,std::vector<T>> & value)324     std::vector<T> &get_lvalue(std::map<std::string, std::vector<T>> &value) {
325         return value[this->get_path_fragment(-2)];
326     };
327 
328     template<typename T>
get_lvalue(std::map<std::string,T> & value)329     T &get_lvalue(std::map<std::string, T> &value) {
330         return value[this->get_path_fragment(-1)];
331     };
332 
333     template<typename T>
get_lvalue(T & lvalue)334     T &get_lvalue(T &lvalue) {
335         return lvalue;
336     };
337 
338     template<typename T>
get_rvalue(std::map<std::string,std::vector<T>> & value)339     T &get_rvalue(std::map<std::string, std::vector<T>> &value) {
340         return value[this->get_path_fragment(-2)].back();
341     };
342 
343     template<typename T>
get_rvalue(std::map<std::string,T> & value)344     T &get_rvalue(std::map<std::string, T> &value) {
345         return value[this->get_path_fragment(-1)];
346     };
347 
348     template<typename T>
get_rvalue(std::vector<T> & value)349     T &get_rvalue(std::vector<T> &value) {
350         return value.back();
351     };
352 
353     template<typename T>
get_rvalue(T & lvalue)354     T &get_rvalue(T &lvalue) {
355         return lvalue;
356     };
357 
358     template<typename T, typename MEM_T, MEM_T T::*MEM>
get_obj_member()359     auto &get_obj_member() {
360         auto obj = (T *) this->ypc_obj_stack.top();
361 
362         return obj->*MEM;
363     };
364 
365     const std::string ypc_source;
366     int ypc_line_number{1};
367     const struct json_path_container *ypc_handlers;
368     std::stack<void *> ypc_obj_stack;
369     void *                  ypc_userdata{nullptr};
370     yajl_handle             ypc_handle{nullptr};
371     const unsigned char *   ypc_json_text{nullptr};
372     yajl_callbacks          ypc_callbacks;
373     yajl_callbacks          ypc_alt_callbacks;
374     std::vector<char>       ypc_path;
375     std::vector<size_t>     ypc_path_index_stack;
376     std::vector<int>        ypc_array_index;
377     std::vector<const json_path_handler_base *> ypc_handler_stack;
378     pcre_context_static<30> ypc_pcre_context;
379     bool                    ypc_ignore_unused{false};
380     const struct json_path_container *ypc_sibling_handlers{nullptr};
381     const struct json_path_handler_base *ypc_current_handler{nullptr};
382     std::set<std::string>   ypc_active_paths;
383     error_reporter_t ypc_error_reporter{nullptr};
384     std::map<intern_string_t, source_location> *ypc_locations{nullptr};
385 
386     void update_callbacks(const json_path_container *handlers = nullptr,
387                           int child_start = 0);
388 private:
389     static const yajl_callbacks DEFAULT_CALLBACKS;
390 
index_for_provider() const391     int index_for_provider() const {
392         return this->ypc_array_index.empty() ? -1 : this->ypc_array_index.back();
393     };
394 
395     static int map_start(void *ctx);
396     static int map_key(void *ctx, const unsigned char *key, size_t len);
397     static int map_end(void *ctx);
398     static int array_start(void *ctx);
399     static int array_end(void *ctx);
400     static int handle_unused(void *ctx);
401 };
402 
403 class yajlpp_generator {
404 public:
yajlpp_generator(yajl_gen handle)405     yajlpp_generator(yajl_gen handle) : yg_handle(handle) { };
406 
operator ()(const std::string & str)407     yajl_gen_status operator()(const std::string &str)
408     {
409         return yajl_gen_string(this->yg_handle, str);
410     };
411 
operator ()(const char * str)412     yajl_gen_status operator()(const char *str)
413     {
414         return yajl_gen_string(this->yg_handle, (const unsigned char *)str, strlen(str));
415     };
416 
operator ()(const char * str,size_t len)417     yajl_gen_status operator()(const char *str, size_t len)
418     {
419         return yajl_gen_string(this->yg_handle, (const unsigned char *)str, len);
420     };
421 
operator ()(const intern_string_t & str)422     yajl_gen_status operator()(const intern_string_t &str)
423     {
424         return yajl_gen_string(this->yg_handle, (const unsigned char *)str.get(), str.size());
425     };
426 
operator ()(const string_fragment & str)427     yajl_gen_status operator()(const string_fragment &str)
428     {
429         return yajl_gen_string(this->yg_handle, (const unsigned char *)str.data(), str.length());
430     };
431 
operator ()(bool value)432     yajl_gen_status operator()(bool value)
433     {
434         return yajl_gen_bool(this->yg_handle, value);
435     };
436 
operator ()(double value)437     yajl_gen_status operator()(double value)
438     {
439         return yajl_gen_double(this->yg_handle, value);
440     };
441 
442     template<typename T>
operator ()(T value,typename std::enable_if<std::is_integral<T>::value &&!std::is_same<T,bool>::value>::type * dummy=0)443     yajl_gen_status operator()(T value,
444                                typename std::enable_if<std::is_integral<T>::value &&
445                                                        !std::is_same<T, bool>::value>::type* dummy = 0)
446     {
447         return yajl_gen_integer(this->yg_handle, value);
448     };
449 
450     template<typename T>
operator ()(const T & container,typename std::enable_if<!std::is_integral<T>::value>::type * dummy=0)451     yajl_gen_status operator()(const T &container,
452                                typename std::enable_if<!std::is_integral<T>::value>::type* dummy = 0)
453     {
454         yajl_gen_array_open(this->yg_handle);
455         for (const auto& elem : container) {
456             yajl_gen_status rc = (*this)(elem);
457 
458             if (rc != yajl_gen_status_ok) {
459                 return rc;
460             }
461         }
462 
463         yajl_gen_array_close(this->yg_handle);
464 
465         return yajl_gen_status_ok;
466     };
467 
operator ()()468     yajl_gen_status operator()()
469     {
470         return yajl_gen_null(this->yg_handle);
471     };
472 private:
473     yajl_gen yg_handle;
474 };
475 
476 class yajlpp_container_base {
477 public:
yajlpp_container_base(yajl_gen handle)478     yajlpp_container_base(yajl_gen handle)
479         : gen(handle), ycb_handle(handle) {};
480 
481     yajlpp_generator gen;
482 
483 protected:
484     yajl_gen ycb_handle;
485 };
486 
487 class yajlpp_map : public yajlpp_container_base {
488 public:
yajlpp_map(yajl_gen handle)489     yajlpp_map(yajl_gen handle) : yajlpp_container_base(handle)
490     {
491         yajl_gen_map_open(handle);
492     };
493 
~yajlpp_map()494     ~yajlpp_map() {
495         yajl_gen_map_close(this->ycb_handle);
496     };
497 };
498 
499 class yajlpp_array : public yajlpp_container_base {
500 public:
yajlpp_array(yajl_gen handle)501     yajlpp_array(yajl_gen handle) : yajlpp_container_base(handle)
502     {
503         yajl_gen_array_open(handle);
504     };
505 
~yajlpp_array()506     ~yajlpp_array() { yajl_gen_array_close(this->ycb_handle); };
507 };
508 
509 class yajlpp_gen_context {
510 public:
yajlpp_gen_context(yajl_gen handle,const struct json_path_container & handlers)511     yajlpp_gen_context(yajl_gen handle, const struct json_path_container &handlers)
512             : ygc_handle(handle),
513               ygc_depth(0),
514               ygc_handlers(&handlers)
515     {
516     };
517 
518     template<typename T>
with_default_obj(T & obj)519     yajlpp_gen_context &with_default_obj(T &obj) {
520         this->ygc_default_stack.push(&obj);
521         return *this;
522     };
523 
524     template<typename T>
with_obj(T & obj)525     yajlpp_gen_context &with_obj(T &obj) {
526         this->ygc_obj_stack.push(&obj);
527         return *this;
528     };
529 
530     yajlpp_gen_context &with_context(yajlpp_parse_context &ypc);
531 
532     void gen();
533 
534     void gen_schema(const json_path_container *handlers = nullptr);
535 
536     yajl_gen ygc_handle;
537     int ygc_depth;
538     std::stack<void *> ygc_default_stack;
539     std::stack<void *> ygc_obj_stack;
540     std::vector<std::string> ygc_path;
541     const json_path_container *ygc_handlers;
542     std::map<std::string, const json_path_container *> ygc_schema_definitions;
543 };
544 
545 class yajlpp_gen {
546 public:
yajlpp_gen()547     yajlpp_gen() : yg_handle(yajl_gen_free) {
548         this->yg_handle = yajl_gen_alloc(nullptr);
549     };
550 
get_handle() const551     yajl_gen get_handle() const {
552         return this->yg_handle.in();
553     };
554 
operator yajl_gen()555     operator yajl_gen () {
556         return this->yg_handle.in();
557     };
558 
to_string_fragment()559     string_fragment to_string_fragment() {
560         const unsigned char *buf;
561         size_t len;
562 
563         yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
564 
565         return string_fragment((const char *) buf, 0, len);
566     };
567 
568 private:
569     auto_mem<yajl_gen_t> yg_handle;
570 };
571 
572 struct json_string {
json_stringjson_string573     explicit json_string(yajl_gen_t *gen) {
574         const unsigned char *buf;
575 
576         yajl_gen_get_buf(gen, &buf, &this->js_len);
577 
578         this->js_content = (const unsigned char *) malloc(this->js_len);
579         memcpy((void *) this->js_content.in(), buf, this->js_len);
580     };
581 
582     auto_mem<const unsigned char> js_content;
583     size_t js_len{0};
584 };
585 
586 void dump_schema_to(const json_path_container &jpc, const char *internals_dir, const char *name);
587 
588 #endif
589