/
githubmirror
/
amphtml
Обзор
Документация
Войти
/
githubmirror
/
amphtml
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
validator/cpp/htmlparser/json/json.cc
370 строк
12 KB
Michael Rybak
Sync for validator cpp engine and cpp htmlparser (#38451)
22 сен 2022, 01:07
Не верифицирован
22 сен 2022, 01:07
9b2cccd
Код
Авторство
О чём код?
#include "cpp/htmlparser/json/json.h" #include <stack> #include "absl/strings/numbers.h" #include "cpp/htmlparser/logging.h" #include "cpp/htmlparser/validators/json.h" namespace htmlparser::json { namespace { // ParseInternal is internal library responsible for parsing/validating JSON and // deserializing them to JSON objects in C++. // // The parsing technology is different. Unlike lexer/tokenizer based approach // this parser is built using a state table auto generated by (validatorgen.cc) // which reads JSON grammar from (jsongrammar.txt). The JSON validator // (validators/json.h) accurately validates the JSON and also provides callbacks // for each parsing event. See switch case statement in ParseInternal::Parse() // method below. // // So essentially, this parser does not actual parsing/tokenizing work but // simply assembles the JSON objects. This makes this parser very fast and // memory efficient. // // Using ParseOptions one can customzie the behavior of the parser to further // optimize and match the project requirements. // // Though clients cannot access this class, this class is not thread safe. // Entire execution should be in one thread execution only. Running validate // in one thread and Parser in another can lead to undefined behavior or even // crash the binary. // // The Validator (validators/json.h) is accurate validator and hence no extra // validation is required in parse internal. For example a string callback is // guaranteed to contain a double quoted string. There is no further check for // existence of double quotes or missing quotes etc. Same applies to different // objects. The callback ARRAY_NEXT_ITEM for example is guaranteed to be in // JsonArray node, so it simply appends the item in the current_node_ which is // JsonArray. class ParseInternal { public: ParseInternal(std::string_view json_str, ParseOptions options); std::unique_ptr<JsonObject> Parse(); private: // We register the callbacks for beginning of these types but populate these // fields only when a proper terminating callback is invoked, for example // ARRAY_NEXT_ITEM, or DICT_VAL_END. The hints can be and/or'ed so in one // state two types NUMBER_T and FLOAT_T can be active. enum TypeHint { UNKNOWN = 1 << 0, NUMBER_T = 1 << 1, FLOAT_T = 1 << 2, STRING_T = 1 << 3, NULL_T = 1 << 4, TRUE_T = 1 << 5, FALSE_T = 1 << 6, NUMBER_AFTER_ZERO_T = 1 << 7, }; // All the following methods must be called only from the validator callbacks, // as it assumes some validation conditions. Calling them outside of callbacks // can lead to undefined behavior. // =========================================================================== // Assembles the current string excluding double quotes. inline std::string_view CurrentString(std::size_t current_index); // Assembles the current number or double based on type hints. inline std::string_view CurrentNumberStr(std::size_t current_index); // Assembles the dictionary key in string format. inline std::string_view DictKey() const; // Creates a new node and places it on the stack. inline void NewNode(JsonObject&& node); // Populates the dictionary item, i.e key/val pair. inline void PopulateDictionaryItem(std::size_t current_index); // Populates the array item, i.e appends the value to existing array. inline void PopulateArrayItem(std::size_t current_index); // Pops the node from the stack. When the node closes example dictionary or // array ends. inline void Pop(); // When new nested node is created, the current node is pushed on to stack // which is popped back when the child node is closed. inline void Push(JsonObject* node); // Assembles root values. i.e. "string", true, false, null, 123, 1.23 etc. inline void AssembleRootValues(std::size_t current_index); // The current node on which all insert/append operations are performed. // In most cases this is either dictionary {} or array []. JsonObject* current_node_; // The actual input json string. std::string_view json_str_; // The parser customization options. ParseOptions parse_options_; // The TypeHint(s) holder. int64_t hints_ = 0; // Various string markers in the json. std::size_t dict_key_start_idx_ = std::string_view::npos; std::size_t dict_key_end_idx_ = std::string_view::npos; std::size_t string_start_idx_ = std::string_view::npos; // The object stack to push parent objects when new nested nodes are created. // Stack must be empty at the end of the parsing or it is an error. std::stack<JsonObject*> obj_stack_; }; ParseInternal::ParseInternal(std::string_view json_str, ParseOptions options) : json_str_(json_str), parse_options_(options) {} std::string_view ParseInternal::CurrentString(std::size_t current_index) { hints_ ^= STRING_T; // The indexes are guaranteed by the validator. return json_str_.substr(string_start_idx_ + 1, current_index - string_start_idx_ - 2); } std::string_view ParseInternal::CurrentNumberStr(std::size_t current_index) { hints_ ^= NUMBER_T; return json_str_.substr(string_start_idx_, current_index - string_start_idx_); } void ParseInternal::NewNode(JsonObject&& node) { if (auto array = current_node_->Get<JsonArray>(); array) { array->Append(std::move(node)); Push(array->Last()); } else if (auto dict = current_node_->Get<JsonDict>(); dict) { auto key = DictKey(); dict->Insert(key, std::move(node)); Push(dict->Get(key)); } } void ParseInternal::PopulateDictionaryItem(std::size_t current_index) { auto dict = current_node_->Get<JsonDict>(); if (hints_ & STRING_T) { auto key = DictKey(); auto value = CurrentString(current_index); if (parse_options_.use_string_references) { dict->Insert<std::string_view>(key, value); } else { dict->Insert<std::string>(key, std::string(value)); } } else if (hints_ & NUMBER_T) { auto str = CurrentNumberStr(current_index); if (hints_ & FLOAT_T) { double num; if (absl::SimpleAtod(str, &num)) { dict->Insert<double>(DictKey(), num); } hints_ ^= FLOAT_T; } else { int64_t num; if (absl::SimpleAtoi(str, &num)) { dict->Insert<int64_t>(DictKey(), num); } } } else if (hints_ & NULL_T) { dict->Insert(DictKey(), nullptr); hints_ ^= NULL_T; } else if (hints_ & TRUE_T) { dict->Insert(DictKey(), true); hints_ ^= TRUE_T; } else if (hints_ & FALSE_T) { dict->Insert(DictKey(), false); hints_ ^= FALSE_T; } } void ParseInternal::PopulateArrayItem(std::size_t current_index) { auto array = current_node_->Get<JsonArray>(); if (hints_ & STRING_T) { auto str = CurrentString(current_index); if (parse_options_.use_string_references) { array->Append<std::string_view>(str); } else { array->Append<std::string>(std::string(str)); } } else if (hints_ & NUMBER_T) { auto str = CurrentNumberStr(current_index); if (hints_ & FLOAT_T) { double num; if (absl::SimpleAtod(str, &num)) { array->Append<double>(num); } hints_ ^= FLOAT_T; } else { int64_t num; if (absl::SimpleAtoi(str, &num)) { array->Append<int64_t>(num); } } } else if (hints_ & NULL_T) { array->Append(nullptr); hints_ ^= NULL_T; } else if (hints_ & TRUE_T) { array->Append(true); hints_ ^= TRUE_T; } else if (hints_ & FALSE_T) { array->Append(false); hints_ ^= FALSE_T; } } void ParseInternal::Pop() { if (!obj_stack_.empty()) { current_node_ = obj_stack_.top(); obj_stack_.pop(); } } void ParseInternal::Push(JsonObject* node) { obj_stack_.push(current_node_); current_node_ = node; } std::string_view ParseInternal::DictKey() const { return json_str_.substr(dict_key_start_idx_, dict_key_end_idx_ - dict_key_start_idx_); } void ParseInternal::AssembleRootValues(std::size_t current_index) { if (hints_ & STRING_T) { if (parse_options_.use_string_references) { *current_node_ = CurrentString(current_index); } else { *current_node_ = std::string(CurrentString(current_index)); } } else if (hints_ & TRUE_T) { *current_node_ = true; } else if (hints_ & FALSE_T) { *current_node_ = false; } else if (hints_ & NULL_T) { *current_node_ = JsonNull(); } else if (hints_ & NUMBER_T) { auto str = CurrentNumberStr(current_index); if (hints_ & FLOAT_T) { double num; if (absl::SimpleAtod(str, &num)) { *current_node_ = num; } hints_ ^= FLOAT_T; } else { int64_t num; if (absl::SimpleAtoi(str, &num)) { *current_node_ = num; } } } hints_ = 0; } std::unique_ptr<JsonObject> ParseInternal::Parse() { auto value = std::make_unique<JsonObject>(true); current_node_ = value.get(); auto result = Validate(json_str_, [&](auto cb_code, auto state_code, int i) { switch (cb_code) { case CallbackCode::ROOT_DICT: { *current_node_ = JsonDict(); break; } case CallbackCode::ROOT_ARRAY: { *current_node_ = JsonArray(); break; } case CallbackCode::ROOT_STRING: case CallbackCode::STRING_T: { hints_ |= STRING_T; string_start_idx_ = i; break; } case CallbackCode::ROOT_BOOL_TRUE: case CallbackCode::TRUE_T: { hints_ |= TRUE_T; break; } case CallbackCode::ROOT_BOOL_FALSE: case CallbackCode::FALSE_T: { hints_ |= FALSE_T; break; } case CallbackCode::ROOT_NULL_VAL: case CallbackCode::NULL_T: { hints_ |= NULL_T; break; } case CallbackCode::ROOT_NUMBER: case CallbackCode::NUMBER_T: { hints_ |= NUMBER_T; string_start_idx_ = i; break; } case CallbackCode::ROOT_NUMBER_AFTER_ZERO: { hints_ |= NUMBER_AFTER_ZERO_T; break; } case CallbackCode::FLOATING_POINT_T: { hints_ |= FLOAT_T; break; } case CallbackCode::DICT_KEY_BEGIN: { // If this is EOF, validator will invalidate the json, so the increment // below will never cause IndexOutOfBoundsException. dict_key_start_idx_ = i + 1 /* skip start quote */; break; } case CallbackCode::DICT_VAL_END: { PopulateDictionaryItem(i); break; } case CallbackCode::DICT_KEY_END: { dict_key_end_idx_ = i - 1; /* skip end quote */ break; } case CallbackCode::DICT_END: { PopulateDictionaryItem(i); Pop(); break; } case CallbackCode::ARRAY_VAL_END: { PopulateArrayItem(i); break; } case CallbackCode::ARRAY_END: { PopulateArrayItem(i); Pop(); break; } case CallbackCode::DICT_T: { JsonDict dict; JsonObject dict_node(dict); NewNode(std::move(dict_node)); break; } case CallbackCode::ARRAY_T: { JsonArray arr; JsonObject array_node(arr); NewNode(std::move(array_node)); break; } case CallbackCode::PARSE_END: { AssembleRootValues(i); CHECK(obj_stack_.empty()) << "Json objects not assembled properly."; break; } case CallbackCode::NONE: { // Not possible. Making compiler happy. CHECK(false) << "JSONParser reached unreachable condition."; break; } // NOTE: Do not add default: label. } }); if (result.first) { return value; } return nullptr; } } // namespace std::unique_ptr<JsonObject> Parse(std::string_view json_str, ParseOptions options) { return ParseInternal(json_str, options).Parse(); } } // namespace htmlparser::json