| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/json/json_reader.h" |
| |
| #include <string_view> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "build/build_config.h" |
| #include "base/strings/string_view_rust.h" |
| #include "third_party/rust/serde_json_lenient/v0_2/wrapper/functions.h" |
| #include "third_party/rust/serde_json_lenient/v0_2/wrapper/lib.rs.h" |
| |
| namespace { |
| const char kSecurityJsonParsingTime[] = "Security.JSONParser.ParsingTime"; |
| } // namespace |
| |
| // This namespace defines FFI-friendly functions that are be called from Rust in |
| // //third_party/rust/serde_json_lenient/v0_2/wrapper/. |
| namespace serde_json_lenient { |
| |
| base::Value::List& list_append_list(base::Value::List& ctx) { |
| ctx.Append(base::Value::List()); |
| return ctx.back().GetList(); |
| } |
| |
| base::Value::Dict& list_append_dict(base::Value::List& ctx) { |
| ctx.Append(base::Value::Dict()); |
| return ctx.back().GetDict(); |
| } |
| |
| void list_append_none(base::Value::List& ctx) { |
| ctx.Append(base::Value()); |
| } |
| |
| void list_append_bool(base::Value::List& ctx, bool val) { |
| ctx.Append(val); |
| } |
| |
| void list_append_i32(base::Value::List& ctx, int32_t val) { |
| ctx.Append(val); |
| } |
| |
| void list_append_f64(base::Value::List& ctx, double val) { |
| ctx.Append(val); |
| } |
| |
| void list_append_str(base::Value::List& ctx, rust::Str val) { |
| ctx.Append(std::string(val)); |
| } |
| |
| base::Value::List& dict_set_list(base::Value::Dict& ctx, rust::Str key) { |
| base::Value* value = |
| ctx.Set(base::RustStrToStringView(key), base::Value::List()); |
| return value->GetList(); |
| } |
| |
| base::Value::Dict& dict_set_dict(base::Value::Dict& ctx, rust::Str key) { |
| base::Value* value = |
| ctx.Set(base::RustStrToStringView(key), base::Value::Dict()); |
| return value->GetDict(); |
| } |
| |
| void dict_set_none(base::Value::Dict& ctx, rust::Str key) { |
| ctx.Set(base::RustStrToStringView(key), base::Value()); |
| } |
| |
| void dict_set_bool(base::Value::Dict& ctx, rust::Str key, bool val) { |
| ctx.Set(base::RustStrToStringView(key), val); |
| } |
| |
| void dict_set_i32(base::Value::Dict& ctx, rust::Str key, int32_t val) { |
| ctx.Set(base::RustStrToStringView(key), val); |
| } |
| |
| void dict_set_f64(base::Value::Dict& ctx, rust::Str key, double val) { |
| ctx.Set(base::RustStrToStringView(key), val); |
| } |
| |
| void dict_set_str(base::Value::Dict& ctx, rust::Str key, rust::Str val) { |
| ctx.Set(base::RustStrToStringView(key), std::string(val)); |
| } |
| |
| namespace { |
| |
| base::JSONReader::Result DecodeJSONInRust(std::string_view json, |
| int options, |
| size_t max_depth) { |
| const JsonOptions rust_options = { |
| .allow_trailing_commas = |
| (options & base::JSON_ALLOW_TRAILING_COMMAS) != 0, |
| .replace_invalid_characters = |
| (options & base::JSON_REPLACE_INVALID_CHARACTERS) != 0, |
| .allow_comments = (options & base::JSON_ALLOW_COMMENTS) != 0, |
| .allow_newlines = (options & base::JSON_ALLOW_NEWLINES_IN_STRINGS) != 0, |
| .allow_vert_tab = (options & base::JSON_ALLOW_VERT_TAB) != 0, |
| .allow_x_escapes = (options & base::JSON_ALLOW_X_ESCAPES) != 0, |
| .max_depth = max_depth, |
| }; |
| |
| base::Value::List list; |
| DecodeError error; |
| bool ok = |
| decode_json(base::StringViewToRustSlice(json), rust_options, list, error); |
| |
| if (!ok) { |
| return base::unexpected(base::JSONReader::Error{ |
| .message = std::string(error.message), |
| .line = error.line, |
| .column = error.column, |
| }); |
| } |
| |
| return std::move(list.back()); |
| } |
| |
| } // namespace |
| } // namespace serde_json_lenient |
| |
| namespace base { |
| |
| std::string JSONReader::Error::ToString() const { |
| return base::StrCat({"line ", base::NumberToString(line), ", column ", |
| base::NumberToString(column), ": ", message}); |
| } |
| |
| // static |
| std::optional<Value> JSONReader::Read(std::string_view json, |
| int options, |
| size_t max_depth) { |
| SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime); |
| |
| JSONReader::Result result = |
| serde_json_lenient::DecodeJSONInRust(json, options, max_depth); |
| if (!result.has_value()) { |
| return std::nullopt; |
| } |
| return std::move(*result); |
| } |
| |
| // static |
| std::optional<Value::Dict> JSONReader::ReadDict(std::string_view json, |
| int options, |
| size_t max_depth) { |
| std::optional<Value> value = Read(json, options, max_depth); |
| if (!value || !value->is_dict()) { |
| return std::nullopt; |
| } |
| return std::move(*value).TakeDict(); |
| } |
| |
| // static |
| std::optional<Value::List> JSONReader::ReadList(std::string_view json, |
| int options, |
| size_t max_depth) { |
| std::optional<Value> value = Read(json, options, max_depth); |
| if (!value || !value->is_list()) { |
| return std::nullopt; |
| } |
| return std::move(*value).TakeList(); |
| } |
| |
| // static |
| JSONReader::Result JSONReader::ReadAndReturnValueWithError( |
| std::string_view json, |
| int options) { |
| SCOPED_UMA_HISTOGRAM_TIMER_MICROS(kSecurityJsonParsingTime); |
| return serde_json_lenient::DecodeJSONInRust(json, options, |
| internal::kAbsoluteMaxDepth); |
| } |
| |
| } // namespace base |