duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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 }