#pragma once #include #include namespace saw { namespace impl { 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); } } static error_or print_pretty_indent(buffer& to, uint64_t depth){ { auto eov = to.push(*reinterpret_cast("\n")); if(!eov.template is_type()){ return eov; } } for(uint64_t ind = 0; ind < depth; ++ind){ auto eov_ele = to.push('\t'); if(!eov_ele.template is_type()){ return eov_ele; } } return void_t{}; } }; template class json_encode { static_assert(always_false, "This schema type is not being handled by the json encoding."); }; template struct json_encode { using Schema = schema::Bool; static error_or encode(const data& from, buffer& to, uint64_t, bool){ auto val = from.get(); std::string_view view = val ? "true" : "false"; uint64_t view_s = view.size(); auto& buff = to; { error err = buff.write_require_length(view_s); if(!err.template is_type()){ return std::move(err); } } { error err = buff.push(*reinterpret_cast(&view[0]), view_s); if(!err.template is_type()){ return std::move(err); } } return make_void(); } }; template struct json_encode, FromEncode> { using Schema = schema::Primitive; static error_or encode(const data& from, buffer& to, uint64_t, bool) { auto val = from.get(); std::array data; auto tc_result = std::to_chars(reinterpret_cast(data.data()), reinterpret_cast(data.data())+data.size(), val); if(tc_result.ec != std::errc{}){ return make_error(); } size_t bytes_written = 0; for(char* ptr = reinterpret_cast(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()){ return std::move(err); } for(char* ptr = reinterpret_cast(data.data()); ptr != tc_result.ptr; ++ptr){ uint8_t* un_ptr = reinterpret_cast(ptr); buff.push(un_ptr[0]); } return void_t{}; } }; template struct json_encode { using Schema = schema::String; static error_or encode(const data& from, buffer& to, uint64_t, bool) { { auto err = to.push('"'); if(!err.template is_type()){ 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()){ return err; } } { auto err = to.push('"'); if(!err.template is_type()){ return err; } } return void_t{}; } }; template struct json_encode, FromEncode> { using Schema = schema::Tuple; template static error_or encode_element(const data, FromEncode>& from, buffer& to, uint64_t depth, bool pretty){ if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } { auto eov = json_encode::type, FromEncode>::encode(from.template get(), to, depth, pretty); if(eov.is_error()){ return eov; } } if constexpr ( (i+1) < sizeof...(T)){ { auto eov_ele = to.push(','); if(!eov_ele.template is_type()){ return eov_ele; } } { auto eov_ele = encode_element(from, to, depth, pretty); if(eov_ele.is_error()){ return eov_ele; } } } return void_t{}; } static error_or encode(const data& from, buffer& to, uint64_t depth, bool pretty) { { auto err = to.push('['); if(!err.template is_type()){ return err; } } if constexpr ( sizeof...(T) > 0 ){ auto eov = encode_element<0>(from, to, depth+1u, pretty); if(eov.is_error()){ return eov; } } if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } { auto err = to.push(']'); if(!err.template is_type()){ return err; } } return void_t{}; } }; template struct json_encode, FromEncode> { using Schema = schema::Array; template static error_or encode_level(const data& from, buffer& to, std::array& index, uint64_t depth, bool pretty){ if constexpr (Level == D){ if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } auto eov = json_encode::encode(from.at(index), to, depth, pretty); if(eov.is_error()){ return eov; } } else { if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } { auto err = to.push('['); if(!err.template is_type()){ 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()){ return err; } } { index[Level] = i; auto eov = encode_level(from, to, index, depth+1u, pretty); if(eov.is_error()){ return eov; } } } if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } { auto err = to.push(']'); if(!err.template is_type()){ return err; } } } return void_t{}; } static error_or encode(const data& from, buffer& to, uint64_t depth, bool pretty) { std::array index; return encode_level<0>(from, to, index, depth+1u, pretty); } }; template struct json_encode, FromEncode> { using Schema = schema::FixedArray; static error_or encode_at(const data& from, buffer& to, std::array& index, uint64_t depth, bool pretty){ auto eov = json_encode::encode(from.at(index), to, depth, pretty); if(eov.is_error()){ return eov; } return void_t{}; } template static error_or encode_level(const data& from, buffer& to, std::array& index, uint64_t depth, bool pretty){ { auto err = to.push('['); if(!err.template is_type()){ return err; } } for(std::size_t i = 0; i < Dim; ++i){ if( i > 0 ){ auto err = to.push(','); if(!err.template is_type()){ return err; } } { index[Level] = i; if constexpr (sizeof...(DimPack) > 0){ auto eov = encode_level(from, to, index, depth, pretty); if(eov.is_error()){ return eov; } }else{ auto eov = encode_at(from,to,index,depth,pretty); if(eov.is_error()){ return eov; } } } } { auto err = to.push(']'); if(!err.template is_type()){ return err; } } return void_t{}; } static error_or encode(const data& from, buffer& to, uint64_t depth, bool pretty) { if constexpr (sizeof...(D) > 0){ std::array index; return encode_level<0,D...>(from, to, index, depth+1u, pretty); } return void_t{}; } }; template struct json_encode...>, FromEncode> { using Schema = schema::Struct...>; template static error_or encode_element(const data& from, buffer& to, uint64_t depth, bool pretty){ if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } // Encode the name { std::string_view view = parameter_key_pack_type::literal.view(); error err = to.push('"'); if(!err.template is_type()){ return err; } err = to.push(*reinterpret_cast(view.data()), view.size()); if(!err.template is_type()){ return err; } err = to.push('"'); if(!err.template is_type()){ return err; } } // Add the separator { auto eov_ele = to.push(':'); if(!eov_ele.template is_type()){ return eov_ele; } } // Encode the value auto eov = json_encode::type, FromEncode>::encode(from.template get::literal>(), to, depth, pretty); // Go to the next element if constexpr ( (i+1) < sizeof...(T)){ { auto eov_ele = to.push(','); if(!eov_ele.template is_type()){ return eov_ele; } } { auto eov_ele = encode_element(from, to, depth, pretty); if(eov_ele.is_error()){ return eov_ele; } } } return void_t{}; } static error_or encode(const data& from, buffer& to, uint64_t depth, bool pretty) { { auto err = to.push('{'); if(!err.template is_type()){ return err; } } if constexpr ( sizeof...(T) > 0 ){ auto eov = encode_element<0>(from, to, depth+1u, pretty); if(eov.is_error()){ return eov; } } if(pretty){ auto eov = json_helper::print_pretty_indent(to, depth); if(eov.is_error()){ return eov; } } { auto err = to.push('}'); if(!err.template is_type()){ return err; } } return void_t{}; } }; template struct json_encode...>, FromEncode> { using Schema = schema::Union...>; template static error_or encode_element(const data& from, buffer& to, uint64_t depth, bool pretty){ if(from.template holds_alternative::literal>()){ // Encode the name { std::string_view view = parameter_key_pack_type::literal.view(); error err = to.push('"'); if(!err.template is_type()){ return err; } err = to.push(*reinterpret_cast(view.data()), view.size()); if(!err.template is_type()){ return err; } err = to.push('"'); if(!err.template is_type()){ return err; } } // Add the separator { auto eov_ele = to.push(':'); if(!eov_ele.template is_type()){ return eov_ele; } } // Encode the value auto eov = json_encode::type, FromEncode>::encode(from.template get::literal>(), to, depth, pretty); } // Go to the next element if constexpr ( (i+1) < sizeof...(T)){ { auto eov_ele = encode_element(from, to, depth, pretty); if(eov_ele.is_error()){ return eov_ele; } } } return void_t{}; } static error_or encode(const data& from, buffer& to, uint64_t depth, bool pretty) { { auto err = to.push('{'); if(!err.template is_type()){ return err; } } if constexpr ( sizeof...(T) > 0 ){ auto eov = encode_element<0>(from, to, depth, pretty); if(eov.is_error()){ return eov; } } { auto err = to.push('}'); if(!err.template is_type()){ return err; } } return void_t{}; } }; template struct json_decode; template struct json_decode, ToDecode> { using Schema = schema::Primitive; static error_or decode(buffer_view& buff, data& 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(); } if (offset >= buff.read_composite_length()) { return make_error(); } if (buff.read(offset) >= '1' && buff.read(offset) <= '9') { ++offset; if(offset >= buff.read_composite_length()) { return make_error(); } 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(); } { std::string_view num_view{reinterpret_cast(&buff.read()), offset}; typename native_data_type::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("Couldn't decode Primitive Value"); } to.set(result); } buff.read_advance(offset); return void_t{}; } }; template struct json_decode { using Schema = schema::Bool; static error_or decode(buffer_view& buff, data& to){ SAW_ASSERT(buff.read() == 't' || buff.read() == 'f'){ return make_error("Invalid JSON boolean"); } bool assumption = (buff.read() == 't'); std::string_view expected_str = assumption ? "true" : "false"; if(buff.read_composite_length() < expected_str.size()){ return make_error(); } for(uint64_t i = 0; i < expected_str.size(); ++i){ if(buff.read(i) != static_cast(expected_str[i])){ return make_error("Invalid JSON boolean"); } } to.set(assumption); buff.read_advance(expected_str.size()); return make_void(); } }; template struct json_decode { using Schema = schema::String; static error_or decode(buffer_view& buff, data& 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(); } switch(buff.read()){ case '\\':{ buff.read_advance(1); if(buff.read_composite_length() == 0){ return make_error(); } 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(); } 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<(); } } while( buff.read() < 0 ); iss<<'?'; } break; } } buff.read_advance(1); } std::string raw = iss.str(); to.set(std::move(raw)); return void_t{}; } }; template struct json_decode...>, ToDecode> { using Schema = schema::Struct...>; template static error_or decode_field_search(buffer_view& buff, data& to, std::array& fields, const data& search_name){ if constexpr ( i < sizeof...(T)){ using Type = typename parameter_pack_type::type; constexpr static string_literal Literal = parameter_key_pack_type::literal; if(search_name == Literal.view()){ if(fields[i]){ return make_error("Duplicate entry in Struct"); } fields[i] = true; auto eov = json_decode::decode(buff, to.template get()); if(eov.is_error()){ return eov; } }else { decode_field_search(buff, to, fields, search_name); } }else { return make_error("Field in Struct not found"); } return void_t{}; } static error_or decode_fields(buffer_view& buff, data& to, std::array& fields){ for(;;){ data name; auto eov = json_decode::decode(buff, name); if(eov.is_error()){ return eov; } json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } if(buff.read() != ':'){ return make_error("Field separator in Struct expected"); } buff.read_advance(1); json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } { 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(); } 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("Not all fields were set"); } } buff.read_advance(1); return void_t{}; }else{ return build_error([&](){ return std::string{"Expect ',' or '}', but got "} + std::string{static_cast(buff.read())}; }).template make_error("Struct Separator ',' or Struct '}' ending expected"); } json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } } return void_t{}; } static error_or decode(buffer_view& buff, data& to){ std::array found_fields; std::fill(found_fields.begin(), found_fields.end(), false); SAW_ASSERT(buff.read() == '{'){ return make_error("Expected Struct beginning '{'"); } buff.read_advance(1); json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } // Check if there are no elements present in the JSON Struct if(buff.read() == '}'){ if(sizeof...(T) > 0){ return make_error("Expected non empty struct"); } 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 struct json_decode, ToDecode> { using Schema = schema::Tuple; template static error_or decode_element(buffer_view& buff, data& to){ if constexpr (i < sizeof...(T)){ if constexpr ( i > 0 ){ if(buff.read() != ','){ return make_error("Expected ',' in Tuple"); } buff.read_advance(1); json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } } using Type = typename parameter_pack_type::type; auto eov = json_decode::decode(buff, to.template get()); if(eov.is_error()){ return eov; } json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } eov = decode_element(buff, to); if(eov.is_error()){ return eov; } }else{ if(buff.read() != ']'){ return make_error("Expected Tuple Ending ']'"); } buff.read_advance(1); } return void_t{}; } static error_or decode(buffer_view& buff, data& to){ assert(buff.read() == '['); buff.read_advance(1); json_helper::skip_whitespace(buff); if(buff.read_composite_length() == 0){ return make_error(); } 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 struct json_decode, ToDecode> { using Schema = schema::Array; template static error_or decode_flat_level(buffer_view& buff, std::vector>& to, std::array& index, std::array& dims, bool log_dim){ if constexpr (Level == D) { json_helper::skip_whitespace(buff); try { to.push_back({}); }catch(std::exception& e){ return make_error(); } auto eov = json_decode::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(); } /** * Check if array is empty. */ bool is_empty = false; if(buff.read() == ']'){ buff.read_advance(1); is_empty = true; } index[Level] = 0; for(;!is_empty;){ // We should have an element right now auto eov = decode_flat_level(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(); } ++index[Level]; if(buff.read() == ','){ buff.read_advance(1); } else if(buff.read() == ']'){ buff.read_advance(1); break; } else { return make_error("Expected either a ',' or a ']'."); } json_helper::skip_whitespace(buff); if ( buff.read_composite_length() == 0 ){ return make_error("Buffer empty before decoding was finished."); } } if(log_dim){ dims[Level] = index[Level]; }else if (dims[Level] != index[Level]){ return make_error("Not matching Array endings"); } } return void_t{}; } template static error_or decode_unflat_level(std::vector>& flat, data, ToDecode>& to, std::array& 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(flat, to, index, flat_index); if(eov.is_error()){ return eov; } } } return void_t{}; } static error_or decode(buffer_view& buff, data& to){ std::array index; std::array dims; std::fill(dims.begin(), dims.end(), 0); std::vector> 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); } }; } }