.. _program_listing_file_src_flamegpu_io_JSONGraphReader.cpp: Program Listing for File JSONGraphReader.cpp ============================================ |exhale_lsh| :ref:`Return to documentation for file ` (``src/flamegpu/io/JSONGraphReader.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "flamegpu/io/JSONGraphReader.h" #include #include #include #include #include #include #include #include #include "flamegpu/exception/FLAMEGPUException.h" #include "flamegpu/simulation/detail/CUDAEnvironmentDirectedGraphBuffers.cuh" namespace flamegpu { namespace io { namespace { class JSONAdjacencyGraphSizeReader : public nlohmann::json_sax { enum Mode{ Nop, Root, Nodes, Links }; std::stack mode; unsigned int vertex_count = 0; unsigned int edge_count = 0; std::string lastKey; public: unsigned int getVertexCount() const { return vertex_count; } unsigned int getEdgeCount() const { return edge_count; } bool null() { return true; } bool boolean(bool) { return true; } bool number_integer(number_integer_t) { return true; } bool number_unsigned(number_unsigned_t) { return true; } bool number_float(number_float_t, const string_t&) { return true; } bool string(string_t& ) { return true; } bool binary(binary_t&) { return true; } bool start_object(size_t) { if (mode.empty()) { mode.push(Root); } else if (mode.top() == Nodes) { ++vertex_count; } else if (mode.top() == Links) { ++edge_count; } return true; } bool key(string_t &str) { lastKey = str; return true; } bool end_object() { return true; } bool start_array(size_t) { if (mode.top() == Root && lastKey == "nodes") { mode.push(Nodes); } else if (mode.top() == Root && lastKey == "links") { mode.push(Links); } else { mode.push(Nop); } return true; } bool end_array() { mode.pop(); return true; } // called when a parse error occurs; byte position, the last token, and an exception is passed bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const nlohmann::json::exception& ex) { THROW exception::JSONError(ex.what()); } }; class JSONAdjacencyGraphReader : public nlohmann::json_sax { enum Mode{ Nop, Root, Nodes, Links, Node, Link, VariableArray }; std::stack mode; std::string lastKey; std::string filename; const std::shared_ptr& graph; cudaStream_t stream; const EnvironmentDirectedGraphData &metagraph; std::map vertex_id_map; unsigned int current_index = 0; unsigned int next_id = 1; std::set used_vertex_ids; unsigned int current_variable_array_index = 0; unsigned int last_source = ID_NOT_SET, last_target = ID_NOT_SET; public: JSONAdjacencyGraphReader(const std::string &_filename, const std::shared_ptr& _graph, cudaStream_t _stream) : filename(_filename) , graph(_graph) , stream(_stream) , metagraph(_graph->getDescription()) { } template bool processValue(const T val) { Mode isArray = Nop; if (mode.top() == VariableArray) { isArray = mode.top(); mode.pop(); } if (mode.top() == Node) { const auto f = metagraph.vertexProperties.find(lastKey); if (f == metagraph.vertexProperties.end()) { if (current_index == 0) { fprintf(stderr, "Input file '%s' contains unexpected vector property '%s', skipped during parse.\n", filename.c_str(), lastKey.c_str()); } return true; } const auto &var_data = f->second; size_type elements = var_data.elements; const std::type_index val_type = var_data.type; if (val_type == std::type_index(typeid(float))) { const float t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(double))) { const double t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int64_t))) { const int64_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint64_t))) { const uint64_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int32_t))) { const int32_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint32_t))) { const uint32_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int16_t))) { const int16_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint16_t))) { const uint16_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int8_t))) { const int8_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint8_t))) { const uint8_t t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(char))) { const char t = static_cast(val); graph->getVertexPropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else { THROW exception::JSONError("Input file '%s' contain vertex property '%s', of unknown type %s.\n", filename.c_str(), lastKey.c_str(), val_type.name()); } } else if (mode.top() == Link) { const auto f = metagraph.edgeProperties.find(lastKey); if (f == metagraph.edgeProperties.end()) { if (current_index == 0) { fprintf(stderr, "Input file '%s' contains unexpected edge property '%s', skipped during parse.\n", filename.c_str(), lastKey.c_str()); } return true; } const auto &var_data = f->second; size_type elements = var_data.elements; const std::type_index val_type = var_data.type; if (val_type == std::type_index(typeid(float))) { const float t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(double))) { const double t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int64_t))) { const int64_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint64_t))) { const uint64_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int32_t))) { const int32_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint32_t))) { const uint32_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int16_t))) { const int16_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint16_t))) { const uint16_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(int8_t))) { const int8_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(uint8_t))) { const uint8_t t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else if (val_type == std::type_index(typeid(char))) { const char t = static_cast(val); graph->getEdgePropertyBuffer(lastKey, elements, stream)[current_index * elements + current_variable_array_index++] = t; } else { THROW exception::JSONError("Input file '%s' contain edge property '%s', of unknown type %s.\n", filename.c_str(), lastKey.c_str(), val_type.name()); } } else { THROW exception::JSONError("Unexpected value with key '%s' whilst parsing input file '%s'.\n", lastKey.c_str(), filename.c_str()); } if (isArray == VariableArray) { mode.push(isArray); } else { current_variable_array_index = 0; // Didn't actually want to increment it above, because not in an array } return true; } bool null() { fprintf(stderr, "Warning: JSON graph property '%s' contains NULL, this has been interpreted as NaN (but may represent Inf).\n", lastKey.c_str()); return processValue(std::numeric_limits::quiet_NaN()); } bool boolean(bool b) { return processValue(b); } bool number_integer(number_integer_t i) { return processValue(i); } bool number_unsigned(number_unsigned_t u) { return processValue(u); } bool number_float(number_float_t d, const string_t&) { return processValue(d); } bool string(string_t& str) { if (mode.top() == Node) { if (lastKey == "id") { // Attempt to convert the string to an int unsigned int parse_int = ID_NOT_SET; try { parse_int = static_cast(std::stoul(str)); } catch (...) { } unsigned int assigned_id = parse_int; if (assigned_id == ID_NOT_SET) { while (!used_vertex_ids.emplace(next_id++).second) { } } vertex_id_map.emplace(str, assigned_id); graph->setVertexID(current_index, assigned_id, stream); return true; } else { if (current_index) { fprintf(stderr, "Input file '%s' contains vertex property '%s' of type String, this has been skipped during loading.", filename.c_str(), str.c_str()); } return true; } } else if (mode.top() == Link) { const auto f = vertex_id_map.find(str); if (vertex_id_map.empty()) { THROW exception::JSONError("'links' object occurs before 'nodes' object, unable to parse.\n", filename.c_str()); } else if (f == vertex_id_map.end()) { THROW exception::JSONError("Edge refers to unrecognised Vertex ID '%s', unable to load input file '%s'.\n", str.c_str(), filename.c_str()); } if (lastKey == "source") { if (last_target == ID_NOT_SET) { last_source = f->second; } else { graph->setEdgeSourceDestination(current_index, f->second, last_target); last_target = ID_NOT_SET; } return true; } else if (lastKey == "target") { if (last_source == ID_NOT_SET) { last_target = f->second; } else { graph->setEdgeSourceDestination(current_index, last_source, f->second); last_source = ID_NOT_SET; } return true; } else { if (current_index) { fprintf(stderr, "Input file '%s' contains edge property '%s' of type String, this has been skipped during loading.", filename.c_str(), str.c_str()); } return true; } } THROW exception::JSONError("Unexpected string whilst parsing input file '%s', string properties are not supported.\n", filename.c_str()); } bool binary(binary_t&) { THROW exception::JSONError("Unexpected binary value whilst parsing input file '%s'.\n", filename.c_str()); } bool start_object(size_t) { if (mode.empty()) { mode.push(Root); } else if (mode.top() == Nodes) { mode.push(Node); } else if (mode.top() == Links) { mode.push(Link); } else { THROW exception::JSONError("Unexpected object start whilst parsing input file '%s'.\n", filename.c_str()); } return true; } bool key(string_t &str) { lastKey = str; return true; } bool end_object() { if (mode.top() == Node || mode.top() == Link) { ++current_index; } mode.pop(); return true; } bool start_array(size_t) { if (current_variable_array_index != 0) { THROW exception::JSONError("Array start when current_variable_array_index !=0, in file '%s'. This should never happen.\n", filename.c_str()); } if (mode.top() == Root && lastKey == "nodes") { mode.push(Nodes); } else if (mode.top() == Root && lastKey == "links") { mode.push(Links); } else if (mode.top() == Node || mode.top() == Link) { mode.push(VariableArray); } else { THROW exception::JSONError("Unexpected array start whilst parsing input file '%s'.\n", filename.c_str()); } return true; } bool end_array() { if (mode.top() == VariableArray) { mode.pop(); current_variable_array_index = 0; } else { mode.pop(); current_index = 0; } return true; } // called when a parse error occurs; byte position, the last token, and an exception is passed bool parse_error(std::size_t /*position*/, const std::string& /*last_token*/, const nlohmann::json::exception& ex) { THROW exception::JSONError(ex.what()); } }; } // namespace void JSONGraphReader::loadAdjacencyLike(const std::string& filepath, const std::shared_ptr& directed_graph, cudaStream_t stream) { std::ifstream in(filepath, std::ios::in | std::ios::binary); if (!in) { THROW exception::InvalidFilePath("Unable to open file '%s' for reading.\n", filepath.c_str()); } // First count the size of the graph JSONAdjacencyGraphSizeReader graphSizeCounter; if (!nlohmann::json::sax_parse(in, &graphSizeCounter)) { THROW exception::JSONError("Calculating graph size from input file '%s' failed, in JSONGraphReader::loadAdjacencyLike()\n", filepath.c_str()); } // Second (pre)allocate the graph's buffers directed_graph->setVertexCount(graphSizeCounter.getVertexCount(), stream); directed_graph->setEdgeCount(graphSizeCounter.getEdgeCount()); // Third reset the stream in.clear(); in.seekg(0); // Fourth parse the graph (and map string vertex IDs to integers) JSONAdjacencyGraphReader graphReader(filepath, directed_graph, stream); if (!nlohmann::json::sax_parse(in, &graphReader)) { THROW exception::JSONError("Reading graph from input file '%s' failed, in JSONGraphReader::loadAdjacencyLike()\n", filepath.c_str()); } } } // namespace io } // namespace flamegpu