perf: on Windows, make Archive::HeaderIntegrity() faster (#46509)

* perf: do not clone the map each time we call Archive::HeaderIntegrity()

* perf: use absl::flat_hash_map for the integrity cache

* perf: do not clone the JSON payload string

* perf: preallocate capacity for the integrity cache

* perf: use move variant of insert_or_assign()

* refactor: simplify integrity cache building

remove unnecessary std::optional<>

* refactor: use base::FindOrNull()

* refactor: remove unused #includes

* refactor: make variable types explicit

* fix: make res_size unsigned

* refactor: put GetIntegrityConfigCache() in an unnamed namespace

refator: put LoadIntegrityConfig() in an unnamed namespace

* fix: oops, missing rel_path_utf8 key

* fix: oops, fix Wunreachable-code-return
This commit is contained in:
Charles Kerr 2025-04-07 04:10:29 -05:00 committed by GitHub
parent 41d8f90d68
commit c0fdf09f28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -6,9 +6,10 @@
#include "shell/common/asar/archive.h" #include "shell/common/asar/archive.h"
#include <algorithm> #include <algorithm>
#include <sstream> #include <string_view>
#include "base/base_paths.h" #include "base/base_paths.h"
#include "base/containers/map_util.h"
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
@ -17,6 +18,7 @@
#include "base/strings/string_util_win.h" #include "base/strings/string_util_win.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "shell/common/asar/asar_util.h" #include "shell/common/asar/asar_util.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
namespace asar { namespace asar {
@ -37,19 +39,10 @@ std::optional<base::FilePath> Archive::RelativePath() const {
return relative_path; return relative_path;
} }
std::optional<std::unordered_map<std::string, IntegrityPayload>> namespace {
LoadIntegrityConfigCache() {
static base::NoDestructor<
std::optional<std::unordered_map<std::string, IntegrityPayload>>>
integrity_config_cache;
// Skip loading if cache is already loaded auto LoadIntegrityConfig() {
if (integrity_config_cache->has_value()) { absl::flat_hash_map<std::string, IntegrityPayload> cache;
return *integrity_config_cache;
}
// Init cache
*integrity_config_cache = std::unordered_map<std::string, IntegrityPayload>();
// Load integrity config from exe resource // Load integrity config from exe resource
HMODULE module_handle = ::GetModuleHandle(NULL); HMODULE module_handle = ::GetModuleHandle(NULL);
@ -65,8 +58,8 @@ LoadIntegrityConfigCache() {
PLOG(FATAL) << "LoadResource failed."; PLOG(FATAL) << "LoadResource failed.";
} }
auto* res_data = static_cast<const char*>(::LockResource(rcData)); const auto* res_data = static_cast<const char*>(::LockResource(rcData));
int res_size = SizeofResource(module_handle, resource); const auto res_size = SizeofResource(module_handle, resource);
if (!res_data) { if (!res_data) {
PLOG(FATAL) << "Failed to integrity config from exe resource."; PLOG(FATAL) << "Failed to integrity config from exe resource.";
@ -77,9 +70,8 @@ LoadIntegrityConfigCache() {
} }
// Parse integrity config payload // Parse integrity config payload
std::string integrity_config_payload = std::string(res_data, res_size);
std::optional<base::Value> root = std::optional<base::Value> root =
base::JSONReader::Read(integrity_config_payload); base::JSONReader::Read(std::string_view{res_data, res_size});
if (!root.has_value()) { if (!root.has_value()) {
LOG(FATAL) << "Invalid integrity config: NOT a valid JSON."; LOG(FATAL) << "Invalid integrity config: NOT a valid JSON.";
@ -91,6 +83,7 @@ LoadIntegrityConfigCache() {
} }
// Parse each individual file integrity config // Parse each individual file integrity config
cache.reserve(file_configs->size());
for (size_t i = 0; i < file_configs->size(); i++) { for (size_t i = 0; i < file_configs->size(); i++) {
// Skip invalid file configs // Skip invalid file configs
const base::Value::Dict* ele_dict = (*file_configs)[i].GetIfDict(); const base::Value::Dict* ele_dict = (*file_configs)[i].GetIfDict();
@ -122,37 +115,30 @@ LoadIntegrityConfigCache() {
header_integrity.algorithm = HashAlgorithm::kSHA256; header_integrity.algorithm = HashAlgorithm::kSHA256;
header_integrity.hash = base::ToLowerASCII(*value); header_integrity.hash = base::ToLowerASCII(*value);
integrity_config_cache->value()[base::ToLowerASCII(*file)] = cache.insert_or_assign(base::ToLowerASCII(*file),
std::move(header_integrity); std::move(header_integrity));
} }
return *integrity_config_cache; return cache;
} }
const auto& GetIntegrityConfigCache() {
static const auto cache = base::NoDestructor(LoadIntegrityConfig());
return *cache;
}
} // namespace
std::optional<IntegrityPayload> Archive::HeaderIntegrity() const { std::optional<IntegrityPayload> Archive::HeaderIntegrity() const {
std::optional<base::FilePath> relative_path = RelativePath(); const std::optional<base::FilePath> relative_path = RelativePath();
// Callers should have already asserted this CHECK(relative_path);
CHECK(relative_path.has_value());
// Load integrity config from exe resource const auto key = base::ToLowerASCII(base::WideToUTF8(relative_path->value()));
std::optional<std::unordered_map<std::string, IntegrityPayload>>
integrity_config = LoadIntegrityConfigCache();
if (!integrity_config.has_value()) {
LOG(WARNING) << "Failed to integrity config from exe resource.";
return std::nullopt;
}
// Convert Window rel path to UTF8 lower case if (const auto* payload = base::FindOrNull(GetIntegrityConfigCache(), key))
std::string rel_path_utf8 = base::WideToUTF8(relative_path.value().value()); return *payload;
rel_path_utf8 = base::ToLowerASCII(rel_path_utf8);
// Find file integrity config LOG(FATAL) << "Failed to find file integrity info for " << key;
auto iter = integrity_config.value().find(rel_path_utf8);
if (iter == integrity_config.value().end()) {
LOG(FATAL) << "Failed to find file integrity info for " << rel_path_utf8;
}
return iter->second;
} }
} // namespace asar } // namespace asar