From 1559bcf0cbf5baaa15f753a5b7bf907c3db3d4be Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Tue, 30 Jul 2024 12:25:41 -0400 Subject: [PATCH 01/38] Add JSON support + wrapper --- src/feeder/JSONNode.cpp | 155 ++++++++++ src/feeder/JSONNode.h | 138 +++++++++ src/feeder/WrapperNode.cpp | 554 ++++++++++++++++++++++++++++++++++ src/feeder/WrapperNode.h | 80 +++++ src/feeder/et_feeder.cpp | 4 +- src/feeder/et_feeder.h | 9 +- src/feeder/et_feeder_node.cpp | 34 +-- src/feeder/et_feeder_node.h | 16 +- 8 files changed, 957 insertions(+), 33 deletions(-) create mode 100644 src/feeder/JSONNode.cpp create mode 100644 src/feeder/JSONNode.h create mode 100644 src/feeder/WrapperNode.cpp create mode 100644 src/feeder/WrapperNode.h diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp new file mode 100644 index 00000000..29019ab9 --- /dev/null +++ b/src/feeder/JSONNode.cpp @@ -0,0 +1,155 @@ +#include "et_feeder/JSONNode.h" + +// JSONNode default constructor +JSONNode::JSONNode() {} + +// JSONNode copy constructor +JSONNode::JSONNode(const JSONNode &t) { + node_id = t.node_id; + node_name = t.node_name; + node_type = t.node_type; + is_cpu_op = t.is_cpu_op; + runtime = t.runtime; + data_deps = t.data_deps; + dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; + children_vec_json = t.children_vec_json; + children_set_json = t.children_set_json; + if (node_type == 5 || node_type == 6 || node_type == 7) { + tensor_size = t.tensor_size; + comm_type = t.comm_type; + comm_priority = t.comm_priority; + comm_size = t.comm_size; + comm_src = t.comm_src; + comm_dst = t.comm_dst; + comm_tag = t.comm_tag; + involved_dim_size = t.involved_dim_size; + involved_dim = t.involved_dim; + } +} + +// JSONNode constructor +JSONNode::JSONNode(json data, int32_t id) { + try { + node_id = data["workload_graph"][id]["Id"]; + } + catch (...) { + std::cerr << "node_id not specified in ET" << std::endl; + } + try { + node_name = data["workload_graph"][id]["Name"]; + } + catch (...) { + std::cerr << "node_name not specified in ET" << std::endl; + } + try { + node_type = data["workload_graph"][id]["NodeType"]; + } + catch (...) { + std::cerr << "node_type not specified in ET" << std::endl; + } + try { + is_cpu_op = data["workload_graph"][id]["is_cpu_op"]; + } + catch (...) { + std::cerr << "is_cpu_op not specified in ET" << std::endl; + } + try { + runtime = data["workload_graph"][id]["runtime"]; + } + catch (...) { + // std::cerr << "runtime not specified in ET" << std::endl; + } + try { + data_deps = data["workload_graph"][id]["data_deps"].get>(); + } + catch (...) { + std::cerr << "data deps not specified in ET" << std::endl; + } + if (node_type == 5 || node_type == 6 || node_type == 7) { + try { + tensor_size = data["workload_graph"][id]["tensor_size"]; + } + catch (...) { + // std::cout << "tensor_size not specified in ET" <>(); + } + catch (...) { + // std::cout << "involved_dim not specified in ET" < JSONNode::getDepUnresolvedParentIDs() { + return dep_unresolved_parent_ids_json; +} + +// Set dependency unresolved parent IDs +void JSONNode::setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids) { + dep_unresolved_parent_ids_json = dep_unresolved_parent_ids; +} + +// Add child +void JSONNode::addChild(JSONNode node) { + // Avoid adding the same child node multiple times + // addChild is called multiple times to resolve dependencies + if (children_set_json.find(node) != children_set_json.end()) { + return; + } + children_vec_json.emplace_back(node); + children_set_json.emplace(node); +} + +// Get children vector +std::vector JSONNode::getChildren() { + return children_vec_json; +} diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h new file mode 100644 index 00000000..1baedf71 --- /dev/null +++ b/src/feeder/JSONNode.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +using json = nlohmann::json; + +class JSONNode { + public: + int64_t node_id; + std::string node_name; + int node_type; + bool is_cpu_op; + int64_t runtime; + int64_t num_ops; + int64_t tensor_size; + int64_t comm_type; + int32_t comm_priority; + int64_t comm_size; + int32_t comm_src; + int32_t comm_dst; + int32_t comm_tag; + int32_t involved_dim_size; + std::vector involved_dim; + std::vector data_deps{}; + std::vector dep_unresolved_parent_ids_json{}; + std::vector children_vec_json{}; + + // Compare function for set + struct CompareJSONNodesLT { + bool operator()(const JSONNode& a, const JSONNode& b) const { + return a.node_id < b.node_id; + } + }; + std::set children_set_json{}; + + JSONNode(); + JSONNode(const JSONNode &t); + JSONNode(json data, int32_t id); + void addDepUnresolvedParentID(int64_t node_id); + std::vector getDepUnresolvedParentIDs(); + void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); + void addChild(JSONNode node); + std::vector getChildren(); + + // Define the == operator for comparison + bool operator==(const JSONNode& other) const { + return node_id == other.node_id && + node_name == other.node_name && + node_type == other.node_type && + is_cpu_op == other.is_cpu_op && + runtime == other.runtime && + num_ops == other.num_ops && + tensor_size == other.tensor_size && + comm_type == other.comm_type && + comm_priority == other.comm_priority && + comm_size == other.comm_size && + comm_src == other.comm_src && + comm_dst == other.comm_dst && + comm_tag == other.comm_tag && + involved_dim_size == other.involved_dim_size && + involved_dim == other.involved_dim && + data_deps == other.data_deps && + dep_unresolved_parent_ids_json == other.dep_unresolved_parent_ids_json && + children_vec_json == other.children_vec_json && + children_set_json == other.children_set_json; + } + + // Overload the assignment operator + JSONNode& operator=(const JSONNode& other) { + if (this != &other) { + // Copy all member variables + node_id = other.node_id; + node_name = other.node_name; + node_type = other.node_type; + is_cpu_op = other.is_cpu_op; + runtime = other.runtime; + num_ops = other.num_ops; + tensor_size = other.tensor_size; + comm_type = other.comm_type; + comm_priority = other.comm_priority; + comm_size = other.comm_size; + comm_src = other.comm_src; + comm_dst = other.comm_dst; + comm_tag = other.comm_tag; + involved_dim_size = other.involved_dim_size; + involved_dim = other.involved_dim; + data_deps = other.data_deps; + dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; + children_vec_json = other.children_vec_json; + children_set_json = other.children_set_json; + } + return *this; + } +}; + +// Define a custom hash function for unordered set +namespace std { + template<> + struct hash { + std::size_t operator()(const JSONNode& node) const { + std::size_t h1 = std::hash()(node.node_id); + std::size_t h2 = std::hash()(node.node_name); + std::size_t h3 = std::hash()(node.node_type); + std::size_t h4 = std::hash()(node.is_cpu_op); + std::size_t h5 = std::hash()(node.runtime); + + // A prime number for bit manipulation + const std::size_t prime = 31; + + // Combine the hash of the current member with the hashes of the previous members + std::size_t hash = h1; + hash = hash * prime + h2; + hash = hash * prime + h3; + hash = hash * prime + h4; + hash = hash * prime + h5; + + return hash; + } + }; +} + +// Compare function for JSON node for priority queue +struct CompareJSONNodesGT : public std::binary_function< + JSONNode, + JSONNode, + bool> { + bool operator()( + const JSONNode lhs, + const JSONNode rhs) const { + return lhs.node_id > rhs.node_id; + } +}; \ No newline at end of file diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp new file mode 100644 index 00000000..a7117bf6 --- /dev/null +++ b/src/feeder/WrapperNode.cpp @@ -0,0 +1,554 @@ +#include "WrapperNode.h" + +// WrapperNode default constructor +WrapperNode::WrapperNode() {} + +// WrapperNode copy constructor +WrapperNode::WrapperNode(const WrapperNode& t) { + // Copy the attributes from the original instance to the new instance + format_type_ = t.format_type_; + et_feeder_ = t.et_feeder_; + node_ = t.node_; + json_node_ = t.json_node_; + data_ = t.data_; + node_idx_ = t.node_idx_; + involved_dim_size_ = t.involved_dim_size_; + involved_dim_ = t.involved_dim_; + push_back_queue_proto = t.push_back_queue_proto; + push_back_queue_json = t.push_back_queue_json; + dep_graph_json = t.dep_graph_json; + dep_free_node_id_set_json = t.dep_free_node_id_set_json; + dep_free_node_queue_json = t.dep_free_node_queue_json; + dep_unresolved_node_set_json = t.dep_unresolved_node_set_json; + window_size_json = t.window_size_json; +} + +// WrapperNode create +// format_type_ is assigned based on the extension of the file +void WrapperNode::createWrapper(std::string filename) { + std::string ext = filename.substr(filename.find_last_of(".") + 1); + if (ext == "et") { + std::cout << "Using Protobuf format" << std::endl; + format_type_ = Protobuf; + et_feeder_ = new Chakra::ETFeeder(filename); + } + else if (ext == "json") { + std::cout << "Using JSON format" << std::endl; + format_type_ = JSON; + jsonfile_.open(filename); + data_ = json::parse(jsonfile_); // Parse JSON file + window_size_json = data_["workload_graph"].size(); // Number of nodes + // For legacy purposes. The entire JSON file is read at once + readNextWindow(); + } + else { + std::cerr << "Error: File format not supported." << std::endl; + exit(-1); + } +} + +// Release memory +void WrapperNode::releaseMemory() { + switch(format_type_) { + case Protobuf: + delete et_feeder_; + break; + case JSON: + jsonfile_.close(); + break; + default: + break; + } +} + +WrapperNode::~WrapperNode() { + // releaseMemory(); +} + +// Find the index in JSON dictionary +int64_t WrapperNode::findNodeIndexJSON(int64_t node_id) { + int64_t i; + for (i=0; i < window_size_json; i++) { + if (data_["workload_graph"][i]["Id"] == node_id) { + break; + } + } + return i; +} + +// Overloaded function - addNode +// Add JSON node to dependency graph +void WrapperNode::addNode(JSONNode node) { + dep_graph_json[node.node_id] = node; +} + +// Add Protobuf node to dependency graph +void WrapperNode::addNode(std::shared_ptr node) { + et_feeder_->addNode(node); +} + +// Remove node from dependency graph +void WrapperNode::removeNode(int64_t node_id) { + switch(format_type_) { + case Protobuf: + et_feeder_->removeNode(node_id); + break; + case JSON: + dep_graph_json.erase(node_id); + break; + default: + break; + } +} + +// Read nodes in graph +// node_idx is the continuous index of the JSON nodes and is different from node_id +JSONNode WrapperNode::readNode(int64_t node_idx) { + JSONNode node(data_, node_idx); + bool dep_unresolved = false; + for (int i = 0; i < node.data_deps.size(); ++i) { + auto parent_node = dep_graph_json.find(node.data_deps[i]); + if (parent_node != dep_graph_json.end()) { + parent_node->second.addChild(node); // Add node as a child to the parent node + } else { + dep_unresolved = true; + node.addDepUnresolvedParentID(node.data_deps[i]); + } + } + + if (dep_unresolved) { + dep_unresolved_node_set_json.emplace(node); + } + + return node; +} + +// Read nodes in a window +// For JSON, the entire graph is read in a single window +void WrapperNode::readNextWindow() { + int32_t num_read = 0; + do { + JSONNode new_node = readNode(num_read); + addNode(new_node); + ++num_read; + resolveDep(); + } while ((num_read < window_size_json) || (dep_unresolved_node_set_json.size() != 0)); + + for (auto node_id_node : dep_graph_json) { + int64_t node_id = node_id_node.first; + JSONNode node(node_id_node.second); + // Unordered set does not allow duplicates. So, count returns 1 if key exists, 0 otherwise + if ((dep_free_node_id_set_json.count(node_id) == 0) && + (node.data_deps.size() == 0)) { + dep_free_node_id_set_json.emplace(node_id); + dep_free_node_queue_json.emplace(node); + } + } +} + +// Resolve dependencies +void WrapperNode::resolveDep() { + switch(format_type_) { + case Protobuf: + et_feeder_->resolveDep(); + break; + case JSON: + // Loop over unresolved nodes + for (auto it = dep_unresolved_node_set_json.begin(); + it != dep_unresolved_node_set_json.end();) { + JSONNode node = *it; + std::vector dep_unresolved_parent_ids_json = + node.getDepUnresolvedParentIDs(); + // Loop over unresolved parent IDs + for (auto inner_it = dep_unresolved_parent_ids_json.begin(); + inner_it != dep_unresolved_parent_ids_json.end();) { + auto parent_node = dep_graph_json.find(*inner_it); + if (parent_node != dep_graph_json.end()) { + // Add current node as a child to the parent + parent_node->second.addChild(node); + inner_it = dep_unresolved_parent_ids_json.erase(inner_it); + } else { + ++inner_it; + } + } + if (dep_unresolved_parent_ids_json.size() == 0) { + it = dep_unresolved_node_set_json.erase(it); + } else { + node.setDepUnresolvedParentIDs(dep_unresolved_parent_ids_json); + ++it; + } + } + } +} + +// Push dependency free nodes +void WrapperNode::pushBackIssuableNode(int64_t node_id) { + switch(format_type_) { + case Protobuf: + et_feeder_->pushBackIssuableNode(node_id); + break; + case JSON: + JSONNode node = dep_graph_json[node_id]; + dep_free_node_id_set_json.emplace(node_id); + dep_free_node_queue_json.emplace(node); + } +} + +// Free children +void WrapperNode::freeChildrenNodes(int64_t node_id) { + switch(format_type_) { + case Protobuf: + et_feeder_->freeChildrenNodes(node_id); + break; + case JSON: + JSONNode node = dep_graph_json[node_id]; + for (auto child : node.getChildren()) { + for (auto it = child.data_deps.begin(); + it != child.data_deps.end(); + ++it) { + if (*it == node_id) { + child.data_deps.erase(it); + break; + } + } + if (child.data_deps.size() == 0) { + dep_free_node_id_set_json.emplace(child.node_id); + dep_free_node_queue_json.emplace(child); + } + } + break; + } +} + +// Check if the node is valid +bool WrapperNode::isValidNode() { + switch (format_type_) { + case Protobuf: + if (node_ == nullptr) + return false; + else + return true; + case JSON: + if (node_idx_ < 0) + return false; + else + return true; + default: + std::cerr << "Error in isValid()" << std::endl; + exit(-1); + } +} + +// Push node to queue +void WrapperNode::push_to_queue() { + switch (format_type_) { + case Protobuf: + push_back_queue_proto.push(node_); + break; + case JSON: + // JSONNode json_node(data_, node_idx_); + push_back_queue_json.push(json_node_); + break; + } +} + +// Check if queue is empty +bool WrapperNode::is_queue_empty() { + switch (format_type_) { + case Protobuf: + return push_back_queue_proto.empty(); + break; + case JSON: + return push_back_queue_json.empty(); + break; + default: + std::cerr << "Error in is_queue_empty()" << std::endl; + exit(-1); + } +} + +// Get element in the queue front +void WrapperNode::queue_front() { + switch (format_type_) { + case Protobuf: + node_ = push_back_queue_proto.front(); + case JSON: + json_node_ = push_back_queue_json.front(); + default: + std::cerr << "Error in queue_front()" << std::endl; + exit(-1); + } +} + +// Pop node from queue +void WrapperNode::pop_from_queue() { + switch (format_type_) { + case Protobuf: + push_back_queue_proto.pop(); + case JSON: + push_back_queue_json.pop(); + default: + std::cerr << "Error in pop_from_queue()" << std::endl; + exit(-1); + } +} + +// Get next issuable node from dependency free queue +void WrapperNode::getNextIssuableNode() { + switch (format_type_) { + case Protobuf: + node_ = et_feeder_->getNextIssuableNode(); + break; + case JSON: + if (dep_free_node_queue_json.size() != 0) { + json_node_ = dep_free_node_queue_json.top(); + node_idx_ = findNodeIndexJSON(json_node_.node_id); + dep_free_node_id_set_json.erase(json_node_.node_id); + dep_free_node_queue_json.pop(); + } + else + node_idx_ = -1; + break; + default: + break; + } +} + +// Get node ID +int64_t WrapperNode::getNodeID() { + switch (format_type_) { + case Protobuf: + return node_->id(); + case JSON: + return json_node_.node_id; + default: + std::cerr << "Error in getNodeID()" << std::endl; + exit(-1); + } +} + +// Get node name +std::string WrapperNode::getNodeName() { + switch (format_type_) { + case Protobuf: + return node_->name(); + case JSON: + return json_node_.node_name; + default: + std::cerr << "Error in getNodeName()" << std::endl; + exit(-1); + } +} + +// Get node type +int WrapperNode::getNodeType() { + switch (format_type_) { + case Protobuf: + return node_->type(); + case JSON: + return json_node_.node_type; + default: + std::cerr << "Error in getNodeType()" << std::endl; + exit(-1); + } +} + +// Check if CPU operation +bool WrapperNode::isCPUOp() { + switch (format_type_) { + case Protobuf: + return node_->is_cpu_op(); + case JSON: + return json_node_.is_cpu_op; + default: + std::cerr << "Error in isCPUOp()" << std::endl; + exit(-1); + } +} + +// Get runtime +int64_t WrapperNode::getRuntime() { + switch (format_type_) { + case Protobuf: + return node_->runtime(); + case JSON: + return json_node_.runtime; + default: + std::cerr << "Error in getRuntime()" << std::endl; + exit(-1); + } +} + +// Get num ops +int64_t WrapperNode::getNumOps() { + switch (format_type_) { + case Protobuf: + return node_->num_ops(); + case JSON: + return json_node_.num_ops; + default: + std::cerr << "Error in getNumOps()" << std::endl; + exit(-1); + } +} + +// Get tensor size +int64_t WrapperNode::getTensorSize() { + switch (format_type_) { + case Protobuf: + return node_->tensor_size(); + case JSON: + return json_node_.tensor_size; + default: + std::cerr << "Error in getTensorSize()" << std::endl; + exit(-1); + } +} + +// Get comm type +int64_t WrapperNode::getCommType() { + switch (format_type_) { + case Protobuf: + return node_->comm_type(); + case JSON: + return json_node_.comm_type; + default: + std::cerr << "Error in getCommType()" << std::endl; + exit(-1); + } +} + +// Get comm priority +int32_t WrapperNode::getCommPriority() { + switch (format_type_) { + case Protobuf: + return node_->comm_priority(); + case JSON: + return json_node_.comm_priority; + default: + std::cerr << "Error in getCommPriority()" << std::endl; + exit(-1); + } +} + +// Get comm size +int64_t WrapperNode::getCommSize() { + switch (format_type_) { + case Protobuf: + return node_->comm_size(); + case JSON: + return json_node_.comm_size; + default: + std::cerr << "Error in getCommSize()" << std::endl; + exit(-1); + } +} + +// Get comm src +int32_t WrapperNode::getCommSrc() { + switch (format_type_) { + case Protobuf: + return node_->comm_src(); + case JSON: + return json_node_.comm_src; + default: + std::cerr << "Error in getCommSrc()" << std::endl; + exit(-1); + } +} + +// Get comm dst +int32_t WrapperNode::getCommDst() { + switch (format_type_) { + case Protobuf: + return node_->comm_dst(); + case JSON: + return json_node_.comm_dst; + default: + std::cerr << "Error in getCommDst()" << std::endl; + exit(-1); + } +} + +// Get comm tag +int32_t WrapperNode::getCommTag() { + switch (format_type_) { + case Protobuf: + return node_->comm_tag(); + case JSON: + return json_node_.comm_tag; + default: + std::cerr << "Error in getCommTag()" << std::endl; + exit(-1); + } +} + +// Get involved dim size +int32_t WrapperNode::getInvolvedDimSize() { + switch (format_type_) { + case Protobuf: + return node_->involved_dim_size(); + case JSON: + return json_node_.involved_dim_size; + default: + std::cerr << "Error in getInvolvedDimSize()" << std::endl; + exit(-1); + } +} + +// Get involved dim +bool WrapperNode::getInvolvedDim(int i) { + switch (format_type_) { + case Protobuf: + return node_->involved_dim(i); + case JSON: + return json_node_.involved_dim[i]; + default: + std::cerr << "Error in getInvolvedDim()" << std::endl; + exit(-1); + } +} + +// Check if has more nodes to issue +bool WrapperNode::hasNodesToIssue() { + switch (format_type_) { + case Protobuf: + return et_feeder_->hasNodesToIssue(); + case JSON: + return !(dep_graph_json.empty() && dep_free_node_queue_json.empty()); + default: + std::cerr << "Error in hasNodesToIssue()" << std::endl; + exit(-1); + } +} + +// Lookup Node +void WrapperNode::lookupNode(int64_t node_id) { + switch (format_type_) { + case Protobuf: + node_ = et_feeder_->lookupNode(node_id); + break; + case JSON: + try { + json_node_ = dep_graph_json.at(node_id); + } catch (const std::out_of_range& e) { + std::cerr << "looking for node_id=" << node_id + << " in dep graph, however, not loaded yet" << std::endl; + throw(e); + } + break; + default: + std::cerr << "Error in lookupNode()" << std::endl; + exit(-1); + } +} + +// Overloaded function returns children protobuf nodes +void WrapperNode::getChildren(std::vector>& childrenNodes) { + childrenNodes = node_->getChildren(); +} + +// Overloaded function returns children JSON nodes +void WrapperNode::getChildren(std::vector& childrenNodes) { + childrenNodes = json_node_.getChildren(); +} diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h new file mode 100644 index 00000000..0b2b559e --- /dev/null +++ b/src/feeder/WrapperNode.h @@ -0,0 +1,80 @@ +#pragma once + +#include "et_feeder/et_feeder.h" +#include "et_feeder/et_feeder_node.h" +#include "et_feeder/JSONNode.h" + +using json = nlohmann::json; + +enum format { + Protobuf, + JSON +}; + +// WrapperNode class wraps protobuf and JSON +class WrapperNode { + public: + enum format format_type_; + Chakra::ETFeeder* et_feeder_; + std::shared_ptr node_ {nullptr}; + std::ifstream jsonfile_; + json data_; + JSONNode json_node_; + int64_t node_idx_ = -1; + int32_t involved_dim_size_ = 1; + std::vector involved_dim_; + std::queue> push_back_queue_proto; + std::queue push_back_queue_json; + std::unordered_map dep_graph_json{}; + std::unordered_set dep_free_node_id_set_json{}; + std::priority_queue< + JSONNode, //type of stored elements + std::vector, // underlying container to store elements + CompareJSONNodesGT> // compare type providing a strick weak ordering + dep_free_node_queue_json{}; + std::unordered_set> dep_unresolved_node_set_json{}; + int window_size_json; + + WrapperNode(); + WrapperNode(const WrapperNode& t); + ~WrapperNode(); + void releaseMemory(); + void createWrapper(std::string filename); + void addNode(JSONNode node); + void addNode(std::shared_ptr node); + void removeNode(int64_t node_id); + void readNextWindow(); + JSONNode readNode(int64_t node_id); + void resolveDep(); + void pushBackIssuableNode(int64_t node_id); + void freeChildrenNodes(int64_t node_id); + void addDepUnresolvedParentID(int64_t node_id); + std::vector getDepUnresolvedParentIDs(); + void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); + bool isValidNode(); + void push_to_queue(); + bool is_queue_empty(); + void queue_front(); + void pop_from_queue(); + void getNextIssuableNode(); + int64_t getNodeID(); + std::string getNodeName(); + int getNodeType(); + bool isCPUOp(); + int64_t getRuntime(); + int64_t getNumOps(); + int64_t getTensorSize(); + int64_t getCommType(); + int32_t getCommPriority(); + int64_t getCommSize(); + int32_t getCommSrc(); + int32_t getCommDst(); + int32_t getCommTag(); + int32_t getInvolvedDimSize(); + bool getInvolvedDim(int i); + bool hasNodesToIssue(); + void lookupNode(int64_t node_id); + void getChildren(std::vector>& childrenNodes); + void getChildren(std::vector& childrenNodes); + int64_t findNodeIndexJSON(int64_t node_id); +}; diff --git a/src/feeder/et_feeder.cpp b/src/feeder/et_feeder.cpp index 0cc0810a..38b7efaa 100644 --- a/src/feeder/et_feeder.cpp +++ b/src/feeder/et_feeder.cpp @@ -1,4 +1,4 @@ -#include "et_feeder.h" +#include "et_feeder/et_feeder.h" #include @@ -173,4 +173,4 @@ void ETFeeder::readNextWindow() { dep_free_node_queue_.emplace(node); } } -} +} \ No newline at end of file diff --git a/src/feeder/et_feeder.h b/src/feeder/et_feeder.h index d73ec79a..9510ed9b 100644 --- a/src/feeder/et_feeder.h +++ b/src/feeder/et_feeder.h @@ -6,8 +6,8 @@ #include #include -#include "et_feeder_node.h" -#include "protoio.hh" +#include "et_feeder/et_feeder_node.h" +#include "third_party/utils/protoio.hh" namespace Chakra { struct CompareNodes : public std::binary_function< @@ -33,13 +33,12 @@ class ETFeeder { void pushBackIssuableNode(uint64_t node_id); std::shared_ptr lookupNode(uint64_t node_id); void freeChildrenNodes(uint64_t node_id); - - private: void readGlobalMetadata(); std::shared_ptr readNode(); void readNextWindow(); void resolveDep(); + private: ProtoInputStream trace_; const uint32_t window_size_; bool et_complete_; @@ -54,4 +53,4 @@ class ETFeeder { std::unordered_set> dep_unresolved_node_set_{}; }; -} // namespace Chakra +} // namespace Chakra \ No newline at end of file diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index 2d89b93c..360e9cad 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -1,4 +1,4 @@ -#include "et_feeder_node.h" +#include "et_feeder/et_feeder_node.h" using namespace std; using namespace Chakra; @@ -14,7 +14,7 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { const string& attr_name = attr.name(); if (attr_name == "is_cpu_op") { - this->is_cpu_op_ = static_cast(attr.bool_val()); + this->is_cpu_op_ = static_cast(attr.int32_val()); } else if (attr_name == "num_ops") { this->num_ops_ = static_cast(attr.int64_val()); } else if (attr_name == "tensor_size") { @@ -22,10 +22,16 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { } else if (attr_name == "comm_type") { this->comm_type_ = static_cast(attr.int64_val()); + } else if (attr_name == "involved_dim") { + this->involved_dim_.clear(); + for (const bool val : attr.bool_list().values()) { + this->involved_dim_.push_back(val); + } + this->involved_dim_size_ = this->involved_dim_.size(); } else if (attr_name == "comm_priority") { this->comm_priority_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_size") { - this->comm_size_ = attr.int64_val(); + this->comm_size_ = attr.uint64_val(); } else if (attr_name == "comm_src") { this->comm_src_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_dst") { @@ -71,20 +77,6 @@ void ETFeederNode::setDepUnresolvedParentIDs( dep_unresolved_parent_ids_ = dep_unresolved_parent_ids; } -const ChakraProtoMsg::AttributeProto& ETFeederNode::get_other_attr( - const string& attr_name) const { - if (this->has_other_attr(attr_name)) - return this->other_attrs_.at(attr_name); - throw std::runtime_error( - "Asked for attr \"" + attr_name + "\" from node " + - std::to_string(this->id_) + ", which do not exist"); -} - -bool ETFeederNode::has_other_attr(const string& attr_name) const { - const auto& item = this->other_attrs_.find(attr_name); - return item != this->other_attrs_.end(); -} - uint64_t ETFeederNode::id() { return id_; } @@ -121,6 +113,14 @@ ChakraProtoMsg::CollectiveCommType ETFeederNode::comm_type() { return comm_type_; } +uint32_t ETFeederNode::involved_dim_size() { + return involved_dim_size_; +} + +bool ETFeederNode::involved_dim(int i) { + return involved_dim_[i]; +} + uint32_t ETFeederNode::comm_priority() { return comm_priority_; } diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index c0aede48..b4e5c468 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -5,7 +5,7 @@ #include #include -#include "et_def.pb.h" +#include "et_def/et_def.pb.h" namespace Chakra { @@ -20,10 +20,6 @@ class ETFeederNode { void setDepUnresolvedParentIDs( std::vector const& dep_unresolved_parent_ids); - const ChakraProtoMsg::AttributeProto& get_other_attr( - const std::string& attr_name) const; - bool has_other_attr(const std::string& attr_name) const; - uint64_t id(); std::string name(); bool is_cpu_op(); @@ -33,6 +29,8 @@ class ETFeederNode { uint32_t tensor_loc(); uint64_t tensor_size(); ChakraProtoMsg::CollectiveCommType comm_type(); + uint32_t involved_dim_size(); + bool involved_dim(int i); uint32_t comm_priority(); uint64_t comm_size(); uint32_t comm_src(); @@ -50,17 +48,17 @@ class ETFeederNode { std::unordered_set> children_set_{}; std::vector> children_vec_{}; std::vector dep_unresolved_parent_ids_{}; - std::unordered_map - other_attrs_{}; uint64_t id_; std::string name_; - bool is_cpu_op_; + uint32_t is_cpu_op_; uint64_t runtime_; uint64_t num_ops_; uint32_t tensor_loc_; uint64_t tensor_size_; ChakraProtoMsg::CollectiveCommType comm_type_; + uint32_t involved_dim_size_; + std::vector involved_dim_; uint32_t comm_priority_; uint64_t comm_size_; uint32_t comm_src_; @@ -69,4 +67,4 @@ class ETFeederNode { std::string pg_name_; }; -} // namespace Chakra +} // namespace Chakra \ No newline at end of file From 649c37b54d313d2fbd16765955455d0e2eacd8a6 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 1 Aug 2024 12:44:36 -0400 Subject: [PATCH 02/38] Rebasing with main --- src/feeder/JSONNode.cpp | 46 +- src/feeder/JSONNode.h | 6 - src/feeder/WrapperNode.cpp | 28 - src/feeder/WrapperNode.h | 4 - src/feeder/et_feeder.h | 4 +- src/feeder/et_feeder_node.cpp | 32 +- src/feeder/et_feeder_node.h | 12 +- src/third_party/utils/json.hpp | 22091 +++++++++++++++++++++++++++++++ 8 files changed, 22124 insertions(+), 99 deletions(-) create mode 100644 src/third_party/utils/json.hpp diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 29019ab9..cde85265 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -22,8 +22,6 @@ JSONNode::JSONNode(const JSONNode &t) { comm_src = t.comm_src; comm_dst = t.comm_dst; comm_tag = t.comm_tag; - involved_dim_size = t.involved_dim_size; - involved_dim = t.involved_dim; } } @@ -56,9 +54,7 @@ JSONNode::JSONNode(json data, int32_t id) { try { runtime = data["workload_graph"][id]["runtime"]; } - catch (...) { - // std::cerr << "runtime not specified in ET" << std::endl; - } + catch (...) {} try { data_deps = data["workload_graph"][id]["data_deps"].get>(); } @@ -69,57 +65,31 @@ JSONNode::JSONNode(json data, int32_t id) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; } - catch (...) { - // std::cout << "tensor_size not specified in ET" <>(); - } - catch (...) { - // std::cout << "involved_dim not specified in ET" < involved_dim; std::vector data_deps{}; std::vector dep_unresolved_parent_ids_json{}; std::vector children_vec_json{}; @@ -63,8 +61,6 @@ class JSONNode { comm_src == other.comm_src && comm_dst == other.comm_dst && comm_tag == other.comm_tag && - involved_dim_size == other.involved_dim_size && - involved_dim == other.involved_dim && data_deps == other.data_deps && dep_unresolved_parent_ids_json == other.dep_unresolved_parent_ids_json && children_vec_json == other.children_vec_json && @@ -88,8 +84,6 @@ class JSONNode { comm_src = other.comm_src; comm_dst = other.comm_dst; comm_tag = other.comm_tag; - involved_dim_size = other.involved_dim_size; - involved_dim = other.involved_dim; data_deps = other.data_deps; dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; children_vec_json = other.children_vec_json; diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index a7117bf6..0fdc2052 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -12,8 +12,6 @@ WrapperNode::WrapperNode(const WrapperNode& t) { json_node_ = t.json_node_; data_ = t.data_; node_idx_ = t.node_idx_; - involved_dim_size_ = t.involved_dim_size_; - involved_dim_ = t.involved_dim_; push_back_queue_proto = t.push_back_queue_proto; push_back_queue_json = t.push_back_queue_json; dep_graph_json = t.dep_graph_json; @@ -483,32 +481,6 @@ int32_t WrapperNode::getCommTag() { } } -// Get involved dim size -int32_t WrapperNode::getInvolvedDimSize() { - switch (format_type_) { - case Protobuf: - return node_->involved_dim_size(); - case JSON: - return json_node_.involved_dim_size; - default: - std::cerr << "Error in getInvolvedDimSize()" << std::endl; - exit(-1); - } -} - -// Get involved dim -bool WrapperNode::getInvolvedDim(int i) { - switch (format_type_) { - case Protobuf: - return node_->involved_dim(i); - case JSON: - return json_node_.involved_dim[i]; - default: - std::cerr << "Error in getInvolvedDim()" << std::endl; - exit(-1); - } -} - // Check if has more nodes to issue bool WrapperNode::hasNodesToIssue() { switch (format_type_) { diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index 0b2b559e..fb1507f4 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -21,8 +21,6 @@ class WrapperNode { json data_; JSONNode json_node_; int64_t node_idx_ = -1; - int32_t involved_dim_size_ = 1; - std::vector involved_dim_; std::queue> push_back_queue_proto; std::queue push_back_queue_json; std::unordered_map dep_graph_json{}; @@ -70,8 +68,6 @@ class WrapperNode { int32_t getCommSrc(); int32_t getCommDst(); int32_t getCommTag(); - int32_t getInvolvedDimSize(); - bool getInvolvedDim(int i); bool hasNodesToIssue(); void lookupNode(int64_t node_id); void getChildren(std::vector>& childrenNodes); diff --git a/src/feeder/et_feeder.h b/src/feeder/et_feeder.h index 9510ed9b..61fb4c59 100644 --- a/src/feeder/et_feeder.h +++ b/src/feeder/et_feeder.h @@ -6,8 +6,8 @@ #include #include -#include "et_feeder/et_feeder_node.h" -#include "third_party/utils/protoio.hh" +#include "et_feeder_node.h" +#include "protoio.hh" namespace Chakra { struct CompareNodes : public std::binary_function< diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index 360e9cad..64b3db6b 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -1,4 +1,4 @@ -#include "et_feeder/et_feeder_node.h" +#include "et_feeder_node.h" using namespace std; using namespace Chakra; @@ -14,7 +14,7 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { const string& attr_name = attr.name(); if (attr_name == "is_cpu_op") { - this->is_cpu_op_ = static_cast(attr.int32_val()); + this->is_cpu_op_ = static_cast(attr.bool_val()); } else if (attr_name == "num_ops") { this->num_ops_ = static_cast(attr.int64_val()); } else if (attr_name == "tensor_size") { @@ -22,12 +22,6 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { } else if (attr_name == "comm_type") { this->comm_type_ = static_cast(attr.int64_val()); - } else if (attr_name == "involved_dim") { - this->involved_dim_.clear(); - for (const bool val : attr.bool_list().values()) { - this->involved_dim_.push_back(val); - } - this->involved_dim_size_ = this->involved_dim_.size(); } else if (attr_name == "comm_priority") { this->comm_priority_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_size") { @@ -77,6 +71,20 @@ void ETFeederNode::setDepUnresolvedParentIDs( dep_unresolved_parent_ids_ = dep_unresolved_parent_ids; } +const ChakraProtoMsg::AttributeProto& ETFeederNode::get_other_attr( + const string& attr_name) const { + if (this->has_other_attr(attr_name)) + return this->other_attrs_.at(attr_name); + throw std::runtime_error( + "Asked for attr \"" + attr_name + "\" from node " + + std::to_string(this->id_) + ", which do not exist"); +} + +bool ETFeederNode::has_other_attr(const string& attr_name) const { + const auto& item = this->other_attrs_.find(attr_name); + return item != this->other_attrs_.end(); +} + uint64_t ETFeederNode::id() { return id_; } @@ -113,14 +121,6 @@ ChakraProtoMsg::CollectiveCommType ETFeederNode::comm_type() { return comm_type_; } -uint32_t ETFeederNode::involved_dim_size() { - return involved_dim_size_; -} - -bool ETFeederNode::involved_dim(int i) { - return involved_dim_[i]; -} - uint32_t ETFeederNode::comm_priority() { return comm_priority_; } diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index b4e5c468..b0ccbfa2 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -5,7 +5,7 @@ #include #include -#include "et_def/et_def.pb.h" +#include "et_def.pb.h" namespace Chakra { @@ -20,6 +20,10 @@ class ETFeederNode { void setDepUnresolvedParentIDs( std::vector const& dep_unresolved_parent_ids); + const ChakraProtoMsg::AttributeProto& get_other_attr( + const std::string& attr_name) const; + bool has_other_attr(const std::string& attr_name) const; + uint64_t id(); std::string name(); bool is_cpu_op(); @@ -29,8 +33,6 @@ class ETFeederNode { uint32_t tensor_loc(); uint64_t tensor_size(); ChakraProtoMsg::CollectiveCommType comm_type(); - uint32_t involved_dim_size(); - bool involved_dim(int i); uint32_t comm_priority(); uint64_t comm_size(); uint32_t comm_src(); @@ -48,6 +50,8 @@ class ETFeederNode { std::unordered_set> children_set_{}; std::vector> children_vec_{}; std::vector dep_unresolved_parent_ids_{}; + std::unordered_map + other_attrs_{}; uint64_t id_; std::string name_; @@ -57,8 +61,6 @@ class ETFeederNode { uint32_t tensor_loc_; uint64_t tensor_size_; ChakraProtoMsg::CollectiveCommType comm_type_; - uint32_t involved_dim_size_; - std::vector involved_dim_; uint32_t comm_priority_; uint64_t comm_size_; uint32_t comm_src_; diff --git a/src/third_party/utils/json.hpp b/src/third_party/utils/json.hpp new file mode 100644 index 00000000..cb27e058 --- /dev/null +++ b/src/third_party/utils/json.hpp @@ -0,0 +1,22091 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.5 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2022 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/****************************************************************************\ + * Note on documentation: The source files contain links to the online * + * documentation of the public API at https://json.nlohmann.me. This URL * + * contains the most recent documentation and should also be applicable to * + * previous versions; documentation for deprecated functions is not * + * removed, but marked deprecated. See "Generate documentation" section in * + * file doc/README.md. * +\****************************************************************************/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 5 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +// #include + + +#include // declval, pair +// #include + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + +// #include + + +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) + #ifdef JSON_HAS_CPP_17 + #if defined(__cpp_lib_filesystem) + #define JSON_HAS_FILESYSTEM 1 + #elif defined(__cpp_lib_experimental_filesystem) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif !defined(__has_include) + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_FILESYSTEM 1 + #elif __has_include() + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 + #endif + + // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ + #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support + #if defined(__clang_major__) && __clang_major__ < 7 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support + #if defined(_MSC_VER) && _MSC_VER < 1940 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before iOS 13 + #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + + // no filesystem support before macOS Catalina + #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 + #undef JSON_HAS_FILESYSTEM + #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #endif + #endif +#endif + +#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM + #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 +#endif + +#ifndef JSON_HAS_FILESYSTEM + #define JSON_HAS_FILESYSTEM 0 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow disabling exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow overriding assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + + +// inspired from https://stackoverflow.com/a/26745591 +// allows to call any std function as if (e.g. with begin): +// using std::begin; begin(x); +// +// it allows using the detected idiom to retrieve the return type +// of such an expression +#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ + namespace detail { \ + using std::std_name; \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + } \ + \ + namespace detail2 { \ + struct std_name##_tag \ + { \ + }; \ + \ + template \ + std_name##_tag std_name(T&&...); \ + \ + template \ + using result_of_##std_name = decltype(std_name(std::declval()...)); \ + \ + template \ + struct would_call_std_##std_name \ + { \ + static constexpr auto const value = ::nlohmann::detail:: \ + is_detected_exact::value; \ + }; \ + } /* namespace detail2 */ \ + \ + template \ + struct would_call_std_##std_name : detail2::would_call_std_##std_name \ + { \ + } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/// @brief general exception of the @ref basic_json class +/// @sa https://json.nlohmann.me/api/basic_json/exception/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} // NOLINT(bugprone-throw-keyword-missing) + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/// @brief exception indicating a parse error +/// @sa https://json.nlohmann.me/api/basic_json/parse_error/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return {id_, pos.chars_read_total, w.c_str()}; + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return {id_, byte_, w.c_str()}; + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/// @brief exception indicating errors with iterators +/// @sa https://json.nlohmann.me/api/basic_json/invalid_iterator/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/// @brief exception indicating executing a member function with a wrong type +/// @sa https://json.nlohmann.me/api/basic_json/type_error/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating access out of the defined range +/// @sa https://json.nlohmann.me/api/basic_json/out_of_range/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/// @brief exception indicating other library errors +/// @sa https://json.nlohmann.me/api/basic_json/other_error/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return {id_, w.c_str()}; + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; // NOLINT(readability-redundant-declaration) + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include + + +// #include + + +#include // random_access_iterator_tag + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); +} // namespace nlohmann + +// #include + + +// #include + + +namespace nlohmann +{ +NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); +} // namespace nlohmann + +// #include + +// #include + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; + +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; + +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; + +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +template +struct is_range +{ + private: + using t_ref = typename std::add_lvalue_reference::type; + + using iterator = detected_t; + using sentinel = detected_t; + + // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator + // and https://en.cppreference.com/w/cpp/iterator/sentinel_for + // but reimplementing these would be too much work, as a lot of other concepts are used underneath + static constexpr auto is_iterator_begin = + is_iterator_traits>::value; + + public: + static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; +}; + +template +using iterator_t = enable_if_t::value, result_of_begin())>>; + +template +using range_value_t = value_type_t>>; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < + is_detected::value&& + is_iterator_traits>>::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 + !std::is_same>::value >> +{ + static constexpr bool value = + is_constructible>::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + !is_compatible_string_type::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_iterator_traits>>::value&& +is_detected::value&& +// special case for types like std::filesystem::path whose iterator's value_type are themselves +// c.f. https://github.com/nlohmann/json/pull/3073 +!std::is_same>::value&& + is_complete_type < + detected_t>::value >> +{ + using value_type = range_value_t; + + static constexpr bool value = + std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, + value_type >::value; +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template static one test( decltype(&C::capacity) ) ; + template static two test(...); + + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) +{ + return { { std::forward(j).at(Idx).template get()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +{ + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void from_json(const BasicJsonType& j, std_fs::path& p) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + p = *j.template get_ptr(); +} +#endif + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element +#include // move + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +#if JSON_HAS_EXPERIMENTAL_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::experimental::filesystem; +} // namespace nlohmann::detail +#elif JSON_HAS_FILESYSTEM +#include +namespace nlohmann::detail +{ +namespace std_fs = std::filesystem; +} // namespace nlohmann::detail +#endif + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +#if JSON_HAS_FILESYSTEM || JSON_HAS_EXPERIMENTAL_FILESYSTEM +template +void to_json(BasicJsonType& j, const std_fs::path& p) +{ + j = p.string(); +} +#endif + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ + +/// @sa https://json.nlohmann.me/api/adl_serializer/ +template +struct adl_serializer +{ + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /// @brief convert a JSON value to any value type + /// @sa https://json.nlohmann.me/api/adl_serializer/from_json/ + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + + /// @brief convert any value type to a JSON value + /// @sa https://json.nlohmann.me/api/adl_serializer/to_json/ + template + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} // namespace nlohmann + +// #include + + +#include // uint8_t, uint64_t +#include // tie +#include // move + +namespace nlohmann +{ + +/// @brief an internal type for a backed binary type +/// @sa https://json.nlohmann.me/api/byte_container_with_subtype/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + using container_type = BinaryType; + using subtype_type = std::uint64_t; + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/byte_container_with_subtype/ + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /// @brief sets the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/set_subtype/ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /// @brief return the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/subtype/ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : static_cast(-1); + } + + /// @brief return whether the value has a subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/has_subtype/ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /// @brief clears the binary subtype + /// @sa https://json.nlohmann.me/api/byte_container_with_subtype/clear_subtype/ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // uint8_t +#include // size_t +#include // hash + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move +#include // vector + +// #include + +// #include + + +#include // array +#include // size_t +#include // strlen +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#ifndef JSON_NO_IO + #include // FILE * + #include // istream +#endif // JSON_NO_IO + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +#ifndef JSON_NO_IO +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, e.g. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; +#endif // JSON_NO_IO + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) + {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + + return std::char_traits::eof(); + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitly cast +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief a floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string value was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string value. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary value was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary value. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != static_cast(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = static_cast(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result, so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + static_cast((std::snprintf)(cs.data(), cs.size(), "", static_cast(c))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{static_cast('t'), static_cast('r'), static_cast('u'), static_cast('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{static_cast('f'), static_cast('a'), static_cast('l'), static_cast('s'), static_cast('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{static_cast('n'), static_cast('u'), static_cast('l'), static_cast('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianness(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return whether parsing was successful + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(static_cast(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(static_cast(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or static_cast(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or static_cast(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != static_cast(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianness, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + static_cast((std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current))); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(static_cast(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(static_cast(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ + +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /// @brief create JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/json_pointer/ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/to_string/ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @brief return a string representation of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_string/ + operator std::string() const + { + return to_string(); + } + + /// @brief append another JSON pointer at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /// @brief append an unescaped reference token at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /// @brief append an array index at the end of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slasheq/ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /// @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /// @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(lhs) /= std::move(token); + } + + /// @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/operator_slash/ + friend json_pointer operator/(const json_pointer& lhs, std::size_t array_idx) + { + return json_pointer(lhs) /= array_idx; + } + + /// @brief returns the parent of this JSON pointer + /// @sa https://json.nlohmann.me/api/json_pointer/parent_pointer/ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /// @brief remove last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/pop_back/ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /// @brief return last reference token + /// @sa https://json.nlohmann.me/api/json_pointer/back/ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @brief append an unescaped token at the end of the reference pointer + /// @sa https://json.nlohmann.me/api/json_pointer/push_back/ + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /// @brief return whether pointer points to the root document + /// @sa https://json.nlohmann.me/api/json_pointer/empty/ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template> +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + template> + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast(j); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), static_cast(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? static_cast(value.subtype()) : static_cast(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), static_cast(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianness, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianness + const bool is_little_endian = little_endianness(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // setfill, setw +#include // stringstream +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes floats to doubles, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint))); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + static_cast((std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu)))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (byte | 0); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::stringstream ss; + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << (static_cast(s.back()) | 0); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + ss.str(), BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + // templates to avoid warnings about useless casts + template ::value, int> = 0> + bool is_negative_number(NumberType x) + { + return x < 0; + } + + template < typename NumberType, enable_if_t ::value, int > = 0 > + bool is_negative_number(NumberType /*unused*/) + { + return false; + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative_number(x)) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward, + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::remove returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto end = std::remove(number_buffer.begin(), number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + // NOLINTNEXTLINE(readability-qualified-auto,llvm-qualified-auto): std::find returns an iterator, see https://github.com/nlohmann/json/issues/3081 + const auto dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if we need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using iterator = typename Container::iterator; + using const_iterator = typename Container::const_iterator; + using size_type = typename Container::size_type; + using value_type = typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + return erase(pos, std::next(pos)); + } + + iterator erase(iterator first, iterator last) + { + const auto elements_affected = std::distance(first, last); + const auto offset = std::distance(Container::begin(), first); + + // This is the start situation. We need to delete elements_affected + // elements (3 in this example: e, f, g), and need to return an + // iterator past the last deleted element (h in this example). + // Note that offset is the distance from the start of the vector + // to first. We will need this later. + + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // first last + + // Since we cannot move const Keys, we re-construct them in place. + // We start at first and re-construct (viz. copy) the elements from + // the back of the vector. Example for first iteration: + + // ,--------. + // v | destroy e and re-construct with h + // [ a, b, c, d, e, f, g, h, i, j ] + // ^ ^ + // it it + elements_affected + + for (auto it = first; std::next(it, elements_affected) != Container::end(); ++it) + { + it->~value_type(); // destroy but keep allocation + new (&*it) value_type{std::move(*std::next(it, elements_affected))}; // "move" next element to it + } + + // [ a, b, c, d, h, i, j, h, i, j ] + // ^ ^ + // first last + + // remove the unneeded elements at the end of the vector + Container::resize(this->size() - static_cast(elements_affected)); + + // [ a, b, c, d, h, i, j ] + // ^ ^ + // first last + + // first is now pointing past the last deleted element, but we cannot + // use this iterator, because it may have been invalidated by the + // resize call. Instead, we can return begin() + offset. + return Container::begin() + offset; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } +}; + +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@internal +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + using exception = detail::exception; + using parse_error = detail::parse_error; + using invalid_iterator = detail::invalid_iterator; + using type_error = detail::type_error; + using out_of_range = detail::out_of_range; + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /// @brief returns the allocator associated with the container + /// @sa https://json.nlohmann.me/api/basic_json/get_allocator/ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /// @brief returns version information on the library + /// @sa https://json.nlohmann.me/api/basic_json/meta/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2022 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + + /// @brief object key comparator type + /// @sa https://json.nlohmann.me/api/basic_json/object_comparator_t/ +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /// @brief a type for an object + /// @sa https://json.nlohmann.me/api/basic_json/object_t/ + using object_t = ObjectType>>; + + /// @brief a type for an array + /// @sa https://json.nlohmann.me/api/basic_json/array_t/ + using array_t = ArrayType>; + + /// @brief a type for a string + /// @sa https://json.nlohmann.me/api/basic_json/string_t/ + using string_t = StringType; + + /// @brief a type for a boolean + /// @sa https://json.nlohmann.me/api/basic_json/boolean_t/ + using boolean_t = BooleanType; + + /// @brief a type for a number (integer) + /// @sa https://json.nlohmann.me/api/basic_json/number_integer_t/ + using number_integer_t = NumberIntegerType; + + /// @brief a type for a number (unsigned) + /// @sa https://json.nlohmann.me/api/basic_json/number_unsigned_t/ + using number_unsigned_t = NumberUnsignedType; + + /// @brief a type for a number (floating-point) + /// @sa https://json.nlohmann.me/api/basic_json/number_float_t/ + using number_float_t = NumberFloatType; + + /// @brief a type for a packed binary type + /// @sa https://json.nlohmann.me/api/basic_json/binary_t/ + using binary_t = nlohmann::byte_container_with_subtype; + + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = static_cast(false); + break; + } + + case value_t::number_integer: + { + number_integer = static_cast(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = static_cast(0); + break; + } + + case value_t::number_float: + { + number_float = static_cast(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.5", basic_json())); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) : string(create(value)) {} + + /// constructor for rvalue strings + json_value(string_t&& value) : string(create(std::move(value))) {} + + /// constructor for objects + json_value(const object_t& value) : object(create(value)) {} + + /// constructor for rvalue objects + json_value(object_t&& value) : object(create(std::move(value))) {} + + /// constructor for arrays + json_value(const array_t& value) : array(create(value)) {} + + /// constructor for rvalue arrays + json_value(array_t&& value) : array(create(std::move(value))) {} + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) : binary(create(std::move(value))) {} + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) : binary(create(value)) {} + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) : binary(create(std::move(value))) {} + + void destroy(value_t t) + { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count_set_parents) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count_set_parents; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count_set_parents); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = static_cast(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != static_cast(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /// @brief parser event types + /// @sa https://json.nlohmann.me/api/basic_json/parse_event_t/ + using parse_event_t = detail::parse_event_t; + + /// @brief per-element parser callback type + /// @sa https://json.nlohmann.me/api/basic_json/parser_callback_t/ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /// @brief create an empty value with a given type + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /// @brief create a null object + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /// @brief create a JSON value from compatible types + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /// @brief create a JSON value from an existing one + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + set_parents(); + assert_invariant(); + } + + /// @brief create a container (array or object) from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /// @brief explicitly create a binary array (without subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @brief explicitly create a binary array + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @brief explicitly create a binary array (with subtype) + /// @sa https://json.nlohmann.me/api/basic_json/binary/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /// @brief explicitly create an array from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/array/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /// @brief explicitly create an object from an initializer list + /// @sa https://json.nlohmann.me/api/basic_json/object/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /// @brief construct an array with count copies of given value + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + set_parents(); + assert_invariant(); + } + + /// @brief construct a JSON container given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /// @brief copy constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /// @brief move constructor + /// @sa https://json.nlohmann.me/api/basic_json/basic_json/ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /// @brief copy assignment + /// @sa https://json.nlohmann.me/api/basic_json/operator=/ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + set_parents(); + assert_invariant(); + return *this; + } + + /// @brief destructor + /// @sa https://json.nlohmann.me/api/basic_json/~basic_json/ + ~basic_json() noexcept + { + assert_invariant(false); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /// @brief serialization + /// @sa https://json.nlohmann.me/api/basic_json/dump/ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /// @brief return the type of the JSON value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/type/ + constexpr value_t type() const noexcept + { + return m_type; + } + + /// @brief return whether type is primitive + /// @sa https://json.nlohmann.me/api/basic_json/is_primitive/ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /// @brief return whether type is structured + /// @sa https://json.nlohmann.me/api/basic_json/is_structured/ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /// @brief return whether value is null + /// @sa https://json.nlohmann.me/api/basic_json/is_null/ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /// @brief return whether value is a boolean + /// @sa https://json.nlohmann.me/api/basic_json/is_boolean/ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /// @brief return whether value is a number + /// @sa https://json.nlohmann.me/api/basic_json/is_number/ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /// @brief return whether value is an integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_integer/ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /// @brief return whether value is an unsigned integer number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_unsigned/ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /// @brief return whether value is a floating-point number + /// @sa https://json.nlohmann.me/api/basic_json/is_number_float/ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /// @brief return whether value is an object + /// @sa https://json.nlohmann.me/api/basic_json/is_object/ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /// @brief return whether value is an array + /// @sa https://json.nlohmann.me/api/basic_json/is_array/ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /// @brief return whether value is a string + /// @sa https://json.nlohmann.me/api/basic_json/is_string/ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /// @brief return whether value is a binary array + /// @sa https://json.nlohmann.me/api/basic_json/is_binary/ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /// @brief return whether value is discarded + /// @sa https://json.nlohmann.me/api/basic_json/is_discarded/ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /// @brief return the type of the JSON value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/operator_value_t/ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /// @brief get a pointer value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ptr/ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + auto ret = ValueType(); + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /// @brief get a value (explicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_to/ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow calling get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /// @brief get a reference value (implicit) + /// @sa https://json.nlohmann.me/api/basic_json/get_ref/ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, + +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @brief get a binary value + /// @sa https://json.nlohmann.me/api/basic_json/get_binary/ + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return set_parent(m_value.object->at(key)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified object element with bounds checking + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size & capacity before resizing + const auto old_size = m_value.array->size(); + const auto old_capacity = m_value.array->capacity(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + } + else + { + // set parent for values added above + set_parents(begin() + static_cast(old_size), static_cast(idx + 1 - old_size)); + } +#endif + assert_invariant(); + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified array element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /// @brief access specified object element via JSON Pointer with default value + /// @sa https://json.nlohmann.me/api/basic_json/value/ + /// overload for a default value of type const char* + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + reference front() + { + return *begin(); + } + + /// @brief access the first element + /// @sa https://json.nlohmann.me/api/basic_json/front/ + const_reference front() const + { + return *cbegin(); + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /// @brief access the last element + /// @sa https://json.nlohmann.me/api/basic_json/back/ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /// @brief remove element given an iterator + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove elements given an iterator range + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /// @brief remove element from a JSON object given a key + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + /// @brief remove element from a JSON array given an index + /// @sa https://json.nlohmann.me/api/basic_json/erase/ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief find an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/find/ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /// @brief returns the number of occurrences of a key in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/count/ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /// @brief check the existence of an element in a JSON object + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /// @brief check the existence of an element in a JSON object given a JSON pointer + /// @sa https://json.nlohmann.me/api/basic_json/contains/ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/begin/ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /// @brief returns a const iterator to the first element + /// @sa https://json.nlohmann.me/api/basic_json/cbegin/ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/end/ + const_iterator end() const noexcept + { + return cend(); + } + + /// @brief returns an iterator to one past the last element + /// @sa https://json.nlohmann.me/api/basic_json/cend/ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /// @brief returns an iterator to the reverse-beginning + /// @sa https://json.nlohmann.me/api/basic_json/rbegin/ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /// @brief returns an iterator to the reverse-end + /// @sa https://json.nlohmann.me/api/basic_json/rend/ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /// @brief returns a const reverse iterator to the last element + /// @sa https://json.nlohmann.me/api/basic_json/crbegin/ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /// @brief returns a const reverse iterator to one before the first + /// @sa https://json.nlohmann.me/api/basic_json/crend/ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /// @brief wrapper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + /// @deprecated This function is deprecated since 3.1.0 and will be removed in + /// version 4.0.0 of the library. Please use @ref items() instead; + /// that is, replace `json::iterator_wrapper(j)` with `j.items()`. + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /// @brief helper to access iterator member functions in range-based for + /// @sa https://json.nlohmann.me/api/basic_json/items/ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /// @brief checks whether the container is empty. + /// @sa https://json.nlohmann.me/api/basic_json/empty/ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /// @brief returns the number of elements + /// @sa https://json.nlohmann.me/api/basic_json/size/ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /// @brief returns the maximum possible number of elements + /// @sa https://json.nlohmann.me/api/basic_json/max_size/ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /// @brief clears the contents + /// @sa https://json.nlohmann.me/api/basic_json/clear/ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null, so we do not call the destructor + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/push_back/ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /// @brief add an object to an object + /// @sa https://json.nlohmann.me/api/basic_json/operator+=/ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /// @brief add an object to an array + /// @sa https://json.nlohmann.me/api/basic_json/emplace_back/ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->back(), old_capacity); + } + + /// @brief add an object to an object if key does not exist + /// @sa https://json.nlohmann.me/api/basic_json/emplace/ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /// @brief inserts copies of element into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /// @brief inserts range of elements into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /// @brief inserts elements from initializer list into array + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /// @brief inserts range of elements into object + /// @sa https://json.nlohmann.me/api/basic_json/insert/ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_reference j, bool merge_objects = false) + { + update(j.begin(), j.end(), merge_objects); + } + + /// @brief updates a JSON object from another object, overwriting existing keys + /// @sa https://json.nlohmann.me/api/basic_json/update/ + void update(const_iterator first, const_iterator last, bool merge_objects = false) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(first.m_object->type_name()), *first.m_object)); + } + + for (auto it = first; it != last; ++it) + { + if (merge_objects && it.value().is_object()) + { + auto it2 = m_value.object->find(it.key()); + if (it2 != m_value.object->end()) + { + it2->second.update(it.value(), true); + continue; + } + } + m_value.object->operator[](it.key()) = it.value(); +#if JSON_DIAGNOSTICS + m_value.object->operator[](it.key()).m_parent = this; +#endif + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @brief exchanges the values + /// @sa https://json.nlohmann.me/api/basic_json/swap/ + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /// @brief comparison: equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_eq/ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /// @brief comparison: not equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ne/ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /// @brief comparison: less than + /// @sa https://json.nlohmann.me/api/basic_json/operator_lt/ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /// @brief comparison: less than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_le/ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /// @brief comparison: greater than + /// @sa https://json.nlohmann.me/api/basic_json/operator_gt/ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /// @brief comparison: greater than or equal + /// @sa https://json.nlohmann.me/api/basic_json/operator_ge/ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /// @brief serialize to stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_ltlt/ + /// @deprecated This function is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator<<(std::ostream&, const basic_json&) instead; that is, + /// replace calls like `j >> o;` with `o << j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /// @brief deserialize from a compatible input + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief deserialize from a pair of character iterators + /// @sa https://json.nlohmann.me/api/basic_json/parse/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + /// @brief check if the input is valid JSON + /// @sa https://json.nlohmann.me/api/basic_json/accept/ + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + /// @brief generate SAX events + /// @sa https://json.nlohmann.me/api/basic_json/sax_parse/ + /// @deprecated This function is deprecated since 3.8.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// sax_parse(ptr, ptr + len) instead. + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + /// @deprecated This stream operator is deprecated since 3.0.0 and will be removed in + /// version 4.0.0 of the library. Please use + /// operator>>(std::istream&, basic_json&) instead; that is, + /// replace calls like `j << i;` with `i >> j;`. + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /// @brief deserialize from stream + /// @sa https://json.nlohmann.me/api/basic_json/operator_gtgt/ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /// @brief return the type as string + /// @sa https://json.nlohmann.me/api/basic_json/type_name/ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a CBOR serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_cbor/ + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a MessagePack serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_msgpack/ + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a UBJSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_ubjson/ + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a BSON serialization of a given JSON value + /// @sa https://json.nlohmann.me/api/basic_json/to_bson/ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in CBOR format + /// @sa https://json.nlohmann.me/api/basic_json/from_cbor/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in MessagePack format + /// @sa https://json.nlohmann.me/api/basic_json/from_msgpack/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in UBJSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_ubjson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /// @brief create a JSON value from an input in BSON format + /// @sa https://json.nlohmann.me/api/basic_json/from_bson/ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/operator%5B%5D/ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /// @brief access specified element via JSON Pointer + /// @sa https://json.nlohmann.me/api/basic_json/at/ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /// @brief return flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/flatten/ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /// @brief unflatten a previously flattened JSON value + /// @sa https://json.nlohmann.me/api/basic_json/unflatten/ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /// @brief applies a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/patch/ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, &result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + } + } + } + + return result; + } + + /// @brief creates a diff as a JSON patch + /// @sa https://json.nlohmann.me/api/basic_json/diff/ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // We now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /// @brief applies a JSON Merge Patch + /// @sa https://json.nlohmann.me/api/basic_json/merge_patch/ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/// @brief user-defined to_string function for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/to_string/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} + +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +namespace std // NOLINT(cert-dcl58-cpp) +{ + +/// @brief hash value for JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_hash/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct hash +{ + std::size_t operator()(const nlohmann::NLOHMANN_BASIC_JSON_TPL& j) const + { + return nlohmann::detail::hash(j); + } +}; + +// specialization for std::less +template<> +struct less< ::nlohmann::detail::value_t> // do not remove the space after '<', see https://github.com/nlohmann/json/pull/679 +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/// @brief exchanges the values of two JSON objects +/// @sa https://json.nlohmann.me/api/basic_json/std_swap/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +inline void swap(nlohmann::NLOHMANN_BASIC_JSON_TPL& j1, nlohmann::NLOHMANN_BASIC_JSON_TPL& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable::value) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef JSON_HAS_FILESYSTEM +#undef JSON_HAS_EXPERIMENTAL_FILESYSTEM +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT +#undef NLOHMANN_CAN_CALL_STD_FUNC_IMPL + +// #include + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ From 8e311bcf7cb1afd44efafb6795ee6d958432f576 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 1 Aug 2024 12:51:44 -0400 Subject: [PATCH 03/38] adding json test data --- tests/data/jsonData.tar.gz | Bin 0 -> 128189 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/data/jsonData.tar.gz diff --git a/tests/data/jsonData.tar.gz b/tests/data/jsonData.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..4f35d74fd52771420d9aac743d9d12e3502be931 GIT binary patch literal 128189 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 VP?L8Hcj6*+LHzyc4~QNg{15U6(f0rV literal 0 HcmV?d00001 From 6054b0bf4380d7ff4e4e45d3c2ff33851019899b Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 2 Aug 2024 11:46:26 -0400 Subject: [PATCH 04/38] adding wrapper tests --- tests/feeder/tests.cpp | 2 +- tests/feeder/wrapperTests.cpp | 168 ++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 tests/feeder/wrapperTests.cpp diff --git a/tests/feeder/tests.cpp b/tests/feeder/tests.cpp index 8fa659c2..04a61af2 100644 --- a/tests/feeder/tests.cpp +++ b/tests/feeder/tests.cpp @@ -136,4 +136,4 @@ TEST_F(ETFeederTest, NodeGetChildrenTest) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} +} \ No newline at end of file diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapperTests.cpp new file mode 100644 index 00000000..35eb5c79 --- /dev/null +++ b/tests/feeder/wrapperTests.cpp @@ -0,0 +1,168 @@ +#include +#include "WrapperNode.h" + +class WrapperNodeTest : public ::testing::Test { + protected: + WrapperNodeTest() {} + virtual ~WrapperNodeTest() {} + + void SetUp(const std::string& filename) { + node.createWrapper(filename); + } + + virtual void TearDown() { + node.releaseMemory(); + } + + WrapperNode node; +}; + +TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { + SetUp("tests/data/small_chakra.0.json"); + node.getNextIssuableNode(); + int64_t firstNodeID = node.getNodeID(); + ASSERT_EQ(firstNodeID, 216); + + node.getNextIssuableNode(); + int64_t secondNodeID = node.getNodeID(); + ASSERT_EQ(secondNodeID, 432); +} + +TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { + SetUp("tests/data/small_chakra.0.json"); + node.getNextIssuableNode(); + int64_t firstNodeType = node.getNodeType(); + ASSERT_EQ(firstNodeType, ChakraProtoMsg::COMP_NODE); + ASSERT_TRUE(node.is_cpu_op()); + + node.getNextIssuableNode(); + int64_t secondNodeType = node.getNodeType(); + ASSERT_EQ(secondNodeType, ChakraProtoMsg::COMM_COLL_NODE); + ASSERT_TRUE(node.is_cpu_op()); +} + +TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { + std::string filename = "tests/data/small_chakra.0.json"; + std::string ext = filename.substr(filename.find_last_of(".") + 1); + SetUp(filename); + node.getNextIssuableNode(); + if (ext == "et") { + std::vector> children; + node.getChildren(children); + ASSERT_EQ(children[0]->id(), 217); + ASSERT_EQ(children[1]->id(), 430); + ASSERT_EQ(children[2]->id(), 435); + } + else if (ext == "json") { + std::vector children; + node.getChildren(children); + ASSERT_EQ(children[0].node_id, 217); + ASSERT_EQ(children[1].node_id, 430); + ASSERT_EQ(children[2].node_id, 435); + } +} + +TEST_F(WrapperNodeTest, RemoveTest) { + SetUp("tests/data/small_chakra.0.json"); + node.lookupNode(216); + ASSERT_EQ(node.getNodeID(), 216); + node.removeNode(216); + freopen("/dev/null", "w", stderr); + try { + node.lookupNode(216); + ASSERT_TRUE(false) << "node should be removed \n"; + } catch (const std::exception& e) { + // this is the desired behaviour + } + freopen("/dev/tty", "w", stderr); +} + +TEST_F(WrapperNodeTest, RemoveAndGetNextTest) { + SetUp("tests/data/small_chakra.0.json"); + node.lookupNode(216); + ASSERT_EQ(node.getNodeID(), 216); + node.removeNode(216); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 216); +} + +TEST_F(WrapperNodeTest, FreeChildrenTest) { + SetUp("tests/data/small_chakra.0.json"); + node.lookupNode(216); + ASSERT_EQ(node.getNodeID(), 216); + node.freeChildrenNodes(216); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 216); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 217); +} + +TEST_F(WrapperNodeTest, HasNodesToIssueTest) { + SetUp("tests/data/small_chakra.0.json"); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 216); + ASSERT_TRUE(node.hasNodesToIssue()); + node.removeNode(5); + ASSERT_TRUE(node.hasNodesToIssue()); +} + +TEST_F(WrapperNodeTest, PushBackIssuableNodeTest) { + SetUp("tests/data/small_chakra.0.json"); + node.pushBackIssuableNode(217); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 216); + node.getNextIssuableNode(); + ASSERT_EQ(node.getNodeID(), 217); +} + +TEST_F(WrapperNodeTest, AddNodeTest) { + std:string filename = "tests/data/small_chakra.0.json"; + std::string ext = filename.substr(filename.find_last_of(".") + 1); + SetUp(filename); + if (ext == "et") { + std::shared_ptr pnode1; + node.lookupNode(216); + pnode1 = node.node_; + node.removeNode(216); + node.addNode(pnode1); + std::shared_ptr pnode2; + node.lookupNode(216); + pnode2 = node.node_; + ASSERT_EQ(pnode2.getNodeID(), 216); + } + else if (ext == "json") { + JSON jnode1; + node.lookupNode(216); + jnode1 = node.json_node_; + node.removeNode(216); + node.addNode(jnode1); + JSONNode jnode2; + node.lookupNode(216); + jnode2 = node.json_node_; + ASSERT_EQ(pnode2.node_id, 216); + } +} + +TEST_F(WrapperNodeTest, NodeGetChildrenTest) { + std:string filename = "tests/data/small_chakra.0.json"; + std::string ext = filename.substr(filename.find_last_of(".") + 1); + SetUp(filename); + node.lookupNode(216); + if (ext == "et") { + std::vector> children; + node.getChildren(children); + ASSERT_EQ(children[0]->id(), 217); + ASSERT_EQ(children[2]->id(), 435); + } + else if (ext == "json") { + std::vector children; + node.getChildren(children); + ASSERT_EQ(children[0].node_id, 217); + ASSERT_EQ(children[2].node_id, 435); + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From f93765fdab2184ff3cdba8e01cc939aedf006656 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Mon, 5 Aug 2024 17:00:09 -0400 Subject: [PATCH 05/38] code cleanup: make class members private --- src/feeder/JSONNode.cpp | 87 ++++++++++++++++++++++++++++++++++- src/feeder/JSONNode.h | 39 ++++++++++++---- src/feeder/WrapperNode.cpp | 79 ++++++++++++++++++++++--------- src/feeder/WrapperNode.h | 20 +++++--- tests/feeder/wrapperTests.cpp | 26 +++++------ 5 files changed, 202 insertions(+), 49 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index cde85265..4813160b 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -1,4 +1,4 @@ -#include "et_feeder/JSONNode.h" +#include "JSONNode.h" // JSONNode default constructor JSONNode::JSONNode() {} @@ -22,6 +22,8 @@ JSONNode::JSONNode(const JSONNode &t) { comm_src = t.comm_src; comm_dst = t.comm_dst; comm_tag = t.comm_tag; + involved_dim_size = t.involved_dim_size; + involved_dim = t.involved_dim; } } @@ -90,9 +92,92 @@ JSONNode::JSONNode(json data, int32_t id) { comm_tag = data["workload_graph"][id]["comm_tag"]; } catch (...) {} + try { + involved_dim_size = data["workload_graph"][id]["involved_dim_size"]; + } + catch (...) {} + try { + involved_dim = data["workload_graph"][id]["involved_dims"].get>(); + } + catch (...) {} } } +// Node id +int64_t JSONNode::id() const{ + return node_id; +} + +// Node name +std::string JSONNode::name() const{ + return node_name; +} + +// Node type +int JSONNode::type() const{ + return node_type; +} + +// Check if CPU OP +bool JSONNode::isCPUOp() const{ + return is_cpu_op; +} + +// Runtime +int64_t JSONNode::getRuntime() const{ + return runtime; +} + +// Num ops +int64_t JSONNode::getNumOps() const{ + return num_ops; +} + +// Tensor size +int64_t JSONNode::getTensorSize() const{ + return tensor_size; +} + +// Comm type +int64_t JSONNode::getCommType() const{ + return comm_type; +} + +// Comm priority +int32_t JSONNode::getCommPriority() const{ + return comm_priority; +} + +// Comm size +int64_t JSONNode::getCommSize() const{ + return comm_size; +} + +// Comm src +int32_t JSONNode::getCommSrc() const{ + return comm_src; +} + +// Comm dst +int32_t JSONNode::getCommDst() const{ + return comm_dst; +} + +// Comm tag +int32_t JSONNode::getCommTag() const{ + return comm_tag; +} + +// Involved dim size +int32_t JSONNode::getInvolvedDimSize() const{ + return involved_dim_size; +} + +// Involved dim +bool JSONNode::getInvolvedDim(int i) const{ + return involved_dim[i]; +} + // Dependency unresolved parent IDs void JSONNode::addDepUnresolvedParentID(int64_t node_id) { dep_unresolved_parent_ids_json.emplace_back(node_id); diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index 71ed1272..a7f7e870 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -1,6 +1,6 @@ #pragma once -#include +#include "json.hpp" #include #include #include @@ -11,7 +11,7 @@ using json = nlohmann::json; class JSONNode { - public: + private: int64_t node_id; std::string node_name; int node_type; @@ -25,6 +25,10 @@ class JSONNode { int32_t comm_src; int32_t comm_dst; int32_t comm_tag; + int32_t involved_dim_size; + std::vector involved_dim; + + public: std::vector data_deps{}; std::vector dep_unresolved_parent_ids_json{}; std::vector children_vec_json{}; @@ -40,6 +44,21 @@ class JSONNode { JSONNode(); JSONNode(const JSONNode &t); JSONNode(json data, int32_t id); + int64_t id() const; + std::string name() const; + int type() const; + bool isCPUOp() const; + int64_t getRuntime() const; + int64_t getNumOps() const; + int64_t getTensorSize() const; + int64_t getCommType() const; + int32_t getCommPriority() const; + int64_t getCommSize() const; + int32_t getCommSrc() const; + int32_t getCommDst() const; + int32_t getCommTag() const; + int32_t getInvolvedDimSize() const; + bool getInvolvedDim(int i) const; void addDepUnresolvedParentID(int64_t node_id); std::vector getDepUnresolvedParentIDs(); void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); @@ -61,6 +80,8 @@ class JSONNode { comm_src == other.comm_src && comm_dst == other.comm_dst && comm_tag == other.comm_tag && + involved_dim_size == other.involved_dim_size && + involved_dim == other.involved_dim && data_deps == other.data_deps && dep_unresolved_parent_ids_json == other.dep_unresolved_parent_ids_json && children_vec_json == other.children_vec_json && @@ -84,6 +105,8 @@ class JSONNode { comm_src = other.comm_src; comm_dst = other.comm_dst; comm_tag = other.comm_tag; + involved_dim_size = other.involved_dim_size; + involved_dim = other.involved_dim; data_deps = other.data_deps; dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; children_vec_json = other.children_vec_json; @@ -98,11 +121,11 @@ namespace std { template<> struct hash { std::size_t operator()(const JSONNode& node) const { - std::size_t h1 = std::hash()(node.node_id); - std::size_t h2 = std::hash()(node.node_name); - std::size_t h3 = std::hash()(node.node_type); - std::size_t h4 = std::hash()(node.is_cpu_op); - std::size_t h5 = std::hash()(node.runtime); + std::size_t h1 = std::hash()(node.id()); + std::size_t h2 = std::hash()(node.name()); + std::size_t h3 = std::hash()(node.type()); + std::size_t h4 = std::hash()(node.isCPUOp()); + std::size_t h5 = std::hash()(node.getRuntime()); // A prime number for bit manipulation const std::size_t prime = 31; @@ -127,6 +150,6 @@ struct CompareJSONNodesGT : public std::binary_function< bool operator()( const JSONNode lhs, const JSONNode rhs) const { - return lhs.node_id > rhs.node_id; + return lhs.id() > rhs.id(); } }; \ No newline at end of file diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index 0fdc2052..e4d6a1d2 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -9,9 +9,10 @@ WrapperNode::WrapperNode(const WrapperNode& t) { format_type_ = t.format_type_; et_feeder_ = t.et_feeder_; node_ = t.node_; - json_node_ = t.json_node_; data_ = t.data_; node_idx_ = t.node_idx_; + involved_dim_size_ = t.involved_dim_size_; + involved_dim_ = t.involved_dim_; push_back_queue_proto = t.push_back_queue_proto; push_back_queue_json = t.push_back_queue_json; dep_graph_json = t.dep_graph_json; @@ -45,6 +46,11 @@ void WrapperNode::createWrapper(std::string filename) { } } +// WrapperNode constructor +WrapperNode::WrapperNode(std::string filename) { + createWrapper(filename); +} + // Release memory void WrapperNode::releaseMemory() { switch(format_type_) { @@ -59,8 +65,13 @@ void WrapperNode::releaseMemory() { } } -WrapperNode::~WrapperNode() { - // releaseMemory(); +WrapperNode::~WrapperNode() {} + +std::shared_ptr WrapperNode::getProtobufNode() { + return node_; +} +JSONNode WrapperNode::getJSONNode() { + return json_node_; } // Find the index in JSON dictionary @@ -77,7 +88,7 @@ int64_t WrapperNode::findNodeIndexJSON(int64_t node_id) { // Overloaded function - addNode // Add JSON node to dependency graph void WrapperNode::addNode(JSONNode node) { - dep_graph_json[node.node_id] = node; + dep_graph_json[node.id()] = node; } // Add Protobuf node to dependency graph @@ -210,7 +221,7 @@ void WrapperNode::freeChildrenNodes(int64_t node_id) { } } if (child.data_deps.size() == 0) { - dep_free_node_id_set_json.emplace(child.node_id); + dep_free_node_id_set_json.emplace(child.id()); dep_free_node_queue_json.emplace(child); } } @@ -300,8 +311,8 @@ void WrapperNode::getNextIssuableNode() { case JSON: if (dep_free_node_queue_json.size() != 0) { json_node_ = dep_free_node_queue_json.top(); - node_idx_ = findNodeIndexJSON(json_node_.node_id); - dep_free_node_id_set_json.erase(json_node_.node_id); + node_idx_ = findNodeIndexJSON(json_node_.id()); + dep_free_node_id_set_json.erase(json_node_.id()); dep_free_node_queue_json.pop(); } else @@ -318,7 +329,7 @@ int64_t WrapperNode::getNodeID() { case Protobuf: return node_->id(); case JSON: - return json_node_.node_id; + return json_node_.id(); default: std::cerr << "Error in getNodeID()" << std::endl; exit(-1); @@ -331,7 +342,7 @@ std::string WrapperNode::getNodeName() { case Protobuf: return node_->name(); case JSON: - return json_node_.node_name; + return json_node_.name(); default: std::cerr << "Error in getNodeName()" << std::endl; exit(-1); @@ -344,7 +355,7 @@ int WrapperNode::getNodeType() { case Protobuf: return node_->type(); case JSON: - return json_node_.node_type; + return json_node_.type(); default: std::cerr << "Error in getNodeType()" << std::endl; exit(-1); @@ -357,7 +368,7 @@ bool WrapperNode::isCPUOp() { case Protobuf: return node_->is_cpu_op(); case JSON: - return json_node_.is_cpu_op; + return json_node_.isCPUOp(); default: std::cerr << "Error in isCPUOp()" << std::endl; exit(-1); @@ -370,7 +381,7 @@ int64_t WrapperNode::getRuntime() { case Protobuf: return node_->runtime(); case JSON: - return json_node_.runtime; + return json_node_.getRuntime(); default: std::cerr << "Error in getRuntime()" << std::endl; exit(-1); @@ -383,7 +394,7 @@ int64_t WrapperNode::getNumOps() { case Protobuf: return node_->num_ops(); case JSON: - return json_node_.num_ops; + return json_node_.getNumOps(); default: std::cerr << "Error in getNumOps()" << std::endl; exit(-1); @@ -396,7 +407,7 @@ int64_t WrapperNode::getTensorSize() { case Protobuf: return node_->tensor_size(); case JSON: - return json_node_.tensor_size; + return json_node_.getTensorSize(); default: std::cerr << "Error in getTensorSize()" << std::endl; exit(-1); @@ -409,7 +420,7 @@ int64_t WrapperNode::getCommType() { case Protobuf: return node_->comm_type(); case JSON: - return json_node_.comm_type; + return json_node_.getCommType(); default: std::cerr << "Error in getCommType()" << std::endl; exit(-1); @@ -422,7 +433,7 @@ int32_t WrapperNode::getCommPriority() { case Protobuf: return node_->comm_priority(); case JSON: - return json_node_.comm_priority; + return json_node_.getCommPriority(); default: std::cerr << "Error in getCommPriority()" << std::endl; exit(-1); @@ -435,7 +446,7 @@ int64_t WrapperNode::getCommSize() { case Protobuf: return node_->comm_size(); case JSON: - return json_node_.comm_size; + return json_node_.getCommSize(); default: std::cerr << "Error in getCommSize()" << std::endl; exit(-1); @@ -448,7 +459,7 @@ int32_t WrapperNode::getCommSrc() { case Protobuf: return node_->comm_src(); case JSON: - return json_node_.comm_src; + return json_node_.getCommSrc(); default: std::cerr << "Error in getCommSrc()" << std::endl; exit(-1); @@ -461,7 +472,7 @@ int32_t WrapperNode::getCommDst() { case Protobuf: return node_->comm_dst(); case JSON: - return json_node_.comm_dst; + return json_node_.getCommDst(); default: std::cerr << "Error in getCommDst()" << std::endl; exit(-1); @@ -474,13 +485,39 @@ int32_t WrapperNode::getCommTag() { case Protobuf: return node_->comm_tag(); case JSON: - return json_node_.comm_tag; + return json_node_.getCommTag(); default: std::cerr << "Error in getCommTag()" << std::endl; exit(-1); } } +// Get involved dim size +int32_t WrapperNode::getInvolvedDimSize() { + switch (format_type_) { + case Protobuf: + return node_->involved_dim_size(); + case JSON: + return json_node_.getInvolvedDimSize(); + default: + std::cerr << "Error in getInvolvedDimSize()" << std::endl; + exit(-1); + } +} + +// Get involved dim +bool WrapperNode::getInvolvedDim(int i) { + switch (format_type_) { + case Protobuf: + return node_->involved_dim(i); + case JSON: + return json_node_.getInvolvedDim(i); + default: + std::cerr << "Error in getInvolvedDim()" << std::endl; + exit(-1); + } +} + // Check if has more nodes to issue bool WrapperNode::hasNodesToIssue() { switch (format_type_) { @@ -523,4 +560,4 @@ void WrapperNode::getChildren(std::vector> // Overloaded function returns children JSON nodes void WrapperNode::getChildren(std::vector& childrenNodes) { childrenNodes = json_node_.getChildren(); -} +} \ No newline at end of file diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index fb1507f4..bb01ae22 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -1,8 +1,8 @@ #pragma once -#include "et_feeder/et_feeder.h" -#include "et_feeder/et_feeder_node.h" -#include "et_feeder/JSONNode.h" +#include "et_feeder.h" +#include "et_feeder_node.h" +#include "JSONNode.h" using json = nlohmann::json; @@ -13,7 +13,7 @@ enum format { // WrapperNode class wraps protobuf and JSON class WrapperNode { - public: + private: enum format format_type_; Chakra::ETFeeder* et_feeder_; std::shared_ptr node_ {nullptr}; @@ -21,6 +21,8 @@ class WrapperNode { json data_; JSONNode json_node_; int64_t node_idx_ = -1; + int32_t involved_dim_size_ = 1; + std::vector involved_dim_; std::queue> push_back_queue_proto; std::queue push_back_queue_json; std::unordered_map dep_graph_json{}; @@ -32,12 +34,16 @@ class WrapperNode { dep_free_node_queue_json{}; std::unordered_set> dep_unresolved_node_set_json{}; int window_size_json; - + + public: WrapperNode(); WrapperNode(const WrapperNode& t); + WrapperNode(std::string filename); ~WrapperNode(); void releaseMemory(); void createWrapper(std::string filename); + std::shared_ptr getProtobufNode(); + JSONNode getJSONNode(); void addNode(JSONNode node); void addNode(std::shared_ptr node); void removeNode(int64_t node_id); @@ -68,9 +74,11 @@ class WrapperNode { int32_t getCommSrc(); int32_t getCommDst(); int32_t getCommTag(); + int32_t getInvolvedDimSize(); + bool getInvolvedDim(int i); bool hasNodesToIssue(); void lookupNode(int64_t node_id); void getChildren(std::vector>& childrenNodes); void getChildren(std::vector& childrenNodes); int64_t findNodeIndexJSON(int64_t node_id); -}; +}; \ No newline at end of file diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapperTests.cpp index 35eb5c79..7df2bf4a 100644 --- a/tests/feeder/wrapperTests.cpp +++ b/tests/feeder/wrapperTests.cpp @@ -33,12 +33,12 @@ TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { node.getNextIssuableNode(); int64_t firstNodeType = node.getNodeType(); ASSERT_EQ(firstNodeType, ChakraProtoMsg::COMP_NODE); - ASSERT_TRUE(node.is_cpu_op()); + ASSERT_TRUE(node.isCPUOp()); node.getNextIssuableNode(); int64_t secondNodeType = node.getNodeType(); ASSERT_EQ(secondNodeType, ChakraProtoMsg::COMM_COLL_NODE); - ASSERT_TRUE(node.is_cpu_op()); + ASSERT_TRUE(node.isCPUOp()); } TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { @@ -56,9 +56,9 @@ TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { else if (ext == "json") { std::vector children; node.getChildren(children); - ASSERT_EQ(children[0].node_id, 217); - ASSERT_EQ(children[1].node_id, 430); - ASSERT_EQ(children[2].node_id, 435); + ASSERT_EQ(children[0].id(), 217); + ASSERT_EQ(children[1].id(), 430); + ASSERT_EQ(children[2].id(), 435); } } @@ -122,24 +122,24 @@ TEST_F(WrapperNodeTest, AddNodeTest) { if (ext == "et") { std::shared_ptr pnode1; node.lookupNode(216); - pnode1 = node.node_; + pnode1 = node.getProtobufNode(); node.removeNode(216); node.addNode(pnode1); std::shared_ptr pnode2; node.lookupNode(216); - pnode2 = node.node_; - ASSERT_EQ(pnode2.getNodeID(), 216); + pnode2 = node.getProtobufNode(); + ASSERT_EQ(pnode2->id(), 216); } else if (ext == "json") { JSON jnode1; node.lookupNode(216); - jnode1 = node.json_node_; + jnode1 = node.getJSONNode(); node.removeNode(216); node.addNode(jnode1); JSONNode jnode2; node.lookupNode(216); - jnode2 = node.json_node_; - ASSERT_EQ(pnode2.node_id, 216); + jnode2 = node.getJSONNode(); + ASSERT_EQ(jnode2.id(), 216); } } @@ -157,8 +157,8 @@ TEST_F(WrapperNodeTest, NodeGetChildrenTest) { else if (ext == "json") { std::vector children; node.getChildren(children); - ASSERT_EQ(children[0].node_id, 217); - ASSERT_EQ(children[2].node_id, 435); + ASSERT_EQ(children[0].id(), 217); + ASSERT_EQ(children[2].id(), 435); } } From 83519385982822438916cc204e332127863ab0f5 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Wed, 7 Aug 2024 13:30:16 -0400 Subject: [PATCH 06/38] Updating tests --- tests/feeder/wrapperTests.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapperTests.cpp index 7df2bf4a..b840a4fe 100644 --- a/tests/feeder/wrapperTests.cpp +++ b/tests/feeder/wrapperTests.cpp @@ -18,6 +18,7 @@ class WrapperNodeTest : public ::testing::Test { }; TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); int64_t firstNodeID = node.getNodeID(); @@ -29,6 +30,7 @@ TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { } TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); int64_t firstNodeType = node.getNodeType(); @@ -42,6 +44,7 @@ TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { } TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { + // std::string filename = "tests/data/chakra.0.et"; std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); @@ -63,6 +66,7 @@ TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { } TEST_F(WrapperNodeTest, RemoveTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -78,6 +82,7 @@ TEST_F(WrapperNodeTest, RemoveTest) { } TEST_F(WrapperNodeTest, RemoveAndGetNextTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -87,6 +92,7 @@ TEST_F(WrapperNodeTest, RemoveAndGetNextTest) { } TEST_F(WrapperNodeTest, FreeChildrenTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -98,6 +104,7 @@ TEST_F(WrapperNodeTest, FreeChildrenTest) { } TEST_F(WrapperNodeTest, HasNodesToIssueTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); ASSERT_EQ(node.getNodeID(), 216); @@ -107,6 +114,7 @@ TEST_F(WrapperNodeTest, HasNodesToIssueTest) { } TEST_F(WrapperNodeTest, PushBackIssuableNodeTest) { + // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.pushBackIssuableNode(217); node.getNextIssuableNode(); @@ -116,7 +124,8 @@ TEST_F(WrapperNodeTest, PushBackIssuableNodeTest) { } TEST_F(WrapperNodeTest, AddNodeTest) { - std:string filename = "tests/data/small_chakra.0.json"; + // std::string filename = "tests/data/chakra.0.et"; + std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); if (ext == "et") { @@ -144,7 +153,8 @@ TEST_F(WrapperNodeTest, AddNodeTest) { } TEST_F(WrapperNodeTest, NodeGetChildrenTest) { - std:string filename = "tests/data/small_chakra.0.json"; + // std::string filename = "tests/data/chakra.0.et"; + std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); node.lookupNode(216); From 97962532afa2e1751bfb741bac68f0bcd48b1dc8 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 8 Aug 2024 17:54:37 -0400 Subject: [PATCH 07/38] changing datatypes to match protobuf --- src/feeder/JSONNode.cpp | 24 +++++++++++++----------- src/feeder/JSONNode.h | 22 +++++++++++----------- src/feeder/WrapperNode.cpp | 35 +++++++++++++++++------------------ src/feeder/WrapperNode.h | 24 ++++++++++++------------ 4 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 4813160b..689ec448 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -14,6 +14,7 @@ JSONNode::JSONNode(const JSONNode &t) { dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; children_vec_json = t.children_vec_json; children_set_json = t.children_set_json; + // Comm nodes if (node_type == 5 || node_type == 6 || node_type == 7) { tensor_size = t.tensor_size; comm_type = t.comm_type; @@ -63,6 +64,7 @@ JSONNode::JSONNode(json data, int32_t id) { catch (...) { std::cerr << "data deps not specified in ET" << std::endl; } + // Comm nodes if (node_type == 5 || node_type == 6 || node_type == 7) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; @@ -104,7 +106,7 @@ JSONNode::JSONNode(json data, int32_t id) { } // Node id -int64_t JSONNode::id() const{ +uint64_t JSONNode::id() const{ return node_id; } @@ -124,52 +126,52 @@ bool JSONNode::isCPUOp() const{ } // Runtime -int64_t JSONNode::getRuntime() const{ +uint64_t JSONNode::getRuntime() const{ return runtime; } // Num ops -int64_t JSONNode::getNumOps() const{ +uint64_t JSONNode::getNumOps() const{ return num_ops; } // Tensor size -int64_t JSONNode::getTensorSize() const{ +uint64_t JSONNode::getTensorSize() const{ return tensor_size; } // Comm type -int64_t JSONNode::getCommType() const{ +uint64_t JSONNode::getCommType() const{ return comm_type; } // Comm priority -int32_t JSONNode::getCommPriority() const{ +uint32_t JSONNode::getCommPriority() const{ return comm_priority; } // Comm size -int64_t JSONNode::getCommSize() const{ +uint64_t JSONNode::getCommSize() const{ return comm_size; } // Comm src -int32_t JSONNode::getCommSrc() const{ +uint32_t JSONNode::getCommSrc() const{ return comm_src; } // Comm dst -int32_t JSONNode::getCommDst() const{ +uint32_t JSONNode::getCommDst() const{ return comm_dst; } // Comm tag -int32_t JSONNode::getCommTag() const{ +uint32_t JSONNode::getCommTag() const{ return comm_tag; } // Involved dim size -int32_t JSONNode::getInvolvedDimSize() const{ +uint32_t JSONNode::getInvolvedDimSize() const{ return involved_dim_size; } diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index a7f7e870..4054ba7b 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -12,20 +12,20 @@ using json = nlohmann::json; class JSONNode { private: - int64_t node_id; + uint64_t node_id; std::string node_name; int node_type; bool is_cpu_op; - int64_t runtime; - int64_t num_ops; - int64_t tensor_size; - int64_t comm_type; - int32_t comm_priority; - int64_t comm_size; - int32_t comm_src; - int32_t comm_dst; - int32_t comm_tag; - int32_t involved_dim_size; + uint64_t runtime; + uint64_t num_ops; + uint64_t tensor_size; + uint64_t comm_type; + uint32_t comm_priority; + uint64_t comm_size; + uint32_t comm_src; + uint32_t comm_dst; + uint32_t comm_tag; + uint32_t involved_dim_size; std::vector involved_dim; public: diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index e4d6a1d2..73c08bb2 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -75,7 +75,7 @@ JSONNode WrapperNode::getJSONNode() { } // Find the index in JSON dictionary -int64_t WrapperNode::findNodeIndexJSON(int64_t node_id) { +int64_t WrapperNode::findNodeIndexJSON(uint64_t node_id) { int64_t i; for (i=0; i < window_size_json; i++) { if (data_["workload_graph"][i]["Id"] == node_id) { @@ -97,7 +97,7 @@ void WrapperNode::addNode(std::shared_ptr node) { } // Remove node from dependency graph -void WrapperNode::removeNode(int64_t node_id) { +void WrapperNode::removeNode(uint64_t node_id) { switch(format_type_) { case Protobuf: et_feeder_->removeNode(node_id); @@ -112,7 +112,7 @@ void WrapperNode::removeNode(int64_t node_id) { // Read nodes in graph // node_idx is the continuous index of the JSON nodes and is different from node_id -JSONNode WrapperNode::readNode(int64_t node_idx) { +JSONNode WrapperNode::readNode(uint64_t node_idx) { JSONNode node(data_, node_idx); bool dep_unresolved = false; for (int i = 0; i < node.data_deps.size(); ++i) { @@ -191,7 +191,7 @@ void WrapperNode::resolveDep() { } // Push dependency free nodes -void WrapperNode::pushBackIssuableNode(int64_t node_id) { +void WrapperNode::pushBackIssuableNode(uint64_t node_id) { switch(format_type_) { case Protobuf: et_feeder_->pushBackIssuableNode(node_id); @@ -204,7 +204,7 @@ void WrapperNode::pushBackIssuableNode(int64_t node_id) { } // Free children -void WrapperNode::freeChildrenNodes(int64_t node_id) { +void WrapperNode::freeChildrenNodes(uint64_t node_id) { switch(format_type_) { case Protobuf: et_feeder_->freeChildrenNodes(node_id); @@ -255,7 +255,6 @@ void WrapperNode::push_to_queue() { push_back_queue_proto.push(node_); break; case JSON: - // JSONNode json_node(data_, node_idx_); push_back_queue_json.push(json_node_); break; } @@ -324,7 +323,7 @@ void WrapperNode::getNextIssuableNode() { } // Get node ID -int64_t WrapperNode::getNodeID() { +uint64_t WrapperNode::getNodeID() { switch (format_type_) { case Protobuf: return node_->id(); @@ -376,7 +375,7 @@ bool WrapperNode::isCPUOp() { } // Get runtime -int64_t WrapperNode::getRuntime() { +uint64_t WrapperNode::getRuntime() { switch (format_type_) { case Protobuf: return node_->runtime(); @@ -389,7 +388,7 @@ int64_t WrapperNode::getRuntime() { } // Get num ops -int64_t WrapperNode::getNumOps() { +uint64_t WrapperNode::getNumOps() { switch (format_type_) { case Protobuf: return node_->num_ops(); @@ -402,7 +401,7 @@ int64_t WrapperNode::getNumOps() { } // Get tensor size -int64_t WrapperNode::getTensorSize() { +uint64_t WrapperNode::getTensorSize() { switch (format_type_) { case Protobuf: return node_->tensor_size(); @@ -415,7 +414,7 @@ int64_t WrapperNode::getTensorSize() { } // Get comm type -int64_t WrapperNode::getCommType() { +uint64_t WrapperNode::getCommType() { switch (format_type_) { case Protobuf: return node_->comm_type(); @@ -428,7 +427,7 @@ int64_t WrapperNode::getCommType() { } // Get comm priority -int32_t WrapperNode::getCommPriority() { +uint32_t WrapperNode::getCommPriority() { switch (format_type_) { case Protobuf: return node_->comm_priority(); @@ -441,7 +440,7 @@ int32_t WrapperNode::getCommPriority() { } // Get comm size -int64_t WrapperNode::getCommSize() { +uint64_t WrapperNode::getCommSize() { switch (format_type_) { case Protobuf: return node_->comm_size(); @@ -454,7 +453,7 @@ int64_t WrapperNode::getCommSize() { } // Get comm src -int32_t WrapperNode::getCommSrc() { +uint32_t WrapperNode::getCommSrc() { switch (format_type_) { case Protobuf: return node_->comm_src(); @@ -467,7 +466,7 @@ int32_t WrapperNode::getCommSrc() { } // Get comm dst -int32_t WrapperNode::getCommDst() { +uint32_t WrapperNode::getCommDst() { switch (format_type_) { case Protobuf: return node_->comm_dst(); @@ -480,7 +479,7 @@ int32_t WrapperNode::getCommDst() { } // Get comm tag -int32_t WrapperNode::getCommTag() { +uint32_t WrapperNode::getCommTag() { switch (format_type_) { case Protobuf: return node_->comm_tag(); @@ -493,7 +492,7 @@ int32_t WrapperNode::getCommTag() { } // Get involved dim size -int32_t WrapperNode::getInvolvedDimSize() { +uint32_t WrapperNode::getInvolvedDimSize() { switch (format_type_) { case Protobuf: return node_->involved_dim_size(); @@ -532,7 +531,7 @@ bool WrapperNode::hasNodesToIssue() { } // Lookup Node -void WrapperNode::lookupNode(int64_t node_id) { +void WrapperNode::lookupNode(uint64_t node_id) { switch (format_type_) { case Protobuf: node_ = et_feeder_->lookupNode(node_id); diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index bb01ae22..41c1623f 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -21,7 +21,7 @@ class WrapperNode { json data_; JSONNode json_node_; int64_t node_idx_ = -1; - int32_t involved_dim_size_ = 1; + uint32_t involved_dim_size_ = 1; std::vector involved_dim_; std::queue> push_back_queue_proto; std::queue push_back_queue_json; @@ -61,20 +61,20 @@ class WrapperNode { void queue_front(); void pop_from_queue(); void getNextIssuableNode(); - int64_t getNodeID(); + uint64_t getNodeID(); std::string getNodeName(); int getNodeType(); bool isCPUOp(); - int64_t getRuntime(); - int64_t getNumOps(); - int64_t getTensorSize(); - int64_t getCommType(); - int32_t getCommPriority(); - int64_t getCommSize(); - int32_t getCommSrc(); - int32_t getCommDst(); - int32_t getCommTag(); - int32_t getInvolvedDimSize(); + uint64_t getRuntime(); + uint64_t getNumOps(); + uint64_t getTensorSize(); + uint64_t getCommType(); + uint32_t getCommPriority(); + uint64_t getCommSize(); + uint32_t getCommSrc(); + uint32_t getCommDst(); + uint32_t getCommTag(); + uint32_t getInvolvedDimSize(); bool getInvolvedDim(int i); bool hasNodesToIssue(); void lookupNode(int64_t node_id); From 0ce0ae29056166bdff9ea36e5871ee881b8f5d32 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 8 Aug 2024 18:27:13 -0400 Subject: [PATCH 08/38] adding missed datatype changes --- src/feeder/JSONNode.cpp | 20 ++++++++++++-------- src/feeder/JSONNode.h | 34 +++++++++++++++++----------------- src/feeder/WrapperNode.cpp | 9 ++++++--- src/feeder/WrapperNode.h | 21 +++++++++------------ 4 files changed, 44 insertions(+), 40 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 689ec448..451afba3 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -14,7 +14,9 @@ JSONNode::JSONNode(const JSONNode &t) { dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; children_vec_json = t.children_vec_json; children_set_json = t.children_set_json; - // Comm nodes + /* COMM_SEND_NODE = 5, + COMM_RECV_NODE = 6, + COMM_COLL_NODE = 7 */ if (node_type == 5 || node_type == 6 || node_type == 7) { tensor_size = t.tensor_size; comm_type = t.comm_type; @@ -29,7 +31,7 @@ JSONNode::JSONNode(const JSONNode &t) { } // JSONNode constructor -JSONNode::JSONNode(json data, int32_t id) { +JSONNode::JSONNode(json data, uint64_t id) { try { node_id = data["workload_graph"][id]["Id"]; } @@ -59,12 +61,14 @@ JSONNode::JSONNode(json data, int32_t id) { } catch (...) {} try { - data_deps = data["workload_graph"][id]["data_deps"].get>(); + data_deps = data["workload_graph"][id]["data_deps"].get>(); } catch (...) { std::cerr << "data deps not specified in ET" << std::endl; } - // Comm nodes + /* COMM_SEND_NODE = 5, + COMM_RECV_NODE = 6, + COMM_COLL_NODE = 7 */ if (node_type == 5 || node_type == 6 || node_type == 7) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; @@ -181,17 +185,17 @@ bool JSONNode::getInvolvedDim(int i) const{ } // Dependency unresolved parent IDs -void JSONNode::addDepUnresolvedParentID(int64_t node_id) { +void JSONNode::addDepUnresolvedParentID(uint64_t node_id) { dep_unresolved_parent_ids_json.emplace_back(node_id); } // Get dependency unresolved parent IDs -std::vector JSONNode::getDepUnresolvedParentIDs() { +std::vector JSONNode::getDepUnresolvedParentIDs() { return dep_unresolved_parent_ids_json; } // Set dependency unresolved parent IDs -void JSONNode::setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids) { +void JSONNode::setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids) { dep_unresolved_parent_ids_json = dep_unresolved_parent_ids; } @@ -209,4 +213,4 @@ void JSONNode::addChild(JSONNode node) { // Get children vector std::vector JSONNode::getChildren() { return children_vec_json; -} +} \ No newline at end of file diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index 4054ba7b..e92f3eb5 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -29,8 +29,8 @@ class JSONNode { std::vector involved_dim; public: - std::vector data_deps{}; - std::vector dep_unresolved_parent_ids_json{}; + std::vector data_deps{}; + std::vector dep_unresolved_parent_ids_json{}; std::vector children_vec_json{}; // Compare function for set @@ -43,25 +43,25 @@ class JSONNode { JSONNode(); JSONNode(const JSONNode &t); - JSONNode(json data, int32_t id); - int64_t id() const; + JSONNode(json data, uint64_t id); + uint64_t id() const; std::string name() const; int type() const; bool isCPUOp() const; - int64_t getRuntime() const; - int64_t getNumOps() const; - int64_t getTensorSize() const; - int64_t getCommType() const; - int32_t getCommPriority() const; - int64_t getCommSize() const; - int32_t getCommSrc() const; - int32_t getCommDst() const; - int32_t getCommTag() const; - int32_t getInvolvedDimSize() const; + uint64_t getRuntime() const; + uint64_t getNumOps() const; + uint64_t getTensorSize() const; + uint64_t getCommType() const; + uint32_t getCommPriority() const; + uint64_t getCommSize() const; + uint32_t getCommSrc() const; + uint32_t getCommDst() const; + uint32_t getCommTag() const; + uint32_t getInvolvedDimSize() const; bool getInvolvedDim(int i) const; - void addDepUnresolvedParentID(int64_t node_id); - std::vector getDepUnresolvedParentIDs(); - void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); + void addDepUnresolvedParentID(uint64_t node_id); + std::vector getDepUnresolvedParentIDs(); + void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); void addChild(JSONNode node); std::vector getChildren(); diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index 73c08bb2..fdda5224 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -67,9 +67,12 @@ void WrapperNode::releaseMemory() { WrapperNode::~WrapperNode() {} +// Return protobuf node std::shared_ptr WrapperNode::getProtobufNode() { return node_; } + +// Return JSON node JSONNode WrapperNode::getJSONNode() { return json_node_; } @@ -135,7 +138,7 @@ JSONNode WrapperNode::readNode(uint64_t node_idx) { // Read nodes in a window // For JSON, the entire graph is read in a single window void WrapperNode::readNextWindow() { - int32_t num_read = 0; + uint64_t num_read = 0; do { JSONNode new_node = readNode(num_read); addNode(new_node); @@ -144,7 +147,7 @@ void WrapperNode::readNextWindow() { } while ((num_read < window_size_json) || (dep_unresolved_node_set_json.size() != 0)); for (auto node_id_node : dep_graph_json) { - int64_t node_id = node_id_node.first; + uint64_t node_id = node_id_node.first; JSONNode node(node_id_node.second); // Unordered set does not allow duplicates. So, count returns 1 if key exists, 0 otherwise if ((dep_free_node_id_set_json.count(node_id) == 0) && @@ -166,7 +169,7 @@ void WrapperNode::resolveDep() { for (auto it = dep_unresolved_node_set_json.begin(); it != dep_unresolved_node_set_json.end();) { JSONNode node = *it; - std::vector dep_unresolved_parent_ids_json = + std::vector dep_unresolved_parent_ids_json = node.getDepUnresolvedParentIDs(); // Loop over unresolved parent IDs for (auto inner_it = dep_unresolved_parent_ids_json.begin(); diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index 41c1623f..f6349857 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -11,7 +11,7 @@ enum format { JSON }; -// WrapperNode class wraps protobuf and JSON +/// WrapperNode class wraps protobuf and JSON class WrapperNode { private: enum format format_type_; @@ -25,8 +25,8 @@ class WrapperNode { std::vector involved_dim_; std::queue> push_back_queue_proto; std::queue push_back_queue_json; - std::unordered_map dep_graph_json{}; - std::unordered_set dep_free_node_id_set_json{}; + std::unordered_map dep_graph_json{}; + std::unordered_set dep_free_node_id_set_json{}; std::priority_queue< JSONNode, //type of stored elements std::vector, // underlying container to store elements @@ -46,15 +46,12 @@ class WrapperNode { JSONNode getJSONNode(); void addNode(JSONNode node); void addNode(std::shared_ptr node); - void removeNode(int64_t node_id); + void removeNode(uint64_t node_id); void readNextWindow(); - JSONNode readNode(int64_t node_id); + JSONNode readNode(uint64_t node_id); void resolveDep(); - void pushBackIssuableNode(int64_t node_id); - void freeChildrenNodes(int64_t node_id); - void addDepUnresolvedParentID(int64_t node_id); - std::vector getDepUnresolvedParentIDs(); - void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); + void pushBackIssuableNode(uint64_t node_id); + void freeChildrenNodes(uint64_t node_id); bool isValidNode(); void push_to_queue(); bool is_queue_empty(); @@ -77,8 +74,8 @@ class WrapperNode { uint32_t getInvolvedDimSize(); bool getInvolvedDim(int i); bool hasNodesToIssue(); - void lookupNode(int64_t node_id); + void lookupNode(uint64_t node_id); void getChildren(std::vector>& childrenNodes); void getChildren(std::vector& childrenNodes); - int64_t findNodeIndexJSON(int64_t node_id); + int64_t findNodeIndexJSON(uint64_t node_id); }; \ No newline at end of file From 67d29cd35586a95d10a9825ae7eaa0d568239ed0 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 8 Aug 2024 18:29:17 -0400 Subject: [PATCH 09/38] updating WrapperTests datatypes --- tests/feeder/wrapperTests.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapperTests.cpp index b840a4fe..c9a95ae0 100644 --- a/tests/feeder/wrapperTests.cpp +++ b/tests/feeder/wrapperTests.cpp @@ -21,11 +21,11 @@ TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); - int64_t firstNodeID = node.getNodeID(); + uint64_t firstNodeID = node.getNodeID(); ASSERT_EQ(firstNodeID, 216); node.getNextIssuableNode(); - int64_t secondNodeID = node.getNodeID(); + uint64_t secondNodeID = node.getNodeID(); ASSERT_EQ(secondNodeID, 432); } @@ -33,12 +33,12 @@ TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); - int64_t firstNodeType = node.getNodeType(); + uint64_t firstNodeType = node.getNodeType(); ASSERT_EQ(firstNodeType, ChakraProtoMsg::COMP_NODE); ASSERT_TRUE(node.isCPUOp()); node.getNextIssuableNode(); - int64_t secondNodeType = node.getNodeType(); + uint64_t secondNodeType = node.getNodeType(); ASSERT_EQ(secondNodeType, ChakraProtoMsg::COMM_COLL_NODE); ASSERT_TRUE(node.isCPUOp()); } From 045bd6fb7af69873c944afe822dd0ee67c56963f Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 15 Aug 2024 13:11:32 -0400 Subject: [PATCH 10/38] removing involved dims and minor bug fix --- src/feeder/JSONNode.cpp | 20 ---- src/feeder/JSONNode.h | 6 -- src/feeder/WrapperNode.cpp | 214 +++++++++++++++++++++++++++++++------ src/feeder/WrapperNode.h | 4 - 4 files changed, 180 insertions(+), 64 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 451afba3..23d129ab 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -25,8 +25,6 @@ JSONNode::JSONNode(const JSONNode &t) { comm_src = t.comm_src; comm_dst = t.comm_dst; comm_tag = t.comm_tag; - involved_dim_size = t.involved_dim_size; - involved_dim = t.involved_dim; } } @@ -98,14 +96,6 @@ JSONNode::JSONNode(json data, uint64_t id) { comm_tag = data["workload_graph"][id]["comm_tag"]; } catch (...) {} - try { - involved_dim_size = data["workload_graph"][id]["involved_dim_size"]; - } - catch (...) {} - try { - involved_dim = data["workload_graph"][id]["involved_dims"].get>(); - } - catch (...) {} } } @@ -174,16 +164,6 @@ uint32_t JSONNode::getCommTag() const{ return comm_tag; } -// Involved dim size -uint32_t JSONNode::getInvolvedDimSize() const{ - return involved_dim_size; -} - -// Involved dim -bool JSONNode::getInvolvedDim(int i) const{ - return involved_dim[i]; -} - // Dependency unresolved parent IDs void JSONNode::addDepUnresolvedParentID(uint64_t node_id) { dep_unresolved_parent_ids_json.emplace_back(node_id); diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index e92f3eb5..501eaad0 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -57,8 +57,6 @@ class JSONNode { uint32_t getCommSrc() const; uint32_t getCommDst() const; uint32_t getCommTag() const; - uint32_t getInvolvedDimSize() const; - bool getInvolvedDim(int i) const; void addDepUnresolvedParentID(uint64_t node_id); std::vector getDepUnresolvedParentIDs(); void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); @@ -80,8 +78,6 @@ class JSONNode { comm_src == other.comm_src && comm_dst == other.comm_dst && comm_tag == other.comm_tag && - involved_dim_size == other.involved_dim_size && - involved_dim == other.involved_dim && data_deps == other.data_deps && dep_unresolved_parent_ids_json == other.dep_unresolved_parent_ids_json && children_vec_json == other.children_vec_json && @@ -105,8 +101,6 @@ class JSONNode { comm_src = other.comm_src; comm_dst = other.comm_dst; comm_tag = other.comm_tag; - involved_dim_size = other.involved_dim_size; - involved_dim = other.involved_dim; data_deps = other.data_deps; dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; children_vec_json = other.children_vec_json; diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index fdda5224..649f7113 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -11,8 +11,6 @@ WrapperNode::WrapperNode(const WrapperNode& t) { node_ = t.node_; data_ = t.data_; node_idx_ = t.node_idx_; - involved_dim_size_ = t.involved_dim_size_; - involved_dim_ = t.involved_dim_; push_back_queue_proto = t.push_back_queue_proto; push_back_queue_json = t.push_back_queue_json; dep_graph_json = t.dep_graph_json; @@ -55,13 +53,20 @@ WrapperNode::WrapperNode(std::string filename) { void WrapperNode::releaseMemory() { switch(format_type_) { case Protobuf: + { delete et_feeder_; break; + } case JSON: + { jsonfile_.close(); break; + } default: - break; + { + std::cerr << "Error in releaseMemory()" << std::endl; + exit(-1); + } } } @@ -103,13 +108,20 @@ void WrapperNode::addNode(std::shared_ptr node) { void WrapperNode::removeNode(uint64_t node_id) { switch(format_type_) { case Protobuf: + { et_feeder_->removeNode(node_id); break; + } case JSON: + { dep_graph_json.erase(node_id); break; + } default: - break; + { + std::cerr << "Error in removeNode()" << std::endl; + exit(-1); + } } } @@ -162,9 +174,12 @@ void WrapperNode::readNextWindow() { void WrapperNode::resolveDep() { switch(format_type_) { case Protobuf: + { et_feeder_->resolveDep(); break; + } case JSON: + { // Loop over unresolved nodes for (auto it = dep_unresolved_node_set_json.begin(); it != dep_unresolved_node_set_json.end();) { @@ -190,6 +205,12 @@ void WrapperNode::resolveDep() { ++it; } } + } + default: + { + std::cerr << "Error in resolveDep()" << std::endl; + exit(-1); + } } } @@ -197,12 +218,22 @@ void WrapperNode::resolveDep() { void WrapperNode::pushBackIssuableNode(uint64_t node_id) { switch(format_type_) { case Protobuf: + { et_feeder_->pushBackIssuableNode(node_id); break; + } case JSON: + { JSONNode node = dep_graph_json[node_id]; dep_free_node_id_set_json.emplace(node_id); dep_free_node_queue_json.emplace(node); + break; + } + default: + { + std::cerr << "Error in pushBackIssuableNode()" << std::endl; + exit(-1); + } } } @@ -210,9 +241,12 @@ void WrapperNode::pushBackIssuableNode(uint64_t node_id) { void WrapperNode::freeChildrenNodes(uint64_t node_id) { switch(format_type_) { case Protobuf: + { et_feeder_->freeChildrenNodes(node_id); break; + } case JSON: + { JSONNode node = dep_graph_json[node_id]; for (auto child : node.getChildren()) { for (auto it = child.data_deps.begin(); @@ -229,6 +263,12 @@ void WrapperNode::freeChildrenNodes(uint64_t node_id) { } } break; + } + default: + { + std::cerr << "Error in freeChildrenNodes()" << std::endl; + exit(-1); + } } } @@ -236,18 +276,24 @@ void WrapperNode::freeChildrenNodes(uint64_t node_id) { bool WrapperNode::isValidNode() { switch (format_type_) { case Protobuf: + { if (node_ == nullptr) return false; else return true; + } case JSON: + { if (node_idx_ < 0) return false; else return true; + } default: + { std::cerr << "Error in isValid()" << std::endl; exit(-1); + } } } @@ -255,11 +301,20 @@ bool WrapperNode::isValidNode() { void WrapperNode::push_to_queue() { switch (format_type_) { case Protobuf: + { push_back_queue_proto.push(node_); break; + } case JSON: + { push_back_queue_json.push(json_node_); break; + } + default: + { + std::cerr << "Error in push_to_queue()" << std::endl; + exit(-1); + } } } @@ -267,14 +322,18 @@ void WrapperNode::push_to_queue() { bool WrapperNode::is_queue_empty() { switch (format_type_) { case Protobuf: + { return push_back_queue_proto.empty(); - break; + } case JSON: + { return push_back_queue_json.empty(); - break; + } default: + { std::cerr << "Error in is_queue_empty()" << std::endl; exit(-1); + } } } @@ -282,12 +341,20 @@ bool WrapperNode::is_queue_empty() { void WrapperNode::queue_front() { switch (format_type_) { case Protobuf: + { node_ = push_back_queue_proto.front(); - case JSON: + break; + } + case JSON: + { json_node_ = push_back_queue_json.front(); + break; + } default: + { std::cerr << "Error in queue_front()" << std::endl; exit(-1); + } } } @@ -295,12 +362,20 @@ void WrapperNode::queue_front() { void WrapperNode::pop_from_queue() { switch (format_type_) { case Protobuf: + { push_back_queue_proto.pop(); + break; + } case JSON: + { push_back_queue_json.pop(); + break; + } default: + { std::cerr << "Error in pop_from_queue()" << std::endl; exit(-1); + } } } @@ -308,9 +383,12 @@ void WrapperNode::pop_from_queue() { void WrapperNode::getNextIssuableNode() { switch (format_type_) { case Protobuf: + { node_ = et_feeder_->getNextIssuableNode(); break; + } case JSON: + { if (dep_free_node_queue_json.size() != 0) { json_node_ = dep_free_node_queue_json.top(); node_idx_ = findNodeIndexJSON(json_node_.id()); @@ -320,8 +398,12 @@ void WrapperNode::getNextIssuableNode() { else node_idx_ = -1; break; + } default: - break; + { + std::cerr << "Error in getNextIssuableNode()" << std::endl; + exit(-1); + } } } @@ -329,12 +411,18 @@ void WrapperNode::getNextIssuableNode() { uint64_t WrapperNode::getNodeID() { switch (format_type_) { case Protobuf: + { return node_->id(); + } case JSON: + { return json_node_.id(); + } default: + { std::cerr << "Error in getNodeID()" << std::endl; exit(-1); + } } } @@ -342,12 +430,18 @@ uint64_t WrapperNode::getNodeID() { std::string WrapperNode::getNodeName() { switch (format_type_) { case Protobuf: + { return node_->name(); + } case JSON: + { return json_node_.name(); + } default: + { std::cerr << "Error in getNodeName()" << std::endl; exit(-1); + } } } @@ -355,12 +449,18 @@ std::string WrapperNode::getNodeName() { int WrapperNode::getNodeType() { switch (format_type_) { case Protobuf: + { return node_->type(); + } case JSON: + { return json_node_.type(); + } default: + { std::cerr << "Error in getNodeType()" << std::endl; exit(-1); + } } } @@ -368,12 +468,18 @@ int WrapperNode::getNodeType() { bool WrapperNode::isCPUOp() { switch (format_type_) { case Protobuf: + { return node_->is_cpu_op(); + } case JSON: + { return json_node_.isCPUOp(); + } default: + { std::cerr << "Error in isCPUOp()" << std::endl; exit(-1); + } } } @@ -381,12 +487,18 @@ bool WrapperNode::isCPUOp() { uint64_t WrapperNode::getRuntime() { switch (format_type_) { case Protobuf: + { return node_->runtime(); + } case JSON: + { return json_node_.getRuntime(); + } default: + { std::cerr << "Error in getRuntime()" << std::endl; exit(-1); + } } } @@ -394,12 +506,18 @@ uint64_t WrapperNode::getRuntime() { uint64_t WrapperNode::getNumOps() { switch (format_type_) { case Protobuf: + { return node_->num_ops(); + } case JSON: + { return json_node_.getNumOps(); + } default: + { std::cerr << "Error in getNumOps()" << std::endl; exit(-1); + } } } @@ -407,12 +525,18 @@ uint64_t WrapperNode::getNumOps() { uint64_t WrapperNode::getTensorSize() { switch (format_type_) { case Protobuf: + { return node_->tensor_size(); + } case JSON: + { return json_node_.getTensorSize(); + } default: + { std::cerr << "Error in getTensorSize()" << std::endl; exit(-1); + } } } @@ -420,12 +544,18 @@ uint64_t WrapperNode::getTensorSize() { uint64_t WrapperNode::getCommType() { switch (format_type_) { case Protobuf: + { return node_->comm_type(); + } case JSON: + { return json_node_.getCommType(); + } default: + { std::cerr << "Error in getCommType()" << std::endl; exit(-1); + } } } @@ -433,12 +563,18 @@ uint64_t WrapperNode::getCommType() { uint32_t WrapperNode::getCommPriority() { switch (format_type_) { case Protobuf: + { return node_->comm_priority(); + } case JSON: + { return json_node_.getCommPriority(); + } default: + { std::cerr << "Error in getCommPriority()" << std::endl; exit(-1); + } } } @@ -446,12 +582,18 @@ uint32_t WrapperNode::getCommPriority() { uint64_t WrapperNode::getCommSize() { switch (format_type_) { case Protobuf: + { return node_->comm_size(); + } case JSON: + { return json_node_.getCommSize(); + } default: + { std::cerr << "Error in getCommSize()" << std::endl; exit(-1); + } } } @@ -459,12 +601,18 @@ uint64_t WrapperNode::getCommSize() { uint32_t WrapperNode::getCommSrc() { switch (format_type_) { case Protobuf: + { return node_->comm_src(); + } case JSON: + { return json_node_.getCommSrc(); + } default: + { std::cerr << "Error in getCommSrc()" << std::endl; exit(-1); + } } } @@ -472,12 +620,18 @@ uint32_t WrapperNode::getCommSrc() { uint32_t WrapperNode::getCommDst() { switch (format_type_) { case Protobuf: + { return node_->comm_dst(); + } case JSON: + { return json_node_.getCommDst(); + } default: + { std::cerr << "Error in getCommDst()" << std::endl; exit(-1); + } } } @@ -485,38 +639,18 @@ uint32_t WrapperNode::getCommDst() { uint32_t WrapperNode::getCommTag() { switch (format_type_) { case Protobuf: + { return node_->comm_tag(); + } case JSON: + { return json_node_.getCommTag(); + } default: + { std::cerr << "Error in getCommTag()" << std::endl; exit(-1); - } -} - -// Get involved dim size -uint32_t WrapperNode::getInvolvedDimSize() { - switch (format_type_) { - case Protobuf: - return node_->involved_dim_size(); - case JSON: - return json_node_.getInvolvedDimSize(); - default: - std::cerr << "Error in getInvolvedDimSize()" << std::endl; - exit(-1); - } -} - -// Get involved dim -bool WrapperNode::getInvolvedDim(int i) { - switch (format_type_) { - case Protobuf: - return node_->involved_dim(i); - case JSON: - return json_node_.getInvolvedDim(i); - default: - std::cerr << "Error in getInvolvedDim()" << std::endl; - exit(-1); + } } } @@ -524,12 +658,18 @@ bool WrapperNode::getInvolvedDim(int i) { bool WrapperNode::hasNodesToIssue() { switch (format_type_) { case Protobuf: + { return et_feeder_->hasNodesToIssue(); + } case JSON: + { return !(dep_graph_json.empty() && dep_free_node_queue_json.empty()); + } default: + { std::cerr << "Error in hasNodesToIssue()" << std::endl; exit(-1); + } } } @@ -537,9 +677,12 @@ bool WrapperNode::hasNodesToIssue() { void WrapperNode::lookupNode(uint64_t node_id) { switch (format_type_) { case Protobuf: + { node_ = et_feeder_->lookupNode(node_id); break; + } case JSON: + { try { json_node_ = dep_graph_json.at(node_id); } catch (const std::out_of_range& e) { @@ -548,9 +691,12 @@ void WrapperNode::lookupNode(uint64_t node_id) { throw(e); } break; + } default: + { std::cerr << "Error in lookupNode()" << std::endl; exit(-1); + } } } diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index f6349857..9b808829 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -21,8 +21,6 @@ class WrapperNode { json data_; JSONNode json_node_; int64_t node_idx_ = -1; - uint32_t involved_dim_size_ = 1; - std::vector involved_dim_; std::queue> push_back_queue_proto; std::queue push_back_queue_json; std::unordered_map dep_graph_json{}; @@ -71,8 +69,6 @@ class WrapperNode { uint32_t getCommSrc(); uint32_t getCommDst(); uint32_t getCommTag(); - uint32_t getInvolvedDimSize(); - bool getInvolvedDim(int i); bool hasNodesToIssue(); void lookupNode(uint64_t node_id); void getChildren(std::vector>& childrenNodes); From e5a46274fc9c6028ead3745e13d3d024691b0d40 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 15 Aug 2024 13:16:18 -0400 Subject: [PATCH 11/38] fixing include path in et_feeder --- src/feeder/et_feeder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feeder/et_feeder.cpp b/src/feeder/et_feeder.cpp index 38b7efaa..ccd05368 100644 --- a/src/feeder/et_feeder.cpp +++ b/src/feeder/et_feeder.cpp @@ -1,4 +1,4 @@ -#include "et_feeder/et_feeder.h" +#include "et_feeder.h" #include From 46d42d65063e242328a83dac51c661592dd99542 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 15 Aug 2024 16:45:30 -0400 Subject: [PATCH 12/38] add missing break statement --- src/feeder/WrapperNode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index 649f7113..77be4702 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -205,6 +205,7 @@ void WrapperNode::resolveDep() { ++it; } } + break; } default: { From 69dffb13fa0b4460ccf0cf58f6d1f4c747a6de43 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Mon, 26 Aug 2024 12:38:34 -0400 Subject: [PATCH 13/38] minor bug fixes --- src/feeder/JSONNode.cpp | 6 ++++-- src/feeder/JSONNode.h | 8 +++----- src/feeder/WrapperNode.cpp | 12 ++++++++++-- src/feeder/WrapperNode.h | 3 ++- src/feeder/et_feeder_node.cpp | 10 +++++----- src/feeder/et_feeder_node.h | 7 ++++--- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 23d129ab..513b7dfb 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -79,7 +79,9 @@ JSONNode::JSONNode(json data, uint64_t id) { try { comm_priority = data["workload_graph"][id]["comm_priority"]; } - catch (...) {} + catch (...) { + comm_priority = 0; // Protobuf defaults to 0 + } try { comm_size = data["workload_graph"][id]["comm_size"]; } @@ -135,7 +137,7 @@ uint64_t JSONNode::getTensorSize() const{ } // Comm type -uint64_t JSONNode::getCommType() const{ +int64_t JSONNode::getCommType() const{ return comm_type; } diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index 501eaad0..ea80330b 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -1,6 +1,6 @@ #pragma once -#include "json.hpp" +#include #include #include #include @@ -19,14 +19,12 @@ class JSONNode { uint64_t runtime; uint64_t num_ops; uint64_t tensor_size; - uint64_t comm_type; + int64_t comm_type; uint32_t comm_priority; uint64_t comm_size; uint32_t comm_src; uint32_t comm_dst; uint32_t comm_tag; - uint32_t involved_dim_size; - std::vector involved_dim; public: std::vector data_deps{}; @@ -51,7 +49,7 @@ class JSONNode { uint64_t getRuntime() const; uint64_t getNumOps() const; uint64_t getTensorSize() const; - uint64_t getCommType() const; + int64_t getCommType() const; uint32_t getCommPriority() const; uint64_t getCommSize() const; uint32_t getCommSrc() const; diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index 77be4702..c5e4738c 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -32,6 +32,7 @@ void WrapperNode::createWrapper(std::string filename) { else if (ext == "json") { std::cout << "Using JSON format" << std::endl; format_type_ = JSON; + json_et_complete_ = false; jsonfile_.open(filename); data_ = json::parse(jsonfile_); // Parse JSON file window_size_json = data_["workload_graph"].size(); // Number of nodes @@ -115,6 +116,9 @@ void WrapperNode::removeNode(uint64_t node_id) { case JSON: { dep_graph_json.erase(node_id); + if (!json_et_complete_ && (dep_free_node_queue_json.size() < window_size_json)) { + readNextWindow(); + } break; } default: @@ -152,11 +156,15 @@ JSONNode WrapperNode::readNode(uint64_t node_idx) { void WrapperNode::readNextWindow() { uint64_t num_read = 0; do { + if (num_read >= window_size_json) { + json_et_complete_ = true; + break; + } JSONNode new_node = readNode(num_read); addNode(new_node); ++num_read; resolveDep(); - } while ((num_read < window_size_json) || (dep_unresolved_node_set_json.size() != 0)); + } while ((num_read < 256 * window_size_json) || (dep_unresolved_node_set_json.size() != 0)); //arbitrarily large 256 * window_size_json for (auto node_id_node : dep_graph_json) { uint64_t node_id = node_id_node.first; @@ -542,7 +550,7 @@ uint64_t WrapperNode::getTensorSize() { } // Get comm type -uint64_t WrapperNode::getCommType() { +int64_t WrapperNode::getCommType() { switch (format_type_) { case Protobuf: { diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index 9b808829..7fa51d1c 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -32,6 +32,7 @@ class WrapperNode { dep_free_node_queue_json{}; std::unordered_set> dep_unresolved_node_set_json{}; int window_size_json; + bool json_et_complete_; public: WrapperNode(); @@ -63,7 +64,7 @@ class WrapperNode { uint64_t getRuntime(); uint64_t getNumOps(); uint64_t getTensorSize(); - uint64_t getCommType(); + int64_t getCommType(); uint32_t getCommPriority(); uint64_t getCommSize(); uint32_t getCommSrc(); diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index 64b3db6b..f48371dc 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -14,22 +14,22 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { const string& attr_name = attr.name(); if (attr_name == "is_cpu_op") { - this->is_cpu_op_ = static_cast(attr.bool_val()); + this->is_cpu_op_ = attr.bool_val(); } else if (attr_name == "num_ops") { - this->num_ops_ = static_cast(attr.int64_val()); + this->num_ops_ = attr.uint64_val(); } else if (attr_name == "tensor_size") { this->tensor_size_ = attr.uint64_val(); } else if (attr_name == "comm_type") { this->comm_type_ = static_cast(attr.int64_val()); } else if (attr_name == "comm_priority") { - this->comm_priority_ = static_cast(attr.int32_val()); + this->comm_priority_ = attr.uint32_val(); } else if (attr_name == "comm_size") { this->comm_size_ = attr.uint64_val(); } else if (attr_name == "comm_src") { - this->comm_src_ = static_cast(attr.int32_val()); + this->comm_src_ = attr.uint32_val(); } else if (attr_name == "comm_dst") { - this->comm_dst_ = static_cast(attr.int32_val()); + this->comm_dst_ = attr.uint32_val(); } else if (attr_name == "comm_tag") { this->comm_tag_ = static_cast(attr.int32_val()); } else if (attr_name == "pg_name") { diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index b0ccbfa2..79e9856e 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -3,9 +3,10 @@ #include #include #include +#include #include -#include "et_def.pb.h" +#include "schema/protobuf/et_def.pb.h" namespace Chakra { @@ -55,7 +56,7 @@ class ETFeederNode { uint64_t id_; std::string name_; - uint32_t is_cpu_op_; + bool is_cpu_op_; uint64_t runtime_; uint64_t num_ops_; uint32_t tensor_loc_; @@ -69,4 +70,4 @@ class ETFeederNode { std::string pg_name_; }; -} // namespace Chakra \ No newline at end of file +} // namespace Chakra From 3b7a0adc8b6654c3371cc07a866a4ef4d10e7650 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Mon, 26 Aug 2024 14:51:34 -0400 Subject: [PATCH 14/38] fix include path --- src/feeder/et_feeder_node.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index 79e9856e..58afeda8 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -6,7 +6,7 @@ #include #include -#include "schema/protobuf/et_def.pb.h" +#include "et_def.pb.h" namespace Chakra { From 6cbbc4f7304fa9462d807ac31b58f68ba33cfce9 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Thu, 5 Sep 2024 14:22:11 -0400 Subject: [PATCH 15/38] fix lint errors --- src/feeder/JSONNode.cpp | 254 +++++---- src/feeder/JSONNode.h | 234 ++++---- src/feeder/WrapperNode.cpp | 1005 ++++++++++++++++------------------- src/feeder/WrapperNode.h | 133 +++-- src/feeder/et_feeder_node.h | 1 + 5 files changed, 770 insertions(+), 857 deletions(-) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/JSONNode.cpp index 513b7dfb..a8ad93d0 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/JSONNode.cpp @@ -4,192 +4,188 @@ JSONNode::JSONNode() {} // JSONNode copy constructor -JSONNode::JSONNode(const JSONNode &t) { - node_id = t.node_id; - node_name = t.node_name; - node_type = t.node_type; - is_cpu_op = t.is_cpu_op; - runtime = t.runtime; - data_deps = t.data_deps; - dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; - children_vec_json = t.children_vec_json; - children_set_json = t.children_set_json; - /* COMM_SEND_NODE = 5, - COMM_RECV_NODE = 6, - COMM_COLL_NODE = 7 */ - if (node_type == 5 || node_type == 6 || node_type == 7) { - tensor_size = t.tensor_size; - comm_type = t.comm_type; - comm_priority = t.comm_priority; - comm_size = t.comm_size; - comm_src = t.comm_src; - comm_dst = t.comm_dst; - comm_tag = t.comm_tag; - } +JSONNode::JSONNode(const JSONNode& t) { + node_id = t.node_id; + node_name = t.node_name; + node_type = t.node_type; + is_cpu_op = t.is_cpu_op; + runtime = t.runtime; + data_deps = t.data_deps; + dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; + children_vec_json = t.children_vec_json; + children_set_json = t.children_set_json; + /* COMM_SEND_NODE = 5, + COMM_RECV_NODE = 6, + COMM_COLL_NODE = 7 */ + if (node_type == 5 || node_type == 6 || node_type == 7) { + tensor_size = t.tensor_size; + comm_type = t.comm_type; + comm_priority = t.comm_priority; + comm_size = t.comm_size; + comm_src = t.comm_src; + comm_dst = t.comm_dst; + comm_tag = t.comm_tag; + } } // JSONNode constructor JSONNode::JSONNode(json data, uint64_t id) { - try { - node_id = data["workload_graph"][id]["Id"]; - } - catch (...) { - std::cerr << "node_id not specified in ET" << std::endl; - } - try { - node_name = data["workload_graph"][id]["Name"]; - } - catch (...) { - std::cerr << "node_name not specified in ET" << std::endl; - } - try { - node_type = data["workload_graph"][id]["NodeType"]; - } - catch (...) { - std::cerr << "node_type not specified in ET" << std::endl; - } - try { - is_cpu_op = data["workload_graph"][id]["is_cpu_op"]; - } - catch (...) { - std::cerr << "is_cpu_op not specified in ET" << std::endl; - } - try { - runtime = data["workload_graph"][id]["runtime"]; - } - catch (...) {} - try { - data_deps = data["workload_graph"][id]["data_deps"].get>(); - } - catch (...) { - std::cerr << "data deps not specified in ET" << std::endl; - } - /* COMM_SEND_NODE = 5, - COMM_RECV_NODE = 6, - COMM_COLL_NODE = 7 */ - if (node_type == 5 || node_type == 6 || node_type == 7) { - try { - tensor_size = data["workload_graph"][id]["tensor_size"]; - } - catch (...) {} - try { - comm_type = data["workload_graph"][id]["comm_type"]; - } - catch (...) {} - try { - comm_priority = data["workload_graph"][id]["comm_priority"]; - } - catch (...) { - comm_priority = 0; // Protobuf defaults to 0 - } - try { - comm_size = data["workload_graph"][id]["comm_size"]; - } - catch (...) {} - try { - comm_src = data["workload_graph"][id]["comm_src"]; - } - catch (...) {} - try { - comm_dst = data["workload_graph"][id]["comm_dst"]; - } - catch (...) {} - try { - comm_tag = data["workload_graph"][id]["comm_tag"]; - } - catch (...) {} - } - } + try { + node_id = data["workload_graph"][id]["Id"]; + } catch (...) { + std::cerr << "node_id not specified in ET" << std::endl; + } + try { + node_name = data["workload_graph"][id]["Name"]; + } catch (...) { + std::cerr << "node_name not specified in ET" << std::endl; + } + try { + node_type = data["workload_graph"][id]["NodeType"]; + } catch (...) { + std::cerr << "node_type not specified in ET" << std::endl; + } + try { + is_cpu_op = data["workload_graph"][id]["is_cpu_op"]; + } catch (...) { + std::cerr << "is_cpu_op not specified in ET" << std::endl; + } + try { + runtime = data["workload_graph"][id]["runtime"]; + } catch (...) { + } + try { + data_deps = + data["workload_graph"][id]["data_deps"].get>(); + } catch (...) { + std::cerr << "data deps not specified in ET" << std::endl; + } + /* COMM_SEND_NODE = 5, + COMM_RECV_NODE = 6, + COMM_COLL_NODE = 7 */ + if (node_type == 5 || node_type == 6 || node_type == 7) { + try { + tensor_size = data["workload_graph"][id]["tensor_size"]; + } catch (...) { + } + try { + comm_type = data["workload_graph"][id]["comm_type"]; + } catch (...) { + } + try { + comm_priority = data["workload_graph"][id]["comm_priority"]; + } catch (...) { + comm_priority = 0; // Protobuf defaults to 0 + } + try { + comm_size = data["workload_graph"][id]["comm_size"]; + } catch (...) { + } + try { + comm_src = data["workload_graph"][id]["comm_src"]; + } catch (...) { + } + try { + comm_dst = data["workload_graph"][id]["comm_dst"]; + } catch (...) { + } + try { + comm_tag = data["workload_graph"][id]["comm_tag"]; + } catch (...) { + } + } +} // Node id -uint64_t JSONNode::id() const{ - return node_id; +uint64_t JSONNode::id() const { + return node_id; } // Node name -std::string JSONNode::name() const{ - return node_name; +std::string JSONNode::name() const { + return node_name; } // Node type -int JSONNode::type() const{ - return node_type; +int JSONNode::type() const { + return node_type; } // Check if CPU OP -bool JSONNode::isCPUOp() const{ - return is_cpu_op; +bool JSONNode::isCPUOp() const { + return is_cpu_op; } // Runtime -uint64_t JSONNode::getRuntime() const{ - return runtime; +uint64_t JSONNode::getRuntime() const { + return runtime; } // Num ops -uint64_t JSONNode::getNumOps() const{ - return num_ops; +uint64_t JSONNode::getNumOps() const { + return num_ops; } // Tensor size -uint64_t JSONNode::getTensorSize() const{ - return tensor_size; +uint64_t JSONNode::getTensorSize() const { + return tensor_size; } // Comm type -int64_t JSONNode::getCommType() const{ - return comm_type; +int64_t JSONNode::getCommType() const { + return comm_type; } // Comm priority -uint32_t JSONNode::getCommPriority() const{ - return comm_priority; +uint32_t JSONNode::getCommPriority() const { + return comm_priority; } // Comm size -uint64_t JSONNode::getCommSize() const{ - return comm_size; +uint64_t JSONNode::getCommSize() const { + return comm_size; } // Comm src -uint32_t JSONNode::getCommSrc() const{ - return comm_src; +uint32_t JSONNode::getCommSrc() const { + return comm_src; } // Comm dst -uint32_t JSONNode::getCommDst() const{ - return comm_dst; +uint32_t JSONNode::getCommDst() const { + return comm_dst; } // Comm tag -uint32_t JSONNode::getCommTag() const{ - return comm_tag; +uint32_t JSONNode::getCommTag() const { + return comm_tag; } // Dependency unresolved parent IDs void JSONNode::addDepUnresolvedParentID(uint64_t node_id) { - dep_unresolved_parent_ids_json.emplace_back(node_id); + dep_unresolved_parent_ids_json.emplace_back(node_id); } // Get dependency unresolved parent IDs std::vector JSONNode::getDepUnresolvedParentIDs() { - return dep_unresolved_parent_ids_json; + return dep_unresolved_parent_ids_json; } // Set dependency unresolved parent IDs -void JSONNode::setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids) { - dep_unresolved_parent_ids_json = dep_unresolved_parent_ids; +void JSONNode::setDepUnresolvedParentIDs( + std::vector const& dep_unresolved_parent_ids) { + dep_unresolved_parent_ids_json = dep_unresolved_parent_ids; } // Add child void JSONNode::addChild(JSONNode node) { - // Avoid adding the same child node multiple times - // addChild is called multiple times to resolve dependencies - if (children_set_json.find(node) != children_set_json.end()) { - return; - } - children_vec_json.emplace_back(node); - children_set_json.emplace(node); + // Avoid adding the same child node multiple times + // addChild is called multiple times to resolve dependencies + if (children_set_json.find(node) != children_set_json.end()) { + return; + } + children_vec_json.emplace_back(node); + children_set_json.emplace(node); } // Get children vector diff --git a/src/feeder/JSONNode.h b/src/feeder/JSONNode.h index ea80330b..05df0cde 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/JSONNode.h @@ -1,147 +1,139 @@ #pragma once #include -#include #include -#include -#include #include +#include +#include #include +#include using json = nlohmann::json; class JSONNode { - private: - uint64_t node_id; - std::string node_name; - int node_type; - bool is_cpu_op; - uint64_t runtime; - uint64_t num_ops; - uint64_t tensor_size; - int64_t comm_type; - uint32_t comm_priority; - uint64_t comm_size; - uint32_t comm_src; - uint32_t comm_dst; - uint32_t comm_tag; + private: + uint64_t node_id; + std::string node_name; + int node_type; + bool is_cpu_op; + uint64_t runtime; + uint64_t num_ops; + uint64_t tensor_size; + int64_t comm_type; + uint32_t comm_priority; + uint64_t comm_size; + uint32_t comm_src; + uint32_t comm_dst; + uint32_t comm_tag; - public: - std::vector data_deps{}; - std::vector dep_unresolved_parent_ids_json{}; - std::vector children_vec_json{}; + public: + std::vector data_deps{}; + std::vector dep_unresolved_parent_ids_json{}; + std::vector children_vec_json{}; - // Compare function for set - struct CompareJSONNodesLT { - bool operator()(const JSONNode& a, const JSONNode& b) const { - return a.node_id < b.node_id; - } - }; - std::set children_set_json{}; - - JSONNode(); - JSONNode(const JSONNode &t); - JSONNode(json data, uint64_t id); - uint64_t id() const; - std::string name() const; - int type() const; - bool isCPUOp() const; - uint64_t getRuntime() const; - uint64_t getNumOps() const; - uint64_t getTensorSize() const; - int64_t getCommType() const; - uint32_t getCommPriority() const; - uint64_t getCommSize() const; - uint32_t getCommSrc() const; - uint32_t getCommDst() const; - uint32_t getCommTag() const; - void addDepUnresolvedParentID(uint64_t node_id); - std::vector getDepUnresolvedParentIDs(); - void setDepUnresolvedParentIDs(std::vector const& dep_unresolved_parent_ids); - void addChild(JSONNode node); - std::vector getChildren(); + // Compare function for set + struct CompareJSONNodesLT { + bool operator()(const JSONNode& a, const JSONNode& b) const { + return a.node_id < b.node_id; + } + }; + std::set children_set_json{}; - // Define the == operator for comparison - bool operator==(const JSONNode& other) const { - return node_id == other.node_id && - node_name == other.node_name && - node_type == other.node_type && - is_cpu_op == other.is_cpu_op && - runtime == other.runtime && - num_ops == other.num_ops && - tensor_size == other.tensor_size && - comm_type == other.comm_type && - comm_priority == other.comm_priority && - comm_size == other.comm_size && - comm_src == other.comm_src && - comm_dst == other.comm_dst && - comm_tag == other.comm_tag && - data_deps == other.data_deps && - dep_unresolved_parent_ids_json == other.dep_unresolved_parent_ids_json && - children_vec_json == other.children_vec_json && - children_set_json == other.children_set_json; - } + JSONNode(); + JSONNode(const JSONNode& t); + JSONNode(json data, uint64_t id); + uint64_t id() const; + std::string name() const; + int type() const; + bool isCPUOp() const; + uint64_t getRuntime() const; + uint64_t getNumOps() const; + uint64_t getTensorSize() const; + int64_t getCommType() const; + uint32_t getCommPriority() const; + uint64_t getCommSize() const; + uint32_t getCommSrc() const; + uint32_t getCommDst() const; + uint32_t getCommTag() const; + void addDepUnresolvedParentID(uint64_t node_id); + std::vector getDepUnresolvedParentIDs(); + void setDepUnresolvedParentIDs( + std::vector const& dep_unresolved_parent_ids); + void addChild(JSONNode node); + std::vector getChildren(); - // Overload the assignment operator - JSONNode& operator=(const JSONNode& other) { - if (this != &other) { - // Copy all member variables - node_id = other.node_id; - node_name = other.node_name; - node_type = other.node_type; - is_cpu_op = other.is_cpu_op; - runtime = other.runtime; - num_ops = other.num_ops; - tensor_size = other.tensor_size; - comm_type = other.comm_type; - comm_priority = other.comm_priority; - comm_size = other.comm_size; - comm_src = other.comm_src; - comm_dst = other.comm_dst; - comm_tag = other.comm_tag; - data_deps = other.data_deps; - dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; - children_vec_json = other.children_vec_json; - children_set_json = other.children_set_json; - } - return *this; - } + // Define the == operator for comparison + bool operator==(const JSONNode& other) const { + return node_id == other.node_id && node_name == other.node_name && + node_type == other.node_type && is_cpu_op == other.is_cpu_op && + runtime == other.runtime && num_ops == other.num_ops && + tensor_size == other.tensor_size && comm_type == other.comm_type && + comm_priority == other.comm_priority && comm_size == other.comm_size && + comm_src == other.comm_src && comm_dst == other.comm_dst && + comm_tag == other.comm_tag && data_deps == other.data_deps && + dep_unresolved_parent_ids_json == + other.dep_unresolved_parent_ids_json && + children_vec_json == other.children_vec_json && + children_set_json == other.children_set_json; + } + + // Overload the assignment operator + JSONNode& operator=(const JSONNode& other) { + if (this != &other) { + // Copy all member variables + node_id = other.node_id; + node_name = other.node_name; + node_type = other.node_type; + is_cpu_op = other.is_cpu_op; + runtime = other.runtime; + num_ops = other.num_ops; + tensor_size = other.tensor_size; + comm_type = other.comm_type; + comm_priority = other.comm_priority; + comm_size = other.comm_size; + comm_src = other.comm_src; + comm_dst = other.comm_dst; + comm_tag = other.comm_tag; + data_deps = other.data_deps; + dep_unresolved_parent_ids_json = other.dep_unresolved_parent_ids_json; + children_vec_json = other.children_vec_json; + children_set_json = other.children_set_json; + } + return *this; + } }; // Define a custom hash function for unordered set namespace std { - template<> - struct hash { - std::size_t operator()(const JSONNode& node) const { - std::size_t h1 = std::hash()(node.id()); - std::size_t h2 = std::hash()(node.name()); - std::size_t h3 = std::hash()(node.type()); - std::size_t h4 = std::hash()(node.isCPUOp()); - std::size_t h5 = std::hash()(node.getRuntime()); +template <> +struct hash { + std::size_t operator()(const JSONNode& node) const { + std::size_t h1 = std::hash()(node.id()); + std::size_t h2 = std::hash()(node.name()); + std::size_t h3 = std::hash()(node.type()); + std::size_t h4 = std::hash()(node.isCPUOp()); + std::size_t h5 = std::hash()(node.getRuntime()); - // A prime number for bit manipulation - const std::size_t prime = 31; + // A prime number for bit manipulation + const std::size_t prime = 31; - // Combine the hash of the current member with the hashes of the previous members - std::size_t hash = h1; - hash = hash * prime + h2; - hash = hash * prime + h3; - hash = hash * prime + h4; - hash = hash * prime + h5; + // Combine the hash of the current member with the hashes of the previous + // members + std::size_t hash = h1; + hash = hash * prime + h2; + hash = hash * prime + h3; + hash = hash * prime + h4; + hash = hash * prime + h5; - return hash; - } - }; -} + return hash; + } +}; +} // namespace std // Compare function for JSON node for priority queue -struct CompareJSONNodesGT : public std::binary_function< - JSONNode, - JSONNode, - bool> { - bool operator()( - const JSONNode lhs, - const JSONNode rhs) const { +struct CompareJSONNodesGT + : public std::binary_function { + bool operator()(const JSONNode lhs, const JSONNode rhs) const { return lhs.id() > rhs.id(); } }; \ No newline at end of file diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/WrapperNode.cpp index c5e4738c..daf17857 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/WrapperNode.cpp @@ -5,150 +5,145 @@ WrapperNode::WrapperNode() {} // WrapperNode copy constructor WrapperNode::WrapperNode(const WrapperNode& t) { - // Copy the attributes from the original instance to the new instance - format_type_ = t.format_type_; - et_feeder_ = t.et_feeder_; - node_ = t.node_; - data_ = t.data_; - node_idx_ = t.node_idx_; - push_back_queue_proto = t.push_back_queue_proto; - push_back_queue_json = t.push_back_queue_json; - dep_graph_json = t.dep_graph_json; - dep_free_node_id_set_json = t.dep_free_node_id_set_json; - dep_free_node_queue_json = t.dep_free_node_queue_json; - dep_unresolved_node_set_json = t.dep_unresolved_node_set_json; - window_size_json = t.window_size_json; + // Copy the attributes from the original instance to the new instance + format_type_ = t.format_type_; + et_feeder_ = t.et_feeder_; + node_ = t.node_; + data_ = t.data_; + node_idx_ = t.node_idx_; + push_back_queue_proto = t.push_back_queue_proto; + push_back_queue_json = t.push_back_queue_json; + dep_graph_json = t.dep_graph_json; + dep_free_node_id_set_json = t.dep_free_node_id_set_json; + dep_free_node_queue_json = t.dep_free_node_queue_json; + dep_unresolved_node_set_json = t.dep_unresolved_node_set_json; + window_size_json = t.window_size_json; } // WrapperNode create // format_type_ is assigned based on the extension of the file void WrapperNode::createWrapper(std::string filename) { - std::string ext = filename.substr(filename.find_last_of(".") + 1); - if (ext == "et") { - std::cout << "Using Protobuf format" << std::endl; - format_type_ = Protobuf; - et_feeder_ = new Chakra::ETFeeder(filename); - } - else if (ext == "json") { - std::cout << "Using JSON format" << std::endl; - format_type_ = JSON; - json_et_complete_ = false; - jsonfile_.open(filename); - data_ = json::parse(jsonfile_); // Parse JSON file - window_size_json = data_["workload_graph"].size(); // Number of nodes - // For legacy purposes. The entire JSON file is read at once - readNextWindow(); - } - else { - std::cerr << "Error: File format not supported." << std::endl; - exit(-1); - } + std::string ext = filename.substr(filename.find_last_of(".") + 1); + if (ext == "et") { + std::cout << "Using Protobuf format" << std::endl; + format_type_ = Protobuf; + et_feeder_ = new Chakra::ETFeeder(filename); + } else if (ext == "json") { + std::cout << "Using JSON format" << std::endl; + format_type_ = JSON; + json_et_complete_ = false; + jsonfile_.open(filename); + data_ = json::parse(jsonfile_); // Parse JSON file + window_size_json = data_["workload_graph"].size(); // Number of nodes + // For legacy purposes. The entire JSON file is read at once + readNextWindow(); + } else { + std::cerr << "Error: File format not supported." << std::endl; + exit(-1); + } } // WrapperNode constructor WrapperNode::WrapperNode(std::string filename) { - createWrapper(filename); + createWrapper(filename); } // Release memory void WrapperNode::releaseMemory() { - switch(format_type_) { - case Protobuf: - { - delete et_feeder_; - break; - } - case JSON: - { - jsonfile_.close(); - break; - } - default: - { - std::cerr << "Error in releaseMemory()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + delete et_feeder_; + break; + } + case JSON: { + jsonfile_.close(); + break; + } + default: { + std::cerr << "Error in releaseMemory()" << std::endl; + exit(-1); + } + } } WrapperNode::~WrapperNode() {} // Return protobuf node std::shared_ptr WrapperNode::getProtobufNode() { - return node_; + return node_; } // Return JSON node JSONNode WrapperNode::getJSONNode() { - return json_node_; + return json_node_; } // Find the index in JSON dictionary int64_t WrapperNode::findNodeIndexJSON(uint64_t node_id) { - int64_t i; - for (i=0; i < window_size_json; i++) { - if (data_["workload_graph"][i]["Id"] == node_id) { - break; - } - } - return i; + int64_t i; + for (i = 0; i < window_size_json; i++) { + if (data_["workload_graph"][i]["Id"] == node_id) { + break; + } + } + return i; } // Overloaded function - addNode // Add JSON node to dependency graph void WrapperNode::addNode(JSONNode node) { - dep_graph_json[node.id()] = node; + dep_graph_json[node.id()] = node; } // Add Protobuf node to dependency graph void WrapperNode::addNode(std::shared_ptr node) { - et_feeder_->addNode(node); + et_feeder_->addNode(node); } // Remove node from dependency graph void WrapperNode::removeNode(uint64_t node_id) { - switch(format_type_) { - case Protobuf: - { - et_feeder_->removeNode(node_id); - break; - } - case JSON: - { - dep_graph_json.erase(node_id); - if (!json_et_complete_ && (dep_free_node_queue_json.size() < window_size_json)) { - readNextWindow(); - } - break; - } - default: - { - std::cerr << "Error in removeNode()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + et_feeder_->removeNode(node_id); + break; + } + case JSON: { + dep_graph_json.erase(node_id); + if (!json_et_complete_ && + (dep_free_node_queue_json.size() < window_size_json)) { + readNextWindow(); + } + break; + } + default: { + std::cerr << "Error in removeNode()" << std::endl; + exit(-1); + } + } } // Read nodes in graph -// node_idx is the continuous index of the JSON nodes and is different from node_id +// node_idx is the continuous index of the JSON nodes and is different from +// node_id JSONNode WrapperNode::readNode(uint64_t node_idx) { - JSONNode node(data_, node_idx); - bool dep_unresolved = false; - for (int i = 0; i < node.data_deps.size(); ++i) { - auto parent_node = dep_graph_json.find(node.data_deps[i]); - if (parent_node != dep_graph_json.end()) { - parent_node->second.addChild(node); // Add node as a child to the parent node - } else { - dep_unresolved = true; - node.addDepUnresolvedParentID(node.data_deps[i]); - } - } + JSONNode node(data_, node_idx); + bool dep_unresolved = false; + for (int i = 0; i < node.data_deps.size(); ++i) { + auto parent_node = dep_graph_json.find(node.data_deps[i]); + if (parent_node != dep_graph_json.end()) { + parent_node->second.addChild( + node); // Add node as a child to the parent node + } else { + dep_unresolved = true; + node.addDepUnresolvedParentID(node.data_deps[i]); + } + } - if (dep_unresolved) { - dep_unresolved_node_set_json.emplace(node); - } + if (dep_unresolved) { + dep_unresolved_node_set_json.emplace(node); + } - return node; + return node; } // Read nodes in a window @@ -156,7 +151,7 @@ JSONNode WrapperNode::readNode(uint64_t node_idx) { void WrapperNode::readNextWindow() { uint64_t num_read = 0; do { - if (num_read >= window_size_json) { + if (num_read >= window_size_json) { json_et_complete_ = true; break; } @@ -164,14 +159,17 @@ void WrapperNode::readNextWindow() { addNode(new_node); ++num_read; resolveDep(); - } while ((num_read < 256 * window_size_json) || (dep_unresolved_node_set_json.size() != 0)); //arbitrarily large 256 * window_size_json + } while ((num_read < 256 * window_size_json) || + (dep_unresolved_node_set_json.size() != + 0)); // arbitrarily large 256 * window_size_json for (auto node_id_node : dep_graph_json) { uint64_t node_id = node_id_node.first; JSONNode node(node_id_node.second); - // Unordered set does not allow duplicates. So, count returns 1 if key exists, 0 otherwise + // Unordered set does not allow duplicates. So, count returns 1 if key + // exists, 0 otherwise if ((dep_free_node_id_set_json.count(node_id) == 0) && - (node.data_deps.size() == 0)) { + (node.data_deps.size() == 0)) { dep_free_node_id_set_json.emplace(node_id); dep_free_node_queue_json.emplace(node); } @@ -180,541 +178,468 @@ void WrapperNode::readNextWindow() { // Resolve dependencies void WrapperNode::resolveDep() { - switch(format_type_) { - case Protobuf: - { - et_feeder_->resolveDep(); - break; - } - case JSON: - { - // Loop over unresolved nodes - for (auto it = dep_unresolved_node_set_json.begin(); - it != dep_unresolved_node_set_json.end();) { - JSONNode node = *it; - std::vector dep_unresolved_parent_ids_json = - node.getDepUnresolvedParentIDs(); - // Loop over unresolved parent IDs - for (auto inner_it = dep_unresolved_parent_ids_json.begin(); - inner_it != dep_unresolved_parent_ids_json.end();) { - auto parent_node = dep_graph_json.find(*inner_it); - if (parent_node != dep_graph_json.end()) { - // Add current node as a child to the parent - parent_node->second.addChild(node); - inner_it = dep_unresolved_parent_ids_json.erase(inner_it); - } else { - ++inner_it; - } - } - if (dep_unresolved_parent_ids_json.size() == 0) { - it = dep_unresolved_node_set_json.erase(it); - } else { - node.setDepUnresolvedParentIDs(dep_unresolved_parent_ids_json); - ++it; - } - } - break; - } - default: - { - std::cerr << "Error in resolveDep()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + et_feeder_->resolveDep(); + break; + } + case JSON: { + // Loop over unresolved nodes + for (auto it = dep_unresolved_node_set_json.begin(); + it != dep_unresolved_node_set_json.end();) { + JSONNode node = *it; + std::vector dep_unresolved_parent_ids_json = + node.getDepUnresolvedParentIDs(); + // Loop over unresolved parent IDs + for (auto inner_it = dep_unresolved_parent_ids_json.begin(); + inner_it != dep_unresolved_parent_ids_json.end();) { + auto parent_node = dep_graph_json.find(*inner_it); + if (parent_node != dep_graph_json.end()) { + // Add current node as a child to the parent + parent_node->second.addChild(node); + inner_it = dep_unresolved_parent_ids_json.erase(inner_it); + } else { + ++inner_it; + } + } + if (dep_unresolved_parent_ids_json.size() == 0) { + it = dep_unresolved_node_set_json.erase(it); + } else { + node.setDepUnresolvedParentIDs(dep_unresolved_parent_ids_json); + ++it; + } + } + break; + } + default: { + std::cerr << "Error in resolveDep()" << std::endl; + exit(-1); + } + } } // Push dependency free nodes void WrapperNode::pushBackIssuableNode(uint64_t node_id) { - switch(format_type_) { - case Protobuf: - { - et_feeder_->pushBackIssuableNode(node_id); - break; - } - case JSON: - { - JSONNode node = dep_graph_json[node_id]; - dep_free_node_id_set_json.emplace(node_id); - dep_free_node_queue_json.emplace(node); - break; - } - default: - { - std::cerr << "Error in pushBackIssuableNode()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + et_feeder_->pushBackIssuableNode(node_id); + break; + } + case JSON: { + JSONNode node = dep_graph_json[node_id]; + dep_free_node_id_set_json.emplace(node_id); + dep_free_node_queue_json.emplace(node); + break; + } + default: { + std::cerr << "Error in pushBackIssuableNode()" << std::endl; + exit(-1); + } + } } // Free children void WrapperNode::freeChildrenNodes(uint64_t node_id) { - switch(format_type_) { - case Protobuf: - { - et_feeder_->freeChildrenNodes(node_id); - break; - } - case JSON: - { - JSONNode node = dep_graph_json[node_id]; - for (auto child : node.getChildren()) { - for (auto it = child.data_deps.begin(); - it != child.data_deps.end(); - ++it) { - if (*it == node_id) { - child.data_deps.erase(it); - break; - } - } - if (child.data_deps.size() == 0) { - dep_free_node_id_set_json.emplace(child.id()); - dep_free_node_queue_json.emplace(child); - } - } - break; - } - default: - { - std::cerr << "Error in freeChildrenNodes()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + et_feeder_->freeChildrenNodes(node_id); + break; + } + case JSON: { + JSONNode node = dep_graph_json[node_id]; + for (auto child : node.getChildren()) { + for (auto it = child.data_deps.begin(); it != child.data_deps.end(); + ++it) { + if (*it == node_id) { + child.data_deps.erase(it); + break; + } + } + if (child.data_deps.size() == 0) { + dep_free_node_id_set_json.emplace(child.id()); + dep_free_node_queue_json.emplace(child); + } + } + break; + } + default: { + std::cerr << "Error in freeChildrenNodes()" << std::endl; + exit(-1); + } + } } // Check if the node is valid bool WrapperNode::isValidNode() { - switch (format_type_) { - case Protobuf: - { - if (node_ == nullptr) - return false; - else - return true; - } - case JSON: - { - if (node_idx_ < 0) - return false; - else - return true; - } - default: - { - std::cerr << "Error in isValid()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + if (node_ == nullptr) + return false; + else + return true; + } + case JSON: { + if (node_idx_ < 0) + return false; + else + return true; + } + default: { + std::cerr << "Error in isValid()" << std::endl; + exit(-1); + } + } } // Push node to queue void WrapperNode::push_to_queue() { - switch (format_type_) { - case Protobuf: - { - push_back_queue_proto.push(node_); - break; - } - case JSON: - { - push_back_queue_json.push(json_node_); - break; - } - default: - { - std::cerr << "Error in push_to_queue()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + push_back_queue_proto.push(node_); + break; + } + case JSON: { + push_back_queue_json.push(json_node_); + break; + } + default: { + std::cerr << "Error in push_to_queue()" << std::endl; + exit(-1); + } + } } // Check if queue is empty bool WrapperNode::is_queue_empty() { - switch (format_type_) { - case Protobuf: - { - return push_back_queue_proto.empty(); - } - case JSON: - { - return push_back_queue_json.empty(); - } - default: - { - std::cerr << "Error in is_queue_empty()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return push_back_queue_proto.empty(); + } + case JSON: { + return push_back_queue_json.empty(); + } + default: { + std::cerr << "Error in is_queue_empty()" << std::endl; + exit(-1); + } + } } // Get element in the queue front void WrapperNode::queue_front() { - switch (format_type_) { - case Protobuf: - { - node_ = push_back_queue_proto.front(); - break; - } - case JSON: - { - json_node_ = push_back_queue_json.front(); - break; - } - default: - { - std::cerr << "Error in queue_front()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + node_ = push_back_queue_proto.front(); + break; + } + case JSON: { + json_node_ = push_back_queue_json.front(); + break; + } + default: { + std::cerr << "Error in queue_front()" << std::endl; + exit(-1); + } + } } // Pop node from queue void WrapperNode::pop_from_queue() { - switch (format_type_) { - case Protobuf: - { - push_back_queue_proto.pop(); - break; - } - case JSON: - { - push_back_queue_json.pop(); - break; - } - default: - { - std::cerr << "Error in pop_from_queue()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + push_back_queue_proto.pop(); + break; + } + case JSON: { + push_back_queue_json.pop(); + break; + } + default: { + std::cerr << "Error in pop_from_queue()" << std::endl; + exit(-1); + } + } } // Get next issuable node from dependency free queue void WrapperNode::getNextIssuableNode() { - switch (format_type_) { - case Protobuf: - { - node_ = et_feeder_->getNextIssuableNode(); - break; - } - case JSON: - { - if (dep_free_node_queue_json.size() != 0) { - json_node_ = dep_free_node_queue_json.top(); - node_idx_ = findNodeIndexJSON(json_node_.id()); - dep_free_node_id_set_json.erase(json_node_.id()); - dep_free_node_queue_json.pop(); - } - else - node_idx_ = -1; - break; - } - default: - { - std::cerr << "Error in getNextIssuableNode()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + node_ = et_feeder_->getNextIssuableNode(); + break; + } + case JSON: { + if (dep_free_node_queue_json.size() != 0) { + json_node_ = dep_free_node_queue_json.top(); + node_idx_ = findNodeIndexJSON(json_node_.id()); + dep_free_node_id_set_json.erase(json_node_.id()); + dep_free_node_queue_json.pop(); + } else + node_idx_ = -1; + break; + } + default: { + std::cerr << "Error in getNextIssuableNode()" << std::endl; + exit(-1); + } + } } // Get node ID uint64_t WrapperNode::getNodeID() { - switch (format_type_) { - case Protobuf: - { - return node_->id(); - } - case JSON: - { - return json_node_.id(); - } - default: - { - std::cerr << "Error in getNodeID()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->id(); + } + case JSON: { + return json_node_.id(); + } + default: { + std::cerr << "Error in getNodeID()" << std::endl; + exit(-1); + } + } } // Get node name std::string WrapperNode::getNodeName() { - switch (format_type_) { - case Protobuf: - { - return node_->name(); - } - case JSON: - { - return json_node_.name(); - } - default: - { - std::cerr << "Error in getNodeName()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->name(); + } + case JSON: { + return json_node_.name(); + } + default: { + std::cerr << "Error in getNodeName()" << std::endl; + exit(-1); + } + } } // Get node type int WrapperNode::getNodeType() { - switch (format_type_) { - case Protobuf: - { - return node_->type(); - } - case JSON: - { - return json_node_.type(); - } - default: - { - std::cerr << "Error in getNodeType()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->type(); + } + case JSON: { + return json_node_.type(); + } + default: { + std::cerr << "Error in getNodeType()" << std::endl; + exit(-1); + } + } } // Check if CPU operation bool WrapperNode::isCPUOp() { - switch (format_type_) { - case Protobuf: - { - return node_->is_cpu_op(); - } - case JSON: - { - return json_node_.isCPUOp(); - } - default: - { - std::cerr << "Error in isCPUOp()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->is_cpu_op(); + } + case JSON: { + return json_node_.isCPUOp(); + } + default: { + std::cerr << "Error in isCPUOp()" << std::endl; + exit(-1); + } + } } // Get runtime uint64_t WrapperNode::getRuntime() { - switch (format_type_) { - case Protobuf: - { - return node_->runtime(); - } - case JSON: - { - return json_node_.getRuntime(); - } - default: - { - std::cerr << "Error in getRuntime()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->runtime(); + } + case JSON: { + return json_node_.getRuntime(); + } + default: { + std::cerr << "Error in getRuntime()" << std::endl; + exit(-1); + } + } } // Get num ops uint64_t WrapperNode::getNumOps() { - switch (format_type_) { - case Protobuf: - { - return node_->num_ops(); - } - case JSON: - { - return json_node_.getNumOps(); - } - default: - { - std::cerr << "Error in getNumOps()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->num_ops(); + } + case JSON: { + return json_node_.getNumOps(); + } + default: { + std::cerr << "Error in getNumOps()" << std::endl; + exit(-1); + } + } } // Get tensor size uint64_t WrapperNode::getTensorSize() { - switch (format_type_) { - case Protobuf: - { - return node_->tensor_size(); - } - case JSON: - { - return json_node_.getTensorSize(); - } - default: - { - std::cerr << "Error in getTensorSize()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->tensor_size(); + } + case JSON: { + return json_node_.getTensorSize(); + } + default: { + std::cerr << "Error in getTensorSize()" << std::endl; + exit(-1); + } + } } // Get comm type int64_t WrapperNode::getCommType() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_type(); - } - case JSON: - { - return json_node_.getCommType(); - } - default: - { - std::cerr << "Error in getCommType()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_type(); + } + case JSON: { + return json_node_.getCommType(); + } + default: { + std::cerr << "Error in getCommType()" << std::endl; + exit(-1); + } + } } // Get comm priority uint32_t WrapperNode::getCommPriority() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_priority(); - } - case JSON: - { - return json_node_.getCommPriority(); - } - default: - { - std::cerr << "Error in getCommPriority()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_priority(); + } + case JSON: { + return json_node_.getCommPriority(); + } + default: { + std::cerr << "Error in getCommPriority()" << std::endl; + exit(-1); + } + } } // Get comm size uint64_t WrapperNode::getCommSize() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_size(); - } - case JSON: - { - return json_node_.getCommSize(); - } - default: - { - std::cerr << "Error in getCommSize()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_size(); + } + case JSON: { + return json_node_.getCommSize(); + } + default: { + std::cerr << "Error in getCommSize()" << std::endl; + exit(-1); + } + } } // Get comm src uint32_t WrapperNode::getCommSrc() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_src(); - } - case JSON: - { - return json_node_.getCommSrc(); - } - default: - { - std::cerr << "Error in getCommSrc()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_src(); + } + case JSON: { + return json_node_.getCommSrc(); + } + default: { + std::cerr << "Error in getCommSrc()" << std::endl; + exit(-1); + } + } } // Get comm dst uint32_t WrapperNode::getCommDst() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_dst(); - } - case JSON: - { - return json_node_.getCommDst(); - } - default: - { - std::cerr << "Error in getCommDst()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_dst(); + } + case JSON: { + return json_node_.getCommDst(); + } + default: { + std::cerr << "Error in getCommDst()" << std::endl; + exit(-1); + } + } } // Get comm tag uint32_t WrapperNode::getCommTag() { - switch (format_type_) { - case Protobuf: - { - return node_->comm_tag(); - } - case JSON: - { - return json_node_.getCommTag(); - } - default: - { - std::cerr << "Error in getCommTag()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return node_->comm_tag(); + } + case JSON: { + return json_node_.getCommTag(); + } + default: { + std::cerr << "Error in getCommTag()" << std::endl; + exit(-1); + } + } } // Check if has more nodes to issue bool WrapperNode::hasNodesToIssue() { - switch (format_type_) { - case Protobuf: - { - return et_feeder_->hasNodesToIssue(); - } - case JSON: - { - return !(dep_graph_json.empty() && dep_free_node_queue_json.empty()); - } - default: - { - std::cerr << "Error in hasNodesToIssue()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + return et_feeder_->hasNodesToIssue(); + } + case JSON: { + return !(dep_graph_json.empty() && dep_free_node_queue_json.empty()); + } + default: { + std::cerr << "Error in hasNodesToIssue()" << std::endl; + exit(-1); + } + } } // Lookup Node void WrapperNode::lookupNode(uint64_t node_id) { - switch (format_type_) { - case Protobuf: - { - node_ = et_feeder_->lookupNode(node_id); - break; - } - case JSON: - { - try { - json_node_ = dep_graph_json.at(node_id); - } catch (const std::out_of_range& e) { - std::cerr << "looking for node_id=" << node_id - << " in dep graph, however, not loaded yet" << std::endl; - throw(e); - } - break; - } - default: - { - std::cerr << "Error in lookupNode()" << std::endl; - exit(-1); - } - } + switch (format_type_) { + case Protobuf: { + node_ = et_feeder_->lookupNode(node_id); + break; + } + case JSON: { + try { + json_node_ = dep_graph_json.at(node_id); + } catch (const std::out_of_range& e) { + std::cerr << "looking for node_id=" << node_id + << " in dep graph, however, not loaded yet" << std::endl; + throw(e); + } + break; + } + default: { + std::cerr << "Error in lookupNode()" << std::endl; + exit(-1); + } + } } // Overloaded function returns children protobuf nodes -void WrapperNode::getChildren(std::vector>& childrenNodes) { - childrenNodes = node_->getChildren(); +void WrapperNode::getChildren( + std::vector>& childrenNodes) { + childrenNodes = node_->getChildren(); } // Overloaded function returns children JSON nodes void WrapperNode::getChildren(std::vector& childrenNodes) { - childrenNodes = json_node_.getChildren(); + childrenNodes = json_node_.getChildren(); } \ No newline at end of file diff --git a/src/feeder/WrapperNode.h b/src/feeder/WrapperNode.h index 7fa51d1c..93d13afb 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/WrapperNode.h @@ -1,78 +1,77 @@ #pragma once +#include "JSONNode.h" #include "et_feeder.h" #include "et_feeder_node.h" -#include "JSONNode.h" using json = nlohmann::json; -enum format { - Protobuf, - JSON -}; +enum format { Protobuf, JSON }; /// WrapperNode class wraps protobuf and JSON class WrapperNode { - private: - enum format format_type_; - Chakra::ETFeeder* et_feeder_; - std::shared_ptr node_ {nullptr}; - std::ifstream jsonfile_; - json data_; - JSONNode json_node_; - int64_t node_idx_ = -1; - std::queue> push_back_queue_proto; - std::queue push_back_queue_json; - std::unordered_map dep_graph_json{}; - std::unordered_set dep_free_node_id_set_json{}; - std::priority_queue< - JSONNode, //type of stored elements - std::vector, // underlying container to store elements - CompareJSONNodesGT> // compare type providing a strick weak ordering - dep_free_node_queue_json{}; - std::unordered_set> dep_unresolved_node_set_json{}; - int window_size_json; - bool json_et_complete_; - - public: - WrapperNode(); - WrapperNode(const WrapperNode& t); - WrapperNode(std::string filename); - ~WrapperNode(); - void releaseMemory(); - void createWrapper(std::string filename); - std::shared_ptr getProtobufNode(); - JSONNode getJSONNode(); - void addNode(JSONNode node); - void addNode(std::shared_ptr node); - void removeNode(uint64_t node_id); - void readNextWindow(); - JSONNode readNode(uint64_t node_id); - void resolveDep(); - void pushBackIssuableNode(uint64_t node_id); - void freeChildrenNodes(uint64_t node_id); - bool isValidNode(); - void push_to_queue(); - bool is_queue_empty(); - void queue_front(); - void pop_from_queue(); - void getNextIssuableNode(); - uint64_t getNodeID(); - std::string getNodeName(); - int getNodeType(); - bool isCPUOp(); - uint64_t getRuntime(); - uint64_t getNumOps(); - uint64_t getTensorSize(); - int64_t getCommType(); - uint32_t getCommPriority(); - uint64_t getCommSize(); - uint32_t getCommSrc(); - uint32_t getCommDst(); - uint32_t getCommTag(); - bool hasNodesToIssue(); - void lookupNode(uint64_t node_id); - void getChildren(std::vector>& childrenNodes); - void getChildren(std::vector& childrenNodes); - int64_t findNodeIndexJSON(uint64_t node_id); + private: + enum format format_type_; + Chakra::ETFeeder* et_feeder_; + std::shared_ptr node_{nullptr}; + std::ifstream jsonfile_; + json data_; + JSONNode json_node_; + int64_t node_idx_ = -1; + std::queue> push_back_queue_proto; + std::queue push_back_queue_json; + std::unordered_map dep_graph_json{}; + std::unordered_set dep_free_node_id_set_json{}; + std::priority_queue< + JSONNode, // type of stored elements + std::vector, // underlying container to store elements + CompareJSONNodesGT> // compare type providing a strick weak ordering + dep_free_node_queue_json{}; + std::unordered_set> + dep_unresolved_node_set_json{}; + int window_size_json; + bool json_et_complete_; + + public: + WrapperNode(); + WrapperNode(const WrapperNode& t); + WrapperNode(std::string filename); + ~WrapperNode(); + void releaseMemory(); + void createWrapper(std::string filename); + std::shared_ptr getProtobufNode(); + JSONNode getJSONNode(); + void addNode(JSONNode node); + void addNode(std::shared_ptr node); + void removeNode(uint64_t node_id); + void readNextWindow(); + JSONNode readNode(uint64_t node_id); + void resolveDep(); + void pushBackIssuableNode(uint64_t node_id); + void freeChildrenNodes(uint64_t node_id); + bool isValidNode(); + void push_to_queue(); + bool is_queue_empty(); + void queue_front(); + void pop_from_queue(); + void getNextIssuableNode(); + uint64_t getNodeID(); + std::string getNodeName(); + int getNodeType(); + bool isCPUOp(); + uint64_t getRuntime(); + uint64_t getNumOps(); + uint64_t getTensorSize(); + int64_t getCommType(); + uint32_t getCommPriority(); + uint64_t getCommSize(); + uint32_t getCommSrc(); + uint32_t getCommDst(); + uint32_t getCommTag(); + bool hasNodesToIssue(); + void lookupNode(uint64_t node_id); + void getChildren( + std::vector>& childrenNodes); + void getChildren(std::vector& childrenNodes); + int64_t findNodeIndexJSON(uint64_t node_id); }; \ No newline at end of file diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index 58afeda8..33f84a5a 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "et_def.pb.h" From 124de08d0c6713863a3a59a77d84131e92442848 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 18:59:19 -0400 Subject: [PATCH 16/38] merging et_feeder_node --- src/feeder/et_feeder_node.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index f48371dc..2d89b93c 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -14,22 +14,22 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { const string& attr_name = attr.name(); if (attr_name == "is_cpu_op") { - this->is_cpu_op_ = attr.bool_val(); + this->is_cpu_op_ = static_cast(attr.bool_val()); } else if (attr_name == "num_ops") { - this->num_ops_ = attr.uint64_val(); + this->num_ops_ = static_cast(attr.int64_val()); } else if (attr_name == "tensor_size") { this->tensor_size_ = attr.uint64_val(); } else if (attr_name == "comm_type") { this->comm_type_ = static_cast(attr.int64_val()); } else if (attr_name == "comm_priority") { - this->comm_priority_ = attr.uint32_val(); + this->comm_priority_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_size") { - this->comm_size_ = attr.uint64_val(); + this->comm_size_ = attr.int64_val(); } else if (attr_name == "comm_src") { - this->comm_src_ = attr.uint32_val(); + this->comm_src_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_dst") { - this->comm_dst_ = attr.uint32_val(); + this->comm_dst_ = static_cast(attr.int32_val()); } else if (attr_name == "comm_tag") { this->comm_tag_ = static_cast(attr.int32_val()); } else if (attr_name == "pg_name") { From 274826f237bd9842ae188af161f882cd03b1a77b Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:14:21 -0400 Subject: [PATCH 17/38] adding install setuptools to github workflows --- .github/workflows/cpp_lint.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index e4530eda..bf93a0f9 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -11,7 +11,9 @@ jobs: uses: actions/checkout@v2 - name: Format and Lint C++ Code - uses: DoozyX/clang-format-lint-action@v0.18.1 + uses: DoozyX/clang-format-lint-action@v0.11 + run: | + pip install setuptools with: source: '.' extensions: 'cc,cpp,h,hh' From 21417d88d2a4b2277c8080e04a2749fb737540c8 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:19:12 -0400 Subject: [PATCH 18/38] updating workflows --- .github/workflows/cpp_lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index bf93a0f9..72329c5e 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -9,11 +9,11 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v2 + run: | + pip install setuptools - name: Format and Lint C++ Code uses: DoozyX/clang-format-lint-action@v0.11 - run: | - pip install setuptools with: source: '.' extensions: 'cc,cpp,h,hh' From ad998fcad3585b7fbaf901a63d7d771188c9d76e Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:21:08 -0400 Subject: [PATCH 19/38] updating workflows --- .github/workflows/cpp_lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index 72329c5e..f3a1981f 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -8,8 +8,8 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v2 - run: | + - uses: actions/checkout@v2 + - run: | pip install setuptools - name: Format and Lint C++ Code From bf61b64485938e9a72a79dabddfb7b977cb9435f Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:25:05 -0400 Subject: [PATCH 20/38] updating cpp_lint.yml --- .github/workflows/cpp_lint.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index f3a1981f..df059e27 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -8,9 +8,10 @@ jobs: steps: - name: Checkout Code - - uses: actions/checkout@v2 - - run: | - pip install setuptools + uses: actions/checkout@v2 + + - name: Install setuptools + run: pip install setuptools - name: Format and Lint C++ Code uses: DoozyX/clang-format-lint-action@v0.11 From 2474103cac444e62614d4abeefad69ef3f15539e Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:39:05 -0400 Subject: [PATCH 21/38] updating clang-format version --- .github/workflows/cpp_lint.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index df059e27..b84c8669 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -10,9 +10,6 @@ jobs: - name: Checkout Code uses: actions/checkout@v2 - - name: Install setuptools - run: pip install setuptools - - name: Format and Lint C++ Code uses: DoozyX/clang-format-lint-action@v0.11 with: From 3a7c2c9a76268df3932075d77e0db39c16858cda Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:41:05 -0400 Subject: [PATCH 22/38] updating clang-format version --- .github/workflows/cpp_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index b84c8669..e4530eda 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -11,7 +11,7 @@ jobs: uses: actions/checkout@v2 - name: Format and Lint C++ Code - uses: DoozyX/clang-format-lint-action@v0.11 + uses: DoozyX/clang-format-lint-action@v0.18.1 with: source: '.' extensions: 'cc,cpp,h,hh' From 7756ec25209a1f09828f7c8c35095846d7478cec Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Fri, 6 Sep 2024 19:53:32 -0400 Subject: [PATCH 23/38] fix lint errors fix lint errors fix lint errors fix lint errors --- tests/feeder/wrapperTests.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapperTests.cpp index c9a95ae0..d8e43f14 100644 --- a/tests/feeder/wrapperTests.cpp +++ b/tests/feeder/wrapperTests.cpp @@ -55,8 +55,7 @@ TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { ASSERT_EQ(children[0]->id(), 217); ASSERT_EQ(children[1]->id(), 430); ASSERT_EQ(children[2]->id(), 435); - } - else if (ext == "json") { + } else if (ext == "json") { std::vector children; node.getChildren(children); ASSERT_EQ(children[0].id(), 217); @@ -138,8 +137,7 @@ TEST_F(WrapperNodeTest, AddNodeTest) { node.lookupNode(216); pnode2 = node.getProtobufNode(); ASSERT_EQ(pnode2->id(), 216); - } - else if (ext == "json") { + } else if (ext == "json") { JSON jnode1; node.lookupNode(216); jnode1 = node.getJSONNode(); @@ -163,8 +161,7 @@ TEST_F(WrapperNodeTest, NodeGetChildrenTest) { node.getChildren(children); ASSERT_EQ(children[0]->id(), 217); ASSERT_EQ(children[2]->id(), 435); - } - else if (ext == "json") { + } else if (ext == "json") { std::vector children; node.getChildren(children); ASSERT_EQ(children[0].id(), 217); From f439bf44229bc68eb29b5c45b98ec712a7570819 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Mon, 23 Sep 2024 12:59:29 -0400 Subject: [PATCH 24/38] Fix rebase error --- src/feeder/et_feeder_node.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index 33f84a5a..c0aede48 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -3,8 +3,6 @@ #include #include #include -#include -#include #include #include "et_def.pb.h" From 58fda3ed3bbb9dd83008e66a38136e2428dc3b88 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Wed, 25 Sep 2024 15:29:54 -0400 Subject: [PATCH 25/38] addressing reviewer comments --- src/feeder/{JSONNode.cpp => json_node.cpp} | 18 +++++++++--------- src/feeder/{JSONNode.h => json_node.h} | 11 +++++++++++ .../{WrapperNode.cpp => wrapper_node.cpp} | 2 +- src/feeder/{WrapperNode.h => wrapper_node.h} | 4 ++-- .../{jsonData.tar.gz => json_trace.tar.gz} | Bin .../{wrapperTests.cpp => wrapper_tests.cpp} | 12 ++---------- 6 files changed, 25 insertions(+), 22 deletions(-) rename src/feeder/{JSONNode.cpp => json_node.cpp} (92%) rename src/feeder/{JSONNode.h => json_node.h} (95%) rename src/feeder/{WrapperNode.cpp => wrapper_node.cpp} (99%) rename src/feeder/{WrapperNode.h => wrapper_node.h} (97%) rename tests/data/{jsonData.tar.gz => json_trace.tar.gz} (100%) rename tests/feeder/{wrapperTests.cpp => wrapper_tests.cpp} (91%) diff --git a/src/feeder/JSONNode.cpp b/src/feeder/json_node.cpp similarity index 92% rename from src/feeder/JSONNode.cpp rename to src/feeder/json_node.cpp index a8ad93d0..578bc04e 100644 --- a/src/feeder/JSONNode.cpp +++ b/src/feeder/json_node.cpp @@ -1,4 +1,4 @@ -#include "JSONNode.h" +#include "json_node.h" // JSONNode default constructor JSONNode::JSONNode() {} @@ -14,10 +14,10 @@ JSONNode::JSONNode(const JSONNode& t) { dep_unresolved_parent_ids_json = t.dep_unresolved_parent_ids_json; children_vec_json = t.children_vec_json; children_set_json = t.children_set_json; - /* COMM_SEND_NODE = 5, - COMM_RECV_NODE = 6, - COMM_COLL_NODE = 7 */ - if (node_type == 5 || node_type == 6 || node_type == 7) { + + if (node_type == NodeType::COMM_SEND_NODE || \ + node_type == NodeType::COMM_RECV_NODE || \ + node_type == NodeType::COMM_COLL_NODE) { tensor_size = t.tensor_size; comm_type = t.comm_type; comm_priority = t.comm_priority; @@ -60,10 +60,10 @@ JSONNode::JSONNode(json data, uint64_t id) { } catch (...) { std::cerr << "data deps not specified in ET" << std::endl; } - /* COMM_SEND_NODE = 5, - COMM_RECV_NODE = 6, - COMM_COLL_NODE = 7 */ - if (node_type == 5 || node_type == 6 || node_type == 7) { + + if (node_type == NodeType::COMM_SEND_NODE || \ + node_type == NodeType::COMM_RECV_NODE || \ + node_type == NodeType::COMM_COLL_NODE) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; } catch (...) { diff --git a/src/feeder/JSONNode.h b/src/feeder/json_node.h similarity index 95% rename from src/feeder/JSONNode.h rename to src/feeder/json_node.h index 05df0cde..744b360b 100644 --- a/src/feeder/JSONNode.h +++ b/src/feeder/json_node.h @@ -10,6 +10,17 @@ using json = nlohmann::json; +enum NodeType: int { + INVALID_NODE = 0, + METADATA_NODE = 1, + MEM_LOAD_NODE = 2, + MEM_STORE_NODE = 3, + COMP_NODE = 4, + COMM_SEND_NODE = 5, + COMM_RECV_NODE = 6, + COMM_COLL_NODE = 7 +}; + class JSONNode { private: uint64_t node_id; diff --git a/src/feeder/WrapperNode.cpp b/src/feeder/wrapper_node.cpp similarity index 99% rename from src/feeder/WrapperNode.cpp rename to src/feeder/wrapper_node.cpp index daf17857..4a4baa63 100644 --- a/src/feeder/WrapperNode.cpp +++ b/src/feeder/wrapper_node.cpp @@ -1,4 +1,4 @@ -#include "WrapperNode.h" +#include "wrapper_node.h" // WrapperNode default constructor WrapperNode::WrapperNode() {} diff --git a/src/feeder/WrapperNode.h b/src/feeder/wrapper_node.h similarity index 97% rename from src/feeder/WrapperNode.h rename to src/feeder/wrapper_node.h index 93d13afb..d08ca0de 100644 --- a/src/feeder/WrapperNode.h +++ b/src/feeder/wrapper_node.h @@ -1,6 +1,6 @@ #pragma once -#include "JSONNode.h" +#include "json_node.h" #include "et_feeder.h" #include "et_feeder_node.h" @@ -8,7 +8,7 @@ using json = nlohmann::json; enum format { Protobuf, JSON }; -/// WrapperNode class wraps protobuf and JSON +// WrapperNode class wraps protobuf and JSON class WrapperNode { private: enum format format_type_; diff --git a/tests/data/jsonData.tar.gz b/tests/data/json_trace.tar.gz similarity index 100% rename from tests/data/jsonData.tar.gz rename to tests/data/json_trace.tar.gz diff --git a/tests/feeder/wrapperTests.cpp b/tests/feeder/wrapper_tests.cpp similarity index 91% rename from tests/feeder/wrapperTests.cpp rename to tests/feeder/wrapper_tests.cpp index d8e43f14..c9541728 100644 --- a/tests/feeder/wrapperTests.cpp +++ b/tests/feeder/wrapper_tests.cpp @@ -18,7 +18,8 @@ class WrapperNodeTest : public ::testing::Test { }; TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { - // SetUp("tests/data/chakra.0.et"); + // tests/data/small_chakra.0.json is a pruned dataset for quick tests + // tests/data/chakra.0.json is the full dataset, which is also available SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); uint64_t firstNodeID = node.getNodeID(); @@ -30,7 +31,6 @@ TEST_F(WrapperNodeTest, ConstructorNodeIDTest) { } TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); uint64_t firstNodeType = node.getNodeType(); @@ -44,7 +44,6 @@ TEST_F(WrapperNodeTest, ConstructorNodeValuesTest) { } TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { - // std::string filename = "tests/data/chakra.0.et"; std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); @@ -65,7 +64,6 @@ TEST_F(WrapperNodeTest, ConstructorWrapperNodeTest) { } TEST_F(WrapperNodeTest, RemoveTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -81,7 +79,6 @@ TEST_F(WrapperNodeTest, RemoveTest) { } TEST_F(WrapperNodeTest, RemoveAndGetNextTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -91,7 +88,6 @@ TEST_F(WrapperNodeTest, RemoveAndGetNextTest) { } TEST_F(WrapperNodeTest, FreeChildrenTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.lookupNode(216); ASSERT_EQ(node.getNodeID(), 216); @@ -103,7 +99,6 @@ TEST_F(WrapperNodeTest, FreeChildrenTest) { } TEST_F(WrapperNodeTest, HasNodesToIssueTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.getNextIssuableNode(); ASSERT_EQ(node.getNodeID(), 216); @@ -113,7 +108,6 @@ TEST_F(WrapperNodeTest, HasNodesToIssueTest) { } TEST_F(WrapperNodeTest, PushBackIssuableNodeTest) { - // SetUp("tests/data/chakra.0.et"); SetUp("tests/data/small_chakra.0.json"); node.pushBackIssuableNode(217); node.getNextIssuableNode(); @@ -123,7 +117,6 @@ TEST_F(WrapperNodeTest, PushBackIssuableNodeTest) { } TEST_F(WrapperNodeTest, AddNodeTest) { - // std::string filename = "tests/data/chakra.0.et"; std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); @@ -151,7 +144,6 @@ TEST_F(WrapperNodeTest, AddNodeTest) { } TEST_F(WrapperNodeTest, NodeGetChildrenTest) { - // std::string filename = "tests/data/chakra.0.et"; std::string filename = "tests/data/small_chakra.0.json"; std::string ext = filename.substr(filename.find_last_of(".") + 1); SetUp(filename); From 61c74d881d4304ac6e5e5d0cffa76e15d200cf66 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Wed, 25 Sep 2024 15:32:22 -0400 Subject: [PATCH 26/38] fix lint errors --- src/feeder/json_node.cpp | 8 ++++---- src/feeder/json_node.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/feeder/json_node.cpp b/src/feeder/json_node.cpp index 578bc04e..755efa69 100644 --- a/src/feeder/json_node.cpp +++ b/src/feeder/json_node.cpp @@ -16,8 +16,8 @@ JSONNode::JSONNode(const JSONNode& t) { children_set_json = t.children_set_json; if (node_type == NodeType::COMM_SEND_NODE || \ - node_type == NodeType::COMM_RECV_NODE || \ - node_type == NodeType::COMM_COLL_NODE) { + node_type == NodeType::COMM_RECV_NODE || \ + node_type == NodeType::COMM_COLL_NODE) { tensor_size = t.tensor_size; comm_type = t.comm_type; comm_priority = t.comm_priority; @@ -62,8 +62,8 @@ JSONNode::JSONNode(json data, uint64_t id) { } if (node_type == NodeType::COMM_SEND_NODE || \ - node_type == NodeType::COMM_RECV_NODE || \ - node_type == NodeType::COMM_COLL_NODE) { + node_type == NodeType::COMM_RECV_NODE || \ + node_type == NodeType::COMM_COLL_NODE) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; } catch (...) { diff --git a/src/feeder/json_node.h b/src/feeder/json_node.h index 744b360b..fc75f960 100644 --- a/src/feeder/json_node.h +++ b/src/feeder/json_node.h @@ -10,7 +10,7 @@ using json = nlohmann::json; -enum NodeType: int { +enum NodeType : int { INVALID_NODE = 0, METADATA_NODE = 1, MEM_LOAD_NODE = 2, From f803f335d14a40b517b6abba41b773fbba3bfa30 Mon Sep 17 00:00:00 2001 From: Vinay Ramakrishnaiah Date: Wed, 25 Sep 2024 15:35:09 -0400 Subject: [PATCH 27/38] fix lint errors --- src/feeder/json_node.cpp | 8 ++++---- src/feeder/wrapper_node.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/feeder/json_node.cpp b/src/feeder/json_node.cpp index 755efa69..03f6077e 100644 --- a/src/feeder/json_node.cpp +++ b/src/feeder/json_node.cpp @@ -15,8 +15,8 @@ JSONNode::JSONNode(const JSONNode& t) { children_vec_json = t.children_vec_json; children_set_json = t.children_set_json; - if (node_type == NodeType::COMM_SEND_NODE || \ - node_type == NodeType::COMM_RECV_NODE || \ + if (node_type == NodeType::COMM_SEND_NODE || + node_type == NodeType::COMM_RECV_NODE || node_type == NodeType::COMM_COLL_NODE) { tensor_size = t.tensor_size; comm_type = t.comm_type; @@ -61,8 +61,8 @@ JSONNode::JSONNode(json data, uint64_t id) { std::cerr << "data deps not specified in ET" << std::endl; } - if (node_type == NodeType::COMM_SEND_NODE || \ - node_type == NodeType::COMM_RECV_NODE || \ + if (node_type == NodeType::COMM_SEND_NODE || + node_type == NodeType::COMM_RECV_NODE || node_type == NodeType::COMM_COLL_NODE) { try { tensor_size = data["workload_graph"][id]["tensor_size"]; diff --git a/src/feeder/wrapper_node.h b/src/feeder/wrapper_node.h index d08ca0de..d8d4d18d 100644 --- a/src/feeder/wrapper_node.h +++ b/src/feeder/wrapper_node.h @@ -1,8 +1,8 @@ #pragma once -#include "json_node.h" #include "et_feeder.h" #include "et_feeder_node.h" +#include "json_node.h" using json = nlohmann::json; From 6ed6e6647de50f7ac4dd3d2f16a7ee0b4deb1266 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Mon, 11 Nov 2024 12:06:04 +0100 Subject: [PATCH 28/38] Specify the kineto filepath explicitly when running HTA analysis Without specifying the kineto filepath explicitly, HTA may pick arbitrary files from the `trace_dir` and either provide incorrect analysis results, or fail in some weird ways. --- src/trace_link/trace_linker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/trace_link/trace_linker.py b/src/trace_link/trace_linker.py index 71696ca8..1123e45d 100644 --- a/src/trace_link/trace_linker.py +++ b/src/trace_link/trace_linker.py @@ -117,7 +117,7 @@ def load_sync_dependencies( sync_dependencies = {} absolute_kineto_file = os.path.abspath(kineto_file) trace_dir = os.path.dirname(absolute_kineto_file) - trace_analysis = TraceAnalysis(trace_dir=trace_dir) + trace_analysis = TraceAnalysis(trace_dir=trace_dir, trace_files={rank: kineto_file}) cp_graph, success = trace_analysis.critical_path_analysis( rank=rank, annotation=annotation, instance_id=instance_id ) From 4fb397e4f0951a27ffdcece8be1fa54a1597fb2d Mon Sep 17 00:00:00 2001 From: Will Won Date: Sun, 3 Nov 2024 01:22:20 -0500 Subject: [PATCH 29/38] Update is_cpu_op to default to false --- src/feeder/et_feeder_node.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index e0427e41..4b5e99e5 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -8,7 +8,7 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { this->id_ = node->id(); this->name_ = node->name(); this->runtime_ = node->duration_micros(); - this->is_cpu_op_ = 1; + this->is_cpu_op_ = 0; for (const auto& attr : node->attr()) { const string& attr_name = attr.name(); From 40ce3be8414f7c5181a50b894fd3cf848957361f Mon Sep 17 00:00:00 2001 From: Joongun Park <8554137+JoongunPark@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:48:08 -0400 Subject: [PATCH 30/38] Fix mishandling All-to-All communication --- src/converter/pytorch_converter.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/converter/pytorch_converter.py b/src/converter/pytorch_converter.py index ea383a51..01498b14 100644 --- a/src/converter/pytorch_converter.py +++ b/src/converter/pytorch_converter.py @@ -350,6 +350,11 @@ def get_protobuf_node_type_from_json_node( return COMM_SEND_NODE if "recv" in keyword: return COMM_RECV_NODE + # In NCCL, all-to-all communication is implemented using point-to-point + # communications. More details can be found here: + # https://docs.nvidia.com/deeplearning/nccl/user-guide/docs/usage/p2p.html + if "nccl:all_to_all" in keyword: + return COMM_COLL_NODE if "ncclKernel" in json_node.name or "ncclDevKernel" in json_node.name: return COMM_COLL_NODE return COMP_NODE @@ -379,6 +384,10 @@ def get_collective_comm_type(self, name: str) -> int: for key in comm_type_mapping: if key in normalized_name: return comm_type_mapping[key] + # If both COMM_COLL_NAME and ncclDevKernel_SendRecv are present, this is nccl:all_to_all. + if "ncclDevKernel_SendRecv" in name: + return comm_type_mapping["alltoall"] + raise ValueError( f"The name '{name}' does not correspond to a recognized collective communication type. " "The converter determines collective communication types based on the node name of a GPU operator. " From 470bde12ce4af1db384283b2620f44084e620edb Mon Sep 17 00:00:00 2001 From: Joongun Park <8554137+JoongunPark@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:15:42 -0400 Subject: [PATCH 31/38] Update logging.info to logging.debug to make it consistent --- src/converter/pytorch_converter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/converter/pytorch_converter.py b/src/converter/pytorch_converter.py index 01498b14..400d94ac 100644 --- a/src/converter/pytorch_converter.py +++ b/src/converter/pytorch_converter.py @@ -470,7 +470,7 @@ def convert_ctrl_dep_to_data_dep( for sync_dep in json_node.sync_dep: if sync_dep not in current_node.data_deps: current_node.data_deps.append(sync_dep) - logging.info( + logging.debug( f"Node ID {current_node.id} now has an synchonization dependency on Node ID {sync_dep}" ) From ed7e286c5149b5482d21b7a418777136f42a54ef Mon Sep 17 00:00:00 2001 From: Joongun Park <8554137+JoongunPark@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:16:23 -0400 Subject: [PATCH 32/38] Eliminate false positive sync dependency --- src/converter/pytorch_converter.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/converter/pytorch_converter.py b/src/converter/pytorch_converter.py index 400d94ac..b9ade2b9 100644 --- a/src/converter/pytorch_converter.py +++ b/src/converter/pytorch_converter.py @@ -469,11 +469,15 @@ def convert_ctrl_dep_to_data_dep( if json_node.sync_dep: for sync_dep in json_node.sync_dep: if sync_dep not in current_node.data_deps: - current_node.data_deps.append(sync_dep) - logging.debug( - f"Node ID {current_node.id} now has an synchonization dependency on Node ID {sync_dep}" - ) - + # Found a bug encoding false dependency HTA. + # Compare start_time to eliminate false sync dependency. + prior_node = protobuf_node_map.get(sync_dep) + if prior_node is not None and prior_node.start_time_micros < current_node.start_time_micros: + current_node.data_deps.append(sync_dep) + logging.debug( + f"Node ID {current_node.id} now has an synchonization dependency on Node ID " + f"{sync_dep}" + ) # Add children to the stack children_chakra_ids = [child.id for child in json_node.children] for child_chakra_id in sorted(children_chakra_ids, reverse=True): From b3dca0beabdd9b8d800de8197d3b9592cc5d3737 Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Thu, 14 Nov 2024 01:21:59 -0500 Subject: [PATCH 33/38] PyTorch nightly needs to support 1.1.1-chakra.0.0.4. --- src/converter/pytorch_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/converter/pytorch_node.py b/src/converter/pytorch_node.py index 86b59acc..e2850104 100644 --- a/src/converter/pytorch_node.py +++ b/src/converter/pytorch_node.py @@ -47,7 +47,7 @@ class PyTorchNode: pg_name (str): Process Group name for the inter-GPU communication. """ - SUPPORTED_VERSIONS = ["1.0.2-chakra.0.0.4", "1.0.3-chakra.0.0.4", "1.1.0-chakra.0.0.4"] + SUPPORTED_VERSIONS = ["1.0.2-chakra.0.0.4", "1.0.3-chakra.0.0.4", "1.1.0-chakra.0.0.4", "1.1.1-chakra.0.0.4"] def __init__(self, schema: str, node_data: Dict[str, Any]) -> None: """ @@ -86,7 +86,7 @@ def parse_data(self, node_data: Dict[str, Any]) -> None: node_data (Dict[str, Any]): The node data to be parsed. """ if self.schema in self.SUPPORTED_VERSIONS: - if self.schema in ["1.0.2-chakra.0.0.4", "1.0.3-chakra.0.0.4", "1.1.0-chakra.0.0.4"]: + if self.schema in ["1.0.2-chakra.0.0.4", "1.0.3-chakra.0.0.4", "1.1.0-chakra.0.0.4", "1.1.1-chakra.0.0.4"]: self._parse_data_1_0_3_chakra_0_0_4(node_data) else: raise ValueError( From cc660d0187d85723e80d2724fb83746d607f0b6e Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Thu, 14 Nov 2024 01:23:07 -0500 Subject: [PATCH 34/38] Get pg_name from record_param_comms for collectives --- src/converter/pytorch_converter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/converter/pytorch_converter.py b/src/converter/pytorch_converter.py index b9ade2b9..48f307c4 100644 --- a/src/converter/pytorch_converter.py +++ b/src/converter/pytorch_converter.py @@ -346,6 +346,8 @@ def get_protobuf_node_type_from_json_node( if parent_node.name == "record_param_comms" else parent_node.name ) + if parent_node.name == "record_param_comms" and parent_node.pg_name != "": + json_node.pg_name = parent_node.pg_name if "send" in keyword: return COMM_SEND_NODE if "recv" in keyword: From c7c7c054bb23bdc4044b7a4b3847987150c883b5 Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Thu, 14 Nov 2024 01:25:34 -0500 Subject: [PATCH 35/38] Update trace_linker to use external_id for finding GPU op's parent CPU op --- src/trace_link/trace_linker.py | 73 +++++++++++---------------- tests/trace_link/test_trace_linker.py | 18 +++---- 2 files changed, 39 insertions(+), 52 deletions(-) diff --git a/src/trace_link/trace_linker.py b/src/trace_link/trace_linker.py index 1123e45d..2f423a95 100644 --- a/src/trace_link/trace_linker.py +++ b/src/trace_link/trace_linker.py @@ -541,7 +541,7 @@ def map_host_to_device_ops( ]: """Map Chakra host operators to corresponding device operators.""" logging.debug("Mapping Charka host operators to corresponding device operators.") - cpu_ev_idx_to_gpu_ops_map = self.group_gpu_ops_by_cpu_launchers( + cpu_external_id_to_gpu_ops_map = self.group_gpu_ops_by_cpu_launchers( kineto_gpu_ops, kineto_correlation_cuda_runtime_map, sorted_kineto_cpu_ops, sorted_kineto_cpu_op_ts ) @@ -569,7 +569,7 @@ def map_host_to_device_ops( ) = self.link_ops( host_op, kineto_op, - cpu_ev_idx_to_gpu_ops_map, + cpu_external_id_to_gpu_ops_map, kineto_rf_id_to_device_op_map, kineto_external_id_to_kineto_op_map, ) @@ -593,7 +593,7 @@ def group_gpu_ops_by_cpu_launchers( """ Group GPU operators based on their corresponding CPU launchers. - This is determined by the 'ev_idx' which links GPU operators to their initiating CPU launcher events. + This is determined by the 'external_id' which links GPU operators to their initiating CPU launcher events. Args: kineto_gpu_ops (List[KinetoOperator]): List of Kineto GPU operators. @@ -607,9 +607,9 @@ def group_gpu_ops_by_cpu_launchers( Dict[int, List[KinetoOperator]]: Mapping from CPU launch event indices to GPU operators. Raises: - ValueError: If 'ev_idx' is missing for any GPU operator. + ValueError: If 'external_id' is missing for any GPU operator. """ - cpu_ev_idx_to_gpu_ops_map = {} + cpu_external_id_to_gpu_ops_map = {} for gpu_op in kineto_gpu_ops: parent_cpu_op = self.find_parent_cpu_op( gpu_op, kineto_correlation_cuda_runtime_map, sorted_kineto_cpu_ops, sorted_kineto_cpu_op_ts @@ -619,9 +619,9 @@ def group_gpu_ops_by_cpu_launchers( logging.warning(warning_msg) continue - if parent_cpu_op.ev_idx == "": + if parent_cpu_op.external_id == "": error_msg = ( - f"Missing 'ev_idx' for CPU operator {parent_cpu_op.name}. " + f"Missing 'external_id' for CPU operator {parent_cpu_op.name}. " f"Cannot link GPU op {gpu_op.name} to {parent_cpu_op.name}." ) logging.warning(error_msg) @@ -629,9 +629,9 @@ def group_gpu_ops_by_cpu_launchers( logging.debug(f"group_gpu_ops_by_cpu_launchers '{parent_cpu_op.name}' -> '{gpu_op.name}'") - cpu_ev_idx_to_gpu_ops_map.setdefault(parent_cpu_op.ev_idx, []).append(gpu_op) + cpu_external_id_to_gpu_ops_map.setdefault(parent_cpu_op.external_id, []).append(gpu_op) - return cpu_ev_idx_to_gpu_ops_map + return cpu_external_id_to_gpu_ops_map def find_parent_cpu_op( self, @@ -713,48 +713,35 @@ def find_closest_op( Returns: Optional[KinetoOperator]: The closest Kineto operator if found. """ - # Searching for the closest timestamp index + # Step 1: Find the initial closest index index = bisect.bisect_left(sorted_kineto_cpu_op_ts, ts) if index == 0: # All operators are later than the timestamp return None - else: - # The operator immediately before the index is the closest one before the timestamp - closest_op = sorted_kineto_cpu_ops[index - 1] - - # Check for NCCL specifics: if it's an NCCL operation and 'nccl:coalesced' should be skipped - if "nccl" in kineto_gpu_op.name.lower() and closest_op.name == "nccl:coalesced": - # Move back to find a non-'nccl:coalesced' operator, if available - for new_index in range(index - 2, -1, -1): - potential_op = sorted_kineto_cpu_ops[new_index] - if potential_op.tid == kineto_gpu_op.tid and potential_op.name != "nccl:coalesced": - return potential_op - # If no valid alternative found before 'nccl:coalesced', continue search forward - index = index - 1 # Adjust index to skip 'nccl:coalesced' - - # After skipping 'nccl:coalesced', verify that the closest operation is on the same thread - # as the GPU operation - if closest_op.tid == kineto_gpu_op.tid: - return closest_op - - # If the tids do not match, search forward to find the closest matching tid - for i in range(index - 1, -1, -1): - op = sorted_kineto_cpu_ops[i] - if op.tid == kineto_gpu_op.tid: - if "nccl" in kineto_gpu_op.name.lower() and op.name == "nccl:coalesced": - continue # Skip 'nccl:coalesced' if it's an NCCL-related GPU operation - if op.timestamp <= ts: - return op - - # If no matching tid is found going forward, return None - return None + + # Step 2: Find the closest operator + tid_only_match = None # Track the best operator with matching tid + for i in range(index - 1, -1, -1): + op = sorted_kineto_cpu_ops[i] + # Skip 'nccl:coalesced' for NCCL-related GPU operations + if "nccl" in kineto_gpu_op.name.lower() and op.name == "nccl:coalesced": + continue + # Return the operator matching both tid and external_id + if op.tid == kineto_gpu_op.tid and op.external_id == kineto_gpu_op.external_id: + return op + # Track the tid_only_match operator with matching tid if no full match is found + if tid_only_match is None and op.tid == kineto_gpu_op.tid: + tid_only_match = op + + # Step 3: Return the best match or None if no match is found + return tid_only_match def link_ops( self, host_op: PyTorchOperator, kineto_op: KinetoOperator, - cpu_ev_idx_to_gpu_ops_map: Dict[int, List[KinetoOperator]], + cpu_external_id_to_gpu_ops_map: Dict[int, List[KinetoOperator]], kineto_rf_id_to_device_op_map: Dict[int, KinetoOperator], kineto_external_id_to_kineto_op_map: Dict[int, KinetoOperator], ) -> Tuple[List[KinetoOperator], int, int, int, Optional[int]]: @@ -764,7 +751,7 @@ def link_ops( Args: host_op (PyTorchOperator): Chakra host operator to link. kineto_op (KinetoOperator): Corresponding Kineto operator. - cpu_ev_idx_to_gpu_ops_map (Dict[int, List[KinetoOperator]]): GPU ops mapping. + cpu_external_id_to_gpu_ops_map (Dict[int, List[KinetoOperator]]): GPU ops mapping. kineto_rf_id_to_device_op_map (Dict[int, KinetoOperator]): Kineto operator mapping. kineto_external_id_to_kineto_op_map (Dict[int, KinetoOperator]): Mapping from external id to KinetoOperators. @@ -779,7 +766,7 @@ def link_ops( - List[int]: List of synchronization dependency IDs. """ kineto_op.host_op = host_op - linked_gpu_ops = cpu_ev_idx_to_gpu_ops_map.get(kineto_op.ev_idx, []) + linked_gpu_ops = cpu_external_id_to_gpu_ops_map.get(kineto_op.external_id, []) inclusive_dur = kineto_op.inclusive_dur exclusive_dur = kineto_op.exclusive_dur timestamp = kineto_op.timestamp diff --git a/tests/trace_link/test_trace_linker.py b/tests/trace_link/test_trace_linker.py index a0441ae4..8430867e 100644 --- a/tests/trace_link/test_trace_linker.py +++ b/tests/trace_link/test_trace_linker.py @@ -381,14 +381,14 @@ def test_group_gpu_ops_by_cpu_launchers(trace_linker): kineto_gpu_op2.tid = 2 kineto_runtime_op1 = MagicMock(spec=KinetoOperator) - kineto_runtime_op1.ev_idx = "cpu_op1" + kineto_runtime_op1.external_id = "cpu_op1" kineto_runtime_op1.timestamp = 100 kineto_runtime_op1.tid = 1 kineto_runtime_op1.name = "runtime_op1" kineto_runtime_op1.correlation = 123 kineto_runtime_op2 = MagicMock(spec=KinetoOperator) - kineto_runtime_op2.ev_idx = "cpu_op2" + kineto_runtime_op2.external_id = "cpu_op2" kineto_runtime_op2.timestamp = 200 kineto_runtime_op2.tid = 2 kineto_runtime_op2.name = "runtime_op2" @@ -445,7 +445,7 @@ def test_find_parent_cpu_op(mock_find_closest_op, trace_linker): MagicMock(spec=PyTorchOperator, id=1), MagicMock( spec=KinetoOperator, - ev_idx="1", + external_id="1", inclusive_dur=100, exclusive_dur=50, timestamp=123456, @@ -461,7 +461,7 @@ def test_find_parent_cpu_op(mock_find_closest_op, trace_linker): MagicMock(spec=PyTorchOperator, id=2), MagicMock( spec=KinetoOperator, - ev_idx="2", + external_id="2", inclusive_dur=200, exclusive_dur=150, timestamp=223456, @@ -491,7 +491,7 @@ def test_link_ops( ): mock_get_inter_thread_dep.return_value = expected_inter_thread_dep - cpu_ev_idx_to_gpu_ops_map = {kineto_op.ev_idx: expected_linked_gpu_ops} + cpu_external_id_to_gpu_ops_map = {kineto_op.external_id: expected_linked_gpu_ops} kineto_rf_id_to_kineto_op_map = {1: MagicMock(spec=KinetoOperator, host_op=MagicMock(id=42))} kineto_external_id_to_kineto_op_map = { 2: MagicMock(spec=KinetoOperator, host_op=MagicMock(id=3)), @@ -501,7 +501,7 @@ def test_link_ops( result = trace_linker.link_ops( host_op, kineto_op, - cpu_ev_idx_to_gpu_ops_map, + cpu_external_id_to_gpu_ops_map, kineto_rf_id_to_kineto_op_map, kineto_external_id_to_kineto_op_map, ) @@ -520,7 +520,7 @@ def test_link_ops_with_no_gpu_ops(trace_linker): host_op = MagicMock(spec=PyTorchOperator, id=1) kineto_op = MagicMock( spec=KinetoOperator, - ev_idx="1", + external_id="1", inclusive_dur=100, exclusive_dur=50, timestamp=123456, @@ -529,14 +529,14 @@ def test_link_ops_with_no_gpu_ops(trace_linker): sync_dep=[], ) - cpu_ev_idx_to_gpu_ops_map = {} + cpu_external_id_to_gpu_ops_map = {} kineto_rf_id_to_kineto_op_map = {} kineto_external_id_to_kineto_op_map = {} result = trace_linker.link_ops( host_op, kineto_op, - cpu_ev_idx_to_gpu_ops_map, + cpu_external_id_to_gpu_ops_map, kineto_rf_id_to_kineto_op_map, kineto_external_id_to_kineto_op_map, ) From 6d8dea8e0fb230c4348d1706c05eff7a5c8f1c9d Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Fri, 15 Nov 2024 23:34:56 -0500 Subject: [PATCH 36/38] Handling HTA Errors in Chakra --- src/trace_link/trace_linker.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/trace_link/trace_linker.py b/src/trace_link/trace_linker.py index 2f423a95..27827c7e 100644 --- a/src/trace_link/trace_linker.py +++ b/src/trace_link/trace_linker.py @@ -118,11 +118,17 @@ def load_sync_dependencies( absolute_kineto_file = os.path.abspath(kineto_file) trace_dir = os.path.dirname(absolute_kineto_file) trace_analysis = TraceAnalysis(trace_dir=trace_dir, trace_files={rank: kineto_file}) - cp_graph, success = trace_analysis.critical_path_analysis( - rank=rank, annotation=annotation, instance_id=instance_id - ) - if not success: - logging.error("Failed to load Critical Path Graph") + try: + cp_graph, success = trace_analysis.critical_path_analysis( + rank=rank, annotation=annotation, instance_id=instance_id + ) + if not success: + logging.error("Critical path analysis completed but failed to load Critical Path Graph.") + return sync_dependencies + + except ValueError as e: + logging.error("Critical path analysis encountered an invalid graph structure: %s", e) + # Optionally, you could log more details or include rank-specific information if relevant return sync_dependencies raw_events = trace_analysis.t.get_raw_trace_for_one_rank(rank=rank)["traceEvents"] From f51050d375c0c465b31d3280b58b416a2ea5c538 Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:16:12 -0500 Subject: [PATCH 37/38] Fix error encoding METADATA node --- src/converter/pytorch_converter.py | 3 +++ src/converter/pytorch_node.py | 9 +++++++++ tests/converter/test_pytorch_converter.py | 11 +++++++---- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/converter/pytorch_converter.py b/src/converter/pytorch_converter.py index 48f307c4..b5ace295 100644 --- a/src/converter/pytorch_converter.py +++ b/src/converter/pytorch_converter.py @@ -11,6 +11,7 @@ COMM_RECV_NODE, COMM_SEND_NODE, COMP_NODE, + METADATA_NODE, REDUCE_SCATTER, GlobalMetadata, ) @@ -338,6 +339,8 @@ def get_protobuf_node_type_from_json_node( Returns: int: The corresponding Chakra node type. """ + if json_node.is_metadata_op(): + return METADATA_NODE if json_node.is_gpu_op(): if "ncclDevKernel_SendRecv" in json_node.name: parent_node = json_node_map[json_node.parent] diff --git a/src/converter/pytorch_node.py b/src/converter/pytorch_node.py index e2850104..31635db5 100644 --- a/src/converter/pytorch_node.py +++ b/src/converter/pytorch_node.py @@ -137,6 +137,15 @@ def get_op_type(self) -> PyTorchNodeType: else: return PyTorchNodeType.LABEL + def is_metadata_op(self) -> bool: + """ + Check if the node is a METADATA operator. + + Returns + bool: True if the node is a METADATA operator, False otherwise. + """ + return self.get_op_type() == PyTorchNodeType.METADATA + def is_cpu_op(self) -> bool: """ Check if the node is a CPU operator. diff --git a/tests/converter/test_pytorch_converter.py b/tests/converter/test_pytorch_converter.py index 88f2abf9..c01c7569 100644 --- a/tests/converter/test_pytorch_converter.py +++ b/tests/converter/test_pytorch_converter.py @@ -10,6 +10,7 @@ BROADCAST, COMM_COLL_NODE, COMP_NODE, + METADATA_NODE, REDUCE_SCATTER, ) from chakra.schema.protobuf.et_def_pb2 import Node as ChakraNode @@ -167,10 +168,11 @@ def test_write_chakra_et(mock_file: MagicMock, sample_pytorch_data: Dict) -> Non @pytest.mark.parametrize( "pytorch_node_data, expected_type", [ - ({"name": "ncclKernel", "is_gpu_op": True}, COMM_COLL_NODE), - ({"name": "ncclDevKernel", "is_gpu_op": True}, COMM_COLL_NODE), - ({"name": "c10d::all_reduce", "is_gpu_op": True}, COMP_NODE), - ({"name": "other_op", "is_gpu_op": False}, COMP_NODE), + ({"name": "process_group:init", "is_gpu_op": False, "is_metadata_op": True}, METADATA_NODE), + ({"name": "ncclKernel", "is_gpu_op": True, "is_metadata_op": False}, COMM_COLL_NODE), + ({"name": "ncclDevKernel", "is_gpu_op": True, "is_metadata_op": False}, COMM_COLL_NODE), + ({"name": "c10d::all_reduce", "is_gpu_op": True, "is_metadata_op": False}, COMP_NODE), + ({"name": "other_op", "is_gpu_op": False, "is_metadata_op": False}, COMP_NODE), ], ) def test_get_protobuf_node_type_from_json_node(pytorch_node_data: Dict, expected_type: int) -> None: @@ -178,6 +180,7 @@ def test_get_protobuf_node_type_from_json_node(pytorch_node_data: Dict, expected pytorch_node = MagicMock(spec=PyTorchNode) pytorch_node.name = pytorch_node_data["name"] pytorch_node.is_gpu_op = MagicMock(return_value=pytorch_node_data["is_gpu_op"]) + pytorch_node.is_metadata_op = MagicMock(return_value=pytorch_node_data["is_metadata_op"]) # Create a mock json_node_map dictionary with actual PyTorchNode instances mock_pytorch_node_data = { From 810ff883a2a60417fe58081e32a261e738047f73 Mon Sep 17 00:00:00 2001 From: JoongunPark <8554137+JoongunPark@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:56:54 -0500 Subject: [PATCH 38/38] Implement getter functions for nodes' inputs/outputs --- src/feeder/et_feeder_node.cpp | 54 +++++++++++++++++++++++++++++++++++ src/feeder/et_feeder_node.h | 12 ++++++++ 2 files changed, 66 insertions(+) diff --git a/src/feeder/et_feeder_node.cpp b/src/feeder/et_feeder_node.cpp index 4b5e99e5..8fa77b1d 100644 --- a/src/feeder/et_feeder_node.cpp +++ b/src/feeder/et_feeder_node.cpp @@ -10,6 +10,18 @@ ETFeederNode::ETFeederNode(std::shared_ptr node) { this->runtime_ = node->duration_micros(); this->is_cpu_op_ = 0; + if (node->has_inputs()) { + this->inputs_values_ = static_cast(node->inputs().values()); + this->inputs_shapes_ = static_cast(node->inputs().shapes()); + this->inputs_types_ = static_cast(node->inputs().types()); + } + + if (node->has_outputs()) { + this->outputs_values_ = static_cast(node->outputs().values()); + this->outputs_shapes_ = static_cast(node->outputs().shapes()); + this->outputs_types_ = static_cast(node->outputs().types()); + } + for (const auto& attr : node->attr()) { const string& attr_name = attr.name(); @@ -144,3 +156,45 @@ uint32_t ETFeederNode::comm_tag() { string ETFeederNode::pg_name() { return pg_name_; } + +string ETFeederNode::get_inputs_values() const { + if (node_->has_inputs()) { + return inputs_values_; + } + return ""; +} + +string ETFeederNode::get_inputs_shapes() const { + if (node_->has_inputs()) { + return inputs_shapes_; + } + return ""; +} + +string ETFeederNode::get_inputs_types() const { + if (node_->has_inputs()) { + return inputs_types_; + } + return ""; +} + +string ETFeederNode::get_outputs_values() const { + if (node_->has_outputs()) { + return outputs_values_; + } + return ""; +} + +string ETFeederNode::get_outputs_shapes() const { + if (node_->has_outputs()) { + return outputs_shapes_; + } + return ""; +} + +string ETFeederNode::get_outputs_types() const { + if (node_->has_outputs()) { + return outputs_types_; + } + return ""; +} diff --git a/src/feeder/et_feeder_node.h b/src/feeder/et_feeder_node.h index c0aede48..d871e2c9 100644 --- a/src/feeder/et_feeder_node.h +++ b/src/feeder/et_feeder_node.h @@ -39,6 +39,12 @@ class ETFeederNode { uint32_t comm_dst(); uint32_t comm_tag(); std::string pg_name(); + std::string get_inputs_values() const; + std::string get_inputs_shapes() const; + std::string get_inputs_types() const; + std::string get_outputs_values() const; + std::string get_outputs_shapes() const; + std::string get_outputs_types() const; private: void assign_attr_val( @@ -67,6 +73,12 @@ class ETFeederNode { uint32_t comm_dst_; uint32_t comm_tag_; std::string pg_name_; + std::string inputs_values_; + std::string inputs_shapes_; + std::string inputs_types_; + std::string outputs_values_; + std::string outputs_shapes_; + std::string outputs_types_; }; } // namespace Chakra