diff --git a/include/ircd/json.h b/include/ircd/json.h index 9ab7ea0b1..2f4f0e6c5 100644 --- a/include/ircd/json.h +++ b/include/ircd/json.h @@ -22,6 +22,40 @@ #pragma once #define HAVE_IRCD_JSON_H +// +// The IRCd JSON subsystem is meant to be a fast, safe, and extremely +// lightweight interface. We have taken a somewhat non-traditional approach +// and it's important for the developer to understand a few things. +// +// Most JSON interfaces are functions to convert some JSON input to and from +// text into native-machine state like JSON.parse() for JS, boost::ptree, etc. +// For a parsing operation, they make a pass recursing over the entire text, +// allocating native structures, copying data into them, indexing their keys, +// and perhaps performing native-type conversions and checks to present the +// user with a final tree of machine-state usable in their language. The +// original input is then discarded. +// +// Instead, we are interested in having the ability to *compute directly over +// JSON text* itself, and perform the allocating, indexing, copying and +// converting entirely at the time and place of our discretion -- if ever. +// +// The core of this system is a robust and efficient abstract formal grammar +// built with boost::spirit. The formal grammar provides a *proof of robust- +// ness*: security vulnerabilities are more easily spotted by vetting this +// grammar rather than laboriously tracing the program flow of an informal +// handwritten parser. +// +// Next we have taught boost::spirit how to parse into std::string_view rather +// than std::string. Parsing is now a composition of pointers into the original +// string of JSON. No dynamic allocation ever takes place. No copying of data +// ever takes place. IRCd can service an entire request from the original +// network input with absolutely minimal requisite cost. +// +// The output side is also ambitious but probably a little more friendly to +// the developer. We leverage boost::spirit here also providing *formally +// proven* output safety. In other words, the grammar prevents exploits like +// injecting and terminating JSON as it composes the output. +// namespace ircd { namespace json { @@ -47,6 +81,9 @@ enum type enum type type(const string_view &); enum type type(const string_view &, std::nothrow_t); +using path = std::initializer_list; +std::ostream &operator<<(std::ostream &, const path &); + } // namespace json } // namespace ircd @@ -54,4 +91,26 @@ enum type type(const string_view &, std::nothrow_t); #include "json/object.h" #include "json/value.h" #include "json/index.h" -// #include "json/parse.h" +#include "json/parse.h" + +namespace ircd { + +using json::operator<<; + +} // namespace ircd + +inline std::ostream & +ircd::json::operator<<(std::ostream &s, const path &p) +{ + auto it(std::begin(p)); + if(it != std::end(p)) + { + s << *it; + ++it; + } + + for(; it != std::end(p); ++it) + s << '.' << *it; + + return s; +} diff --git a/include/ircd/json/parse.h b/include/ircd/json/parse.h index 5528ff4d0..3bad555ea 100644 --- a/include/ircd/json/parse.h +++ b/include/ircd/json/parse.h @@ -52,6 +52,7 @@ struct parse return std::tuple_size(); } + parse() = default; template parse(R &r, const json::object &obj); }; diff --git a/include/ircd/json/value.h b/include/ircd/json/value.h index 7046eedc8..c19205ee7 100644 --- a/include/ircd/json/value.h +++ b/include/ircd/json/value.h @@ -25,6 +25,12 @@ namespace ircd { namespace json { +// The ircd::json::value is used if we have to keep state in machine-form +// rather than directly computing JSON strings. This class ends up being useful +// for recursive initializer_lists to compose JSON from machine values. It +// is lightweight, consuming the space of two pointers which is the same size +// as a string_view. +// struct value { union // xxx std::variant