summaryrefslogtreecommitdiff
path: root/modules/codec-json/c++/json.tmpl.h
diff options
context:
space:
mode:
Diffstat (limited to 'modules/codec-json/c++/json.tmpl.h')
-rw-r--r--modules/codec-json/c++/json.tmpl.h734
1 files changed, 734 insertions, 0 deletions
diff --git a/modules/codec-json/c++/json.tmpl.h b/modules/codec-json/c++/json.tmpl.h
new file mode 100644
index 0000000..84f0058
--- /dev/null
+++ b/modules/codec-json/c++/json.tmpl.h
@@ -0,0 +1,734 @@
+#pragma once
+
+#include <charconv>
+#include <sstream>
+
+#include <iostream>
+
+namespace saw {
+namespace impl {
+template<typename Schema, typename RootSchema, typename FromEncode>
+class json_encode {
+ static_assert(always_false<Schema, RootSchema, FromEncode>, "This schema type is not being handled by the json encoding.");
+};
+
+template<typename T, size_t N, typename RootSchema, typename FromEncode>
+struct json_encode<schema::Primitive<T,N>, RootSchema, FromEncode> {
+ static error_or<void> encode(const data<schema::Primitive<T,N>, FromEncode>& from, buffer& to) {
+ auto val = from.get();
+ std::array<uint8_t, 256> data;
+ auto tc_result = std::to_chars(reinterpret_cast<char*>(data.data()), reinterpret_cast<char*>(data.data())+data.size(), val);
+
+ if(tc_result.ec != std::errc{}){
+ return make_error<err::critical>();
+ }
+
+ size_t bytes_written = 0;
+ for(char* ptr = reinterpret_cast<char*>(data.data()); ptr != tc_result.ptr; ++ptr){
+ ++bytes_written;
+ }
+
+ auto& buff = to;
+ error err = buff.write_require_length(bytes_written);
+ if(!err.template is_type<err::no_error>()){
+ return std::move(err);
+ }
+
+ for(char* ptr = reinterpret_cast<char*>(data.data()); ptr != tc_result.ptr; ++ptr){
+ uint8_t* un_ptr = reinterpret_cast<uint8_t*>(ptr);
+ buff.push(un_ptr[0]);
+ }
+
+ return void_t{};
+ }
+};
+
+template<typename RootSchema, typename FromEncode>
+struct json_encode<schema::String, RootSchema, FromEncode> {
+ static error_or<void> encode(const data<schema::String, FromEncode>& from, buffer& to) {
+ {
+ auto err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ for(std::size_t i = 0; i < from.size(); ++i){
+ auto err = to.push(from.get_at(i));
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ {
+ auto err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+
+ return void_t{};
+ }
+};
+
+template<typename... T, typename RootSchema, typename FromEncode>
+struct json_encode<schema::Tuple<T...>, RootSchema, FromEncode> {
+ template<size_t i>
+ static error_or<void> encode_element(const data<schema::Tuple<T...>, FromEncode>& from, buffer& to){
+ auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<i>(), to);
+
+ if(eov.is_error()){
+ return eov;
+ }
+
+ if constexpr ( (i+1) < sizeof...(T)){
+ {
+ auto eov_ele = to.push(',');
+ if(!eov_ele.template is_type<err::no_error>()){
+ return eov_ele;
+ }
+ }
+ {
+ auto eov_ele = encode_element<i+1>(from, to);
+ if(eov_ele.is_error()){
+ return eov_ele;
+ }
+ }
+
+
+ }
+ return void_t{};
+ }
+
+ static error_or<void> encode(const data<schema::Tuple<T...>, FromEncode>& from, buffer& to) {
+ {
+ auto err = to.push('[');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ if constexpr ( sizeof...(T) > 0 ){
+ auto eov = encode_element<0>(from, to);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ {
+ auto err = to.push(']');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+
+ return void_t{};
+ }
+};
+
+template<typename T, size_t D, typename RootSchema, typename FromEncode>
+struct json_encode<schema::Array<T,D>, RootSchema, FromEncode> {
+ template<size_t Level>
+ static error_or<void> encode_level(const data<schema::Array<T,D>, FromEncode>& from, buffer& to, std::array<std::size_t, D>& index){
+ if constexpr (Level == D){
+ auto eov = json_encode<T, RootSchema, FromEncode>::encode(from.at(index), to);
+ if(eov.is_error()){
+ return eov;
+ }
+ } else {
+ {
+ auto err = to.push('[');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ for(std::size_t i = 0; i < from.get_dim_size(Level); ++i){
+ if( i > 0 ){
+ auto err = to.push(',');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ {
+ index[Level] = i;
+ auto eov = encode_level<Level+1>(from, to, index);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ }
+ {
+ auto err = to.push(']');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ }
+ return void_t{};
+ }
+
+ static error_or<void> encode(const data<schema::Array<T,D>, FromEncode>& from, buffer& to) {
+ std::array<std::size_t, D> index;
+ return encode_level<0>(from, to, index);
+ }
+};
+
+template<typename... T, string_literal... Key, typename RootSchema, typename FromEncode>
+struct json_encode<schema::Struct<schema::Member<T,Key>...>, RootSchema, FromEncode> {
+
+ template<size_t i>
+ static error_or<void> encode_element(const data<schema::Struct<schema::Member<T,Key>...>, FromEncode>& from, buffer& to){
+ // Encode the name
+ {
+ std::string_view view = parameter_key_pack_type<i, Key...>::literal.view();
+ error err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ err = to.push(*reinterpret_cast<const uint8_t *>(view.data()), view.size());
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ // Add the separator
+ {
+ auto eov_ele = to.push(':');
+ if(!eov_ele.template is_type<err::no_error>()){
+ return eov_ele;
+ }
+ }
+
+ // Encode the value
+ auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<parameter_key_pack_type<i, Key...>::literal>(), to);
+
+ // Go to the next element
+ if constexpr ( (i+1) < sizeof...(T)){
+ {
+ auto eov_ele = to.push(',');
+ if(!eov_ele.template is_type<err::no_error>()){
+ return eov_ele;
+ }
+ }
+ {
+ auto eov_ele = encode_element<i+1>(from, to);
+ if(eov_ele.is_error()){
+ return eov_ele;
+ }
+ }
+
+
+ }
+
+ return void_t{};
+ }
+ static error_or<void> encode(const data<schema::Struct<schema::Member<T,Key>...>, FromEncode>& from, buffer& to) {
+ {
+ auto err = to.push('{');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ if constexpr ( sizeof...(T) > 0 ){
+ auto eov = encode_element<0>(from, to);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ {
+ auto err = to.push('}');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+
+ return void_t{};
+ }
+};
+
+template<typename... T, string_literal... Key, typename RootSchema, typename FromEncode>
+struct json_encode<schema::Union<schema::Member<T,Key>...>, RootSchema, FromEncode> {
+
+ template<size_t i>
+ static error_or<void> encode_element(const data<schema::Union<schema::Member<T,Key>...>, FromEncode>& from, buffer& to){
+ if(from.template holds_alternative<parameter_key_pack_type<i, Key...>::literal>()){
+ // Encode the name
+ {
+ std::string_view view = parameter_key_pack_type<i, Key...>::literal.view();
+ error err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ err = to.push(*reinterpret_cast<const uint8_t *>(view.data()), view.size());
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ err = to.push('"');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ // Add the separator
+ {
+ auto eov_ele = to.push(':');
+ if(!eov_ele.template is_type<err::no_error>()){
+ return eov_ele;
+ }
+ }
+
+ // Encode the value
+ auto eov = json_encode<typename parameter_pack_type<i, T...>::type, RootSchema, FromEncode>::encode(from.template get<parameter_key_pack_type<i, Key...>::literal>(), to);
+ }
+ // Go to the next element
+ if constexpr ( (i+1) < sizeof...(T)){
+ {
+ auto eov_ele = encode_element<i+1>(from, to);
+ if(eov_ele.is_error()){
+ return eov_ele;
+ }
+ }
+
+
+ }
+
+ return void_t{};
+ }
+ static error_or<void> encode(const data<schema::Union<schema::Member<T,Key>...>, FromEncode>& from, buffer& to) {
+ {
+ auto err = to.push('{');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+ if constexpr ( sizeof...(T) > 0 ){
+ auto eov = encode_element<0>(from, to);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ {
+ auto err = to.push('}');
+ if(!err.template is_type<err::no_error>()){
+ return err;
+ }
+ }
+
+ return void_t{};
+ }
+};
+
+struct json_helper {
+ static bool is_whitespace(int8_t ch) {
+ return ch == '\t' || ch == ' ' || ch == '\r' || ch == '\n';
+ }
+
+ static void skip_whitespace(buffer_view& buff) {
+ while(buff.read_composite_length() > 0 && json_helper::is_whitespace(buff.read())) {
+ buff.read_advance(1);
+ }
+ }
+};
+
+template<typename Schema, typename RootSchema, typename ToDecode>
+struct json_decode;
+
+template<typename T, size_t N, typename RootSchema, typename ToDecode>
+struct json_decode<schema::Primitive<T,N>, RootSchema, ToDecode> {
+ static error_or<void> decode(buffer_view& buff, data<schema::Primitive<T,N>, ToDecode>& to) {
+ assert(
+ ( buff.read() >= '0' && buff.read() <= '9')
+ || ( buff.read() == '+' || buff.read() == '-')
+ );
+
+ std::size_t offset = 0;
+
+ if (buff.read() == '-'){
+ ++offset;
+ } else if (buff.read() == '+'){
+ return make_error<err::not_supported>();
+ }
+ if (offset >= buff.read_composite_length()) {
+ return make_error<err::buffer_exhausted>();
+ }
+ if (buff.read(offset) >= '1' && buff.read(offset) <= '9') {
+ ++offset;
+
+ if(offset >= buff.read_composite_length()) {
+ return make_error<err::buffer_exhausted>();
+ }
+
+ while(1){
+ if (buff.read(offset) >= '0' && buff.read(offset) <= '9') {
+ ++offset;
+
+ if(offset >= buff.read_composite_length()) {
+ break;
+ }
+ continue;
+ }
+ break;
+ }
+ } else if (buff.read(offset) == '0' ) {
+ ++offset;
+ } else {
+ return make_error<err::buffer_exhausted>();
+ }
+
+ {
+ std::string_view num_view{reinterpret_cast<char*>(&buff.read()), offset};
+ typename native_data_type<schema::Primitive<T,N>>::type result;
+ auto fc_result = std::from_chars(num_view.data(), num_view.data() + num_view.size(), result);
+ if(fc_result.ec != std::errc{}){
+ return make_error<err::invalid_state>();
+ }
+
+ to.set(result);
+ }
+ buff.read_advance(offset);
+
+ return void_t{};
+ }
+};
+
+template<typename RootSchema, typename ToDecode>
+struct json_decode<schema::String, RootSchema, ToDecode> {
+ static error_or<void> decode(buffer_view& buff, data<schema::String, ToDecode>& to){
+ assert(buff.read() == '"');
+ buff.read_advance(1);
+
+ std::stringstream iss;
+ bool string_done = false;
+ while(!string_done){
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ switch(buff.read()){
+ case '\\':{
+ buff.read_advance(1);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ switch(buff.read()){
+ case '\\':
+ case '/':
+ case '"':
+ iss<< buff.read();
+ break;
+ case 'b':
+ iss<<'\b';
+ break;
+ case 'f':
+ iss<<'\f';
+ break;
+ case 'n':
+ iss<<'\n';
+ break;
+ case 'r':
+ iss<<'\r';
+ break;
+ case 't':
+ iss<<'\t';
+ break;
+ case 'u': {
+ buff.read_advance(1);
+ if(buff.read_composite_length() < 4){
+ return make_error<err::buffer_exhausted>();
+ }
+ iss<<'?';
+ iss<<'?';
+ iss<<'?';
+ iss<<'?';
+
+ buff.read_advance(3);
+ } break;
+ }
+ } break;
+ case '"':
+ string_done = true;
+ break;
+ default:{
+ // Avoids Control sequences
+ if(buff.read() >= ' ' && buff.read() <= '~'){
+ iss<<buff.read();
+ }
+ // Avoid Unicode
+ else if (buff.read() < 0) {
+ do {
+ buff.read_advance(1);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ } while( buff.read() < 0 );
+ iss<<'?';
+ }
+ break;
+ }
+ }
+ buff.read_advance(1);
+ }
+
+ std::string raw = iss.str();
+ to.set(std::move(raw));
+
+ return void_t{};
+ }
+};
+
+template<typename... T, string_literal... Lits, typename RootSchema, typename ToDecode>
+struct json_decode<schema::Struct<schema::Member<T,Lits>...>, RootSchema, ToDecode> {
+ template<std::size_t i>
+ static error_or<void> decode_field_search(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to, std::array<bool, sizeof...(T)>& fields, const data<schema::String,ToDecode>& search_name){
+ if constexpr ( i < sizeof...(T)){
+ using Type = typename parameter_pack_type<i, T...>::type;
+ constexpr static string_literal Literal = parameter_key_pack_type<i, Lits...>::literal;
+ if(search_name == Literal.view()){
+ if(fields[i]){
+ return make_error<err::invalid_state>();
+ }
+ fields[i] = true;
+ auto eov = json_decode<Type, RootSchema, ToDecode>::decode(buff, to.template get<Literal>());
+ if(eov.is_error()){
+ return eov;
+ }
+ }else {
+ decode_field_search<i+1>(buff, to, fields, search_name);
+ }
+ }else {
+ return make_error<err::invalid_state>();
+ }
+ return void_t{};
+ }
+
+ static error_or<void> decode_fields(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to, std::array<bool, sizeof...(T)>& fields){
+ for(;;){
+ data<schema::String, ToDecode> name;
+ auto eov = json_decode<schema::String, RootSchema, ToDecode>::decode(buff, name);
+ if(eov.is_error()){
+ return eov;
+ }
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ if(buff.read() != ':'){
+ return make_error<err::invalid_state>();
+ }
+ buff.read_advance(1);
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ {
+ auto eov = decode_field_search<0>(buff, to, fields, name);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ if(buff.read() == ','){
+ buff.read_advance(1);
+ }else if(buff.read() == '}'){
+ // If not all fields are set, the dataset is incomplete
+ for(auto& iter : fields){
+ if(!iter){
+ return make_error<err::invalid_state>();
+ }
+ }
+ buff.read_advance(1);
+ return void_t{};
+ }else{
+ return make_error<err::invalid_state>();
+ }
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ }
+ return void_t{};
+ }
+
+ static error_or<void> decode(buffer_view& buff, data<schema::Struct<schema::Member<T,Lits>...>, ToDecode>& to){
+ std::array<bool, sizeof...(T)> found_fields;
+ std::fill(found_fields.begin(), found_fields.end(), false);
+
+ assert(buff.read() == '{');
+ buff.read_advance(1);
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ // Check if there are no elements present in the JSON Struct
+ if(buff.read() == '}'){
+ if(sizeof...(T) > 0){
+ return make_error<err::invalid_state>();
+ }
+ buff.read_advance(1);
+ return void_t{};
+ }
+
+ auto eov = decode_fields(buff, to, found_fields);
+ if(eov.is_error()){
+ return eov;
+ }
+
+ return void_t{};
+ }
+};
+
+template<typename... T, typename RootSchema, typename ToDecode>
+struct json_decode<schema::Tuple<T...>, RootSchema, ToDecode> {
+ template<std::size_t i>
+ static error_or<void> decode_element(buffer_view& buff, data<schema::Tuple<T...>, ToDecode>& to){
+ if constexpr (i < sizeof...(T)){
+ if constexpr ( i > 0 ){
+ if(buff.read() != ','){
+ return make_error<err::invalid_state>();
+ }
+ buff.read_advance(1);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+ }
+ using Type = typename parameter_pack_type<i, T...>::type;
+
+ auto eov = json_decode<Type, RootSchema, ToDecode>::decode(buff, to.template get<i>());
+ if(eov.is_error()){
+ return eov;
+ }
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ eov = decode_element<i+1>(buff, to);
+ if(eov.is_error()){
+ return eov;
+ }
+ }else{
+ if(buff.read() != ']'){
+ return make_error<err::invalid_state>();
+ }
+ buff.read_advance(1);
+ }
+ return void_t{};
+ }
+
+ static error_or<void> decode(buffer_view& buff, data<schema::Tuple<T...>, ToDecode>& to){
+ assert(buff.read() == '[');
+ buff.read_advance(1);
+
+ json_helper::skip_whitespace(buff);
+ if(buff.read_composite_length() == 0){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ auto eov = decode_element<0>(buff, to);
+ if(eov.is_error()){
+ return eov;
+ }
+
+ return void_t{};
+ }
+};
+
+// The whole std::vector approach is hacky af. ToDo change it maybe?
+template<typename T, size_t D, typename RootSchema, typename ToDecode>
+struct json_decode<schema::Array<T,D>, RootSchema, ToDecode> {
+ template<size_t Level>
+ static error_or<void> decode_flat_level(buffer_view& buff, std::vector<data<T, encode::Native>>& to, std::array<std::size_t, D>& index, std::array<std::size_t, D>& dims, bool log_dim){
+ if constexpr (Level == D) {
+ json_helper::skip_whitespace(buff);
+ try {
+ to.push_back({});
+ }catch(std::exception& e){
+ return make_error<err::out_of_memory>();
+ }
+ auto eov = json_decode<T, RootSchema, ToDecode>::decode(buff, to.back());
+ if(eov.is_error()){
+ return eov;
+ }
+ } else {
+ assert(buff.read() == '[');
+ buff.read_advance(1);
+
+ json_helper::skip_whitespace(buff);
+ if ( buff.read_composite_length() == 0 ){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ index[Level] = 0;
+ for(;;){
+ // We should have an element right now
+ auto eov = decode_flat_level<Level+1>(buff,to,index,dims, index[Level] == 0 && log_dim);
+ if(eov.is_error()){
+ return eov;
+ }
+ json_helper::skip_whitespace(buff);
+ if ( buff.read_composite_length() == 0 ){
+ return make_error<err::buffer_exhausted>();
+ }
+
+ ++index[Level];
+ if(buff.read() == ','){
+ buff.read_advance(1);
+ } else if(buff.read() == ']'){
+ buff.read_advance(1);
+ break;
+ } else {
+ return make_error<err::invalid_state>();
+ }
+ json_helper::skip_whitespace(buff);
+ if ( buff.read_composite_length() == 0 ){
+ return make_error<err::buffer_exhausted>();
+ }
+ }
+ if(log_dim){
+ dims[Level] = index[Level];
+ }else if (dims[Level] != index[Level]){
+ return make_error<err::invalid_state>();
+ }
+ }
+ return void_t{};
+ }
+
+ template<std::size_t Level>
+ static error_or<void> decode_unflat_level(std::vector<data<T,encode::Native>>& flat, data<schema::Array<T,D>, ToDecode>& to, std::array<std::size_t, D>& index, std::size_t& flat_index) {
+ if constexpr ( Level == D ){
+ auto& flat_data = flat.at(flat_index);
+ to.at(index) = std::move(flat_data);
+ ++flat_index;
+ }else {
+ const std::size_t dim_size = to.get_dim_size(Level);
+ for(index[Level] = 0; index[Level] < dim_size; ++index[Level]){
+
+ auto eov = decode_unflat_level<Level+1>(flat, to, index, flat_index);
+ if(eov.is_error()){
+ return eov;
+ }
+ }
+ }
+ return void_t{};
+ }
+
+ static error_or<void> decode(buffer_view& buff, data<schema::Array<T,D>, ToDecode>& to){
+ std::array<std::size_t, D> index;
+ std::array<std::size_t, D> dims;
+ std::fill(dims.begin(), dims.end(), 0);
+ std::vector<data<T,encode::Native>> flat_array;
+ auto eov = decode_flat_level<0>(buff, flat_array, index, dims, true);
+ if(eov.is_error()){
+ return eov;
+ }
+
+ to = {dims};
+ std::size_t flat_index = 0;
+
+ return decode_unflat_level<0>(flat_array, to, index, flat_index);
+ }
+};
+}
+}