host.cpp (4387B)
1 // SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com> 2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) 3 4 #include "host.h" 5 6 #include "common/assert.h" 7 #include "common/heterogeneous_containers.h" 8 #include "common/log.h" 9 #include "common/string_util.h" 10 11 #include <cstdarg> 12 #include <shared_mutex> 13 14 Log_SetChannel(Host); 15 16 namespace Host { 17 static std::pair<const char*, u32> LookupTranslationString(std::string_view context, std::string_view msg); 18 19 static constexpr u32 TRANSLATION_STRING_CACHE_SIZE = 4 * 1024 * 1024; 20 using TranslationStringMap = PreferUnorderedStringMap<std::pair<u32, u32>>; 21 using TranslationStringContextMap = PreferUnorderedStringMap<TranslationStringMap>; 22 static std::shared_mutex s_translation_string_mutex; 23 static TranslationStringContextMap s_translation_string_map; 24 static std::vector<char> s_translation_string_cache; 25 static u32 s_translation_string_cache_pos; 26 } // namespace Host 27 28 std::pair<const char*, u32> Host::LookupTranslationString(std::string_view context, std::string_view msg) 29 { 30 // TODO: TranslatableString, compile-time hashing. 31 32 TranslationStringContextMap::iterator ctx_it; 33 TranslationStringMap::iterator msg_it; 34 std::pair<const char*, u32> ret; 35 s32 len; 36 37 // Shouldn't happen, but just in case someone tries to translate an empty string. 38 if (msg.empty()) [[unlikely]] 39 { 40 ret.first = &s_translation_string_cache[0]; 41 ret.second = 0; 42 return ret; 43 } 44 45 s_translation_string_mutex.lock_shared(); 46 ctx_it = s_translation_string_map.find(context); 47 48 if (ctx_it == s_translation_string_map.end()) [[unlikely]] 49 goto add_string; 50 51 msg_it = ctx_it->second.find(msg); 52 if (msg_it == ctx_it->second.end()) [[unlikely]] 53 goto add_string; 54 55 ret.first = &s_translation_string_cache[msg_it->second.first]; 56 ret.second = msg_it->second.second; 57 s_translation_string_mutex.unlock_shared(); 58 return ret; 59 60 add_string: 61 s_translation_string_mutex.unlock_shared(); 62 s_translation_string_mutex.lock(); 63 64 if (s_translation_string_cache.empty()) [[unlikely]] 65 { 66 // First element is always an empty string. 67 s_translation_string_cache.resize(TRANSLATION_STRING_CACHE_SIZE); 68 s_translation_string_cache[0] = '\0'; 69 s_translation_string_cache_pos = 0; 70 } 71 72 if ((len = 73 Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], 74 TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) 75 { 76 ERROR_LOG("WARNING: Clearing translation string cache, it might need to be larger."); 77 s_translation_string_cache_pos = 0; 78 if ((len = 79 Internal::GetTranslatedStringImpl(context, msg, &s_translation_string_cache[s_translation_string_cache_pos], 80 TRANSLATION_STRING_CACHE_SIZE - 1 - s_translation_string_cache_pos)) < 0) 81 { 82 Panic("Failed to get translated string after clearing cache."); 83 len = 0; 84 } 85 } 86 87 // New context? 88 if (ctx_it == s_translation_string_map.end()) 89 ctx_it = s_translation_string_map.emplace(context, TranslationStringMap()).first; 90 91 // Impl doesn't null terminate, we need that for C strings. 92 // TODO: do we want to consider aligning the buffer? 93 const u32 insert_pos = s_translation_string_cache_pos; 94 s_translation_string_cache[insert_pos + static_cast<u32>(len)] = 0; 95 96 ctx_it->second.emplace(msg, std::pair<u32, u32>(insert_pos, static_cast<u32>(len))); 97 s_translation_string_cache_pos = insert_pos + static_cast<u32>(len) + 1; 98 99 ret.first = &s_translation_string_cache[insert_pos]; 100 ret.second = static_cast<u32>(len); 101 s_translation_string_mutex.unlock(); 102 return ret; 103 } 104 105 const char* Host::TranslateToCString(std::string_view context, std::string_view msg) 106 { 107 return LookupTranslationString(context, msg).first; 108 } 109 110 std::string_view Host::TranslateToStringView(std::string_view context, std::string_view msg) 111 { 112 const auto mp = LookupTranslationString(context, msg); 113 return std::string_view(mp.first, mp.second); 114 } 115 116 std::string Host::TranslateToString(std::string_view context, std::string_view msg) 117 { 118 return std::string(TranslateToStringView(context, msg)); 119 } 120 121 void Host::ClearTranslationCache() 122 { 123 s_translation_string_mutex.lock(); 124 s_translation_string_map.clear(); 125 s_translation_string_cache_pos = 0; 126 s_translation_string_mutex.unlock(); 127 }