• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "plugins/ets/stdlib/native/core/IntlFormattersCache.h"
17 
18 namespace ark::ets::stdlib::intl {
19 
IntlFormattersCache()20 IntlFormattersCache::IntlFormattersCache()
21 {
22     ASSERT(MAX_SIZE_CACHE > 1U);
23     ASSERT(ERASE_RATIO > 0);
24     ASSERT(ERASE_RATIO < 1);
25     ASSERT(ERASE_AMOUNT > 0);
26 }
27 
NumFmtsCacheInvalidation(ani_env * env,const ParsedOptions & options,ani_status & status)28 LocNumFmt &IntlFormattersCache::NumFmtsCacheInvalidation(ani_env *env, const ParsedOptions &options, ani_status &status)
29 {
30     static LocNumFmt defaultLocNumFmt;
31     auto tag = options.TagString();
32     status = ANI_OK;
33     CacheUMapIterator it;
34     {
35         os::memory::LockHolder lh(mtx_);
36         it = cache_.find(tag);
37         if (it == cache_.end()) {
38             EraseRandFmtsGroupByEraseRatio();
39             // Create new number formatter, number range formatter is empty
40             // Number range formatter will be created via call NumRangeFmtsCacheInvalidation
41             auto *ptr = new icu::number::LocalizedNumberFormatter();
42             if (UNLIKELY(ptr == nullptr)) {
43                 return defaultLocNumFmt;
44             }
45 
46             // Set options
47             status = InitNumFormatter(env, options, *ptr);
48             if (status != ANI_OK) {
49                 return defaultLocNumFmt;
50             }
51 
52             // Save into cache
53             NumberFormatters f;
54             f.numFmt.reset(ptr);
55             auto [iter, isNumFmtInserted] = cache_.insert_or_assign(tag, std::move(f));
56             ASSERT(isNumFmtInserted);
57             it = iter;
58         } else if (it->second.numFmt == nullptr) {
59             // Still not created, now create new number formatter, range formatter is not changed
60             auto *ptr = new icu::number::LocalizedNumberFormatter();
61             if (UNLIKELY(ptr == nullptr)) {
62                 return defaultLocNumFmt;
63             }
64             status = InitNumFormatter(env, options, *ptr);
65             if (status != ANI_OK) {
66                 return defaultLocNumFmt;
67             }
68             it->second.numFmt.reset(ptr);
69         }
70     }
71     return *(it->second.numFmt);
72 }
73 
NumRangeFmtsCacheInvalidation(ani_env * env,const ParsedOptions & options,ani_status & status)74 LocNumRangeFmt &IntlFormattersCache::NumRangeFmtsCacheInvalidation(ani_env *env, const ParsedOptions &options,
75                                                                    ani_status &status)
76 {
77     static LocNumRangeFmt defaultLocNumRangeFmt;
78     auto tag = options.TagString();
79     status = ANI_OK;
80     CacheUMapIterator it;
81     {
82         os::memory::LockHolder lh(mtx_);
83         it = cache_.find(tag);
84         if (it == cache_.end()) {
85             EraseRandFmtsGroupByEraseRatio();
86             // Create new number range formatter, number formatter is empty
87             // Number formatter will be created via call IntlFormattersCacheInvalidation
88             auto *ptr = new icu::number::LocalizedNumberRangeFormatter();
89             if (UNLIKELY(ptr == nullptr)) {
90                 return defaultLocNumRangeFmt;
91             }
92 
93             // Set options
94             status = InitNumRangeFormatter(env, options, *ptr);
95             if (status != ANI_OK) {
96                 return defaultLocNumRangeFmt;
97             }
98 
99             // Save into cache
100             NumberFormatters f;
101             f.numRangeFmt.reset(ptr);
102             auto [iter, isNumRangeFmtInserted] = cache_.insert_or_assign(tag, std::move(f));
103             ASSERT(isNumRangeFmtInserted);
104             it = iter;
105         } else if (it->second.numRangeFmt == nullptr) {
106             // Still not created, now create new number range formatter, number formatter is not changed
107             auto *ptr = new icu::number::LocalizedNumberRangeFormatter();
108             if (UNLIKELY(ptr == nullptr)) {
109                 return defaultLocNumRangeFmt;
110             }
111 
112             status = InitNumRangeFormatter(env, options, *ptr);
113             if (status != ANI_OK) {
114                 return defaultLocNumRangeFmt;
115             }
116             it->second.numRangeFmt.reset(ptr);
117         }
118     }
119     return *(it->second.numRangeFmt);
120 }
121 
EraseRandFmtsGroupByEraseRatio()122 void IntlFormattersCache::EraseRandFmtsGroupByEraseRatio()
123 {
124     os::memory::LockHolder lh(mtx_);
125     // Remove random N "neighbours" items (group) from cache_ if size is maximum
126     if (cache_.size() == MAX_SIZE_CACHE) {
127         // NOLINTNEXTLINE(cert-msc51-cpp)
128         static std::minstd_rand simpleRand(std::time(nullptr));
129         auto delta = static_cast<uint32_t>(simpleRand() % (MAX_SIZE_CACHE - ERASE_AMOUNT + 1U));
130         // delta is in random range [0; MAX_SIZE_CACHE - ERASE_AMOUNT]
131         auto firstIt = cache_.begin();
132         std::advance(firstIt, delta);
133         auto lastIt = firstIt;
134         std::advance(lastIt, ERASE_AMOUNT);
135         cache_.erase(firstIt, lastIt);
136     }
137 }
138 
139 }  // namespace ark::ets::stdlib::intl
140