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