.. _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 #include #include "flamegpu/exception/FLAMEGPUException.h" #include "flamegpu/simulation/detail/CUDAEnvironmentDirectedGraphBuffers.cuh" namespace flamegpu { namespace io { namespace { class JSONAdjacencyGraphSizeReader : public rapidjson::BaseReaderHandler, JSONAdjacencyGraphSizeReader> { 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 Bool(bool b) { return true; } bool Int(int i) { return true; } bool Uint(unsigned u) { return true; } bool Int64(int64_t i) { return true; } bool Uint64(uint64_t u) { return true; } bool Double(double d) { return true; } bool String(const char*str, rapidjson::SizeType, bool) { return true; } bool StartObject() { if (mode.empty()) { mode.push(Root); } else if (mode.top() == Nodes) { ++vertex_count; } else if (mode.top() == Links) { ++edge_count; } return true; } bool Key(const char* str, rapidjson::SizeType, bool) { lastKey = str; return true; } bool EndObject(rapidjson::SizeType) { return true; } bool StartArray() { 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 EndArray(rapidjson::SizeType) { mode.pop(); return true; } }; class JSONAdjacencyGraphReader : public rapidjson::BaseReaderHandler, JSONAdjacencyGraphReader> { 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::RapidJSONError("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::RapidJSONError("Input file '%s' contain edge property '%s', of unknown type %s.\n", filename.c_str(), lastKey.c_str(), val_type.name()); } } else { THROW exception::RapidJSONError("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() { return true; } bool Bool(bool b) { return processValue(b); } bool Int(int i) { return processValue(i); } bool Uint(unsigned u) { return processValue(u); } bool Int64(int64_t i) { return processValue(i); } bool Uint64(uint64_t u) { return processValue(u); } bool Double(double d) { return processValue(d); } bool String(const char* str, rapidjson::SizeType, bool) { 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); } return true; } } else if (mode.top() == Link) { const auto f = vertex_id_map.find(str); if (vertex_id_map.empty()) { THROW exception::RapidJSONError("'links' object occurs before 'nodes' object, unable to parse.\n", filename.c_str()); } else if (f == vertex_id_map.end()) { THROW exception::RapidJSONError("Edge refers to unrecognised Vertex ID '%s', unable to load input file '%s'.\n", 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); } return true; } } THROW exception::RapidJSONError("Unexpected string whilst parsing input file '%s', string properties are not supported.\n", filename.c_str()); } bool StartObject() { 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::RapidJSONError("Unexpected object start whilst parsing input file '%s'.\n", filename.c_str()); } return true; } bool Key(const char* str, rapidjson::SizeType, bool) { lastKey = str; return true; } bool EndObject(rapidjson::SizeType) { if (mode.top() == Node || mode.top() == Link) { ++current_index; } mode.pop(); return true; } bool StartArray() { if (current_variable_array_index != 0) { THROW exception::RapidJSONError("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::RapidJSONError("Unexpected array start whilst parsing input file '%s'.\n", filename.c_str()); } return true; } bool EndArray(rapidjson::SizeType) { if (mode.top() == VariableArray) { mode.pop(); current_variable_array_index = 0; } else { mode.pop(); current_index = 0; } return true; } }; } // 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()); } std::string filestring = std::string((std::istreambuf_iterator(in)), std::istreambuf_iterator()); rapidjson::StringStream filess(filestring.c_str()); rapidjson::Reader reader; // First count the size of the graph JSONAdjacencyGraphSizeReader graphSizeCounter; rapidjson::ParseResult pr1 = reader.Parse(filess, graphSizeCounter); if (pr1.Code() != rapidjson::ParseErrorCode::kParseErrorNone) { THROW exception::RapidJSONError("Whilst calculating graph size from input file '%s', RapidJSON returned error: %s\n", filepath.c_str(), rapidjson::GetParseError_En(pr1.Code())); } // Second (pre)allocate the graph's buffers directed_graph->setVertexCount(graphSizeCounter.getVertexCount(), stream); directed_graph->setEdgeCount(graphSizeCounter.getEdgeCount()); // Third reset the string stream filess = rapidjson::StringStream(filestring.c_str()); // Fourth parse the graph (and map string vertex IDs to integers) JSONAdjacencyGraphReader graphReader(filepath, directed_graph, stream); rapidjson::ParseResult pr2 = reader.Parse(filess, graphReader); if (pr2.Code() != rapidjson::ParseErrorCode::kParseErrorNone) { THROW exception::RapidJSONError("Whilst reading graph from input file '%s', RapidJSON returned error: %s\n", filepath.c_str(), rapidjson::GetParseError_En(pr1.Code())); } } } // namespace io } // namespace flamegpu