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