1/* 2 * Copyright (c) 2024 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 16const helpUtil = requireInternal('arkts.utils'); 17let locks = helpUtil.locks; 18let ASON = helpUtil.ASON; 19let ConditionVariable = helpUtil.ConditionVariable; 20let isSendable = helpUtil.isSendable; 21 22export const typeErrorCode = 401; 23export class BusinessError extends Error { 24 code: number; 25 constructor(errorcode: number, msg: string) { 26 super(msg); 27 this.name = 'BusinessError'; 28 this.code = errorcode; 29 } 30} 31 32class SendableLruCache { 33 // the following cache is a sendable instance of sendable map 34 private cache: SendableMap<Object, Object>; 35 // Default current size 36 private maxSize: number = 64; 37 // Default maximum size 38 private maxNumber: number = 2147483647; 39 private putCount: number = 0; 40 private createCount: number = 0; 41 private evictionCount: number = 0; 42 private hitCount: number = 0; 43 private missCount: number = 0; 44 public length: number = 0; 45 46 public constructor(capacity?: number) { 47 'use sendable'; 48 if (capacity !== undefined && capacity !== null) { 49 if (capacity <= 0 || capacity % 1 !== 0 || capacity > this.maxNumber) { 50 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${capacity} must be small integer`); 51 throw error; 52 } 53 this.maxSize = capacity; 54 } 55 this.cache = new SendableMap(); 56 } 57 58 private changeCapacity(newCapacity: number): void { 59 while (this.cache.size > newCapacity) { 60 this.cache.delete(this.cache.keys().next().value); 61 this.evictionCount++; 62 this.afterRemoval(true, this.cache.keys(), this.cache.values(), null); 63 } 64 } 65 66 protected afterRemoval(isEvict: boolean, key: Object | undefined | null, value: Object | undefined | null, 67 newValue: Object | undefined | null): void { 68 } 69 70 protected createDefault(key: Object): Object | undefined { 71 if (typeof (key as Object) === 'undefined') { 72 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${key} must be Object`); 73 throw error; 74 } 75 return undefined; 76 } 77 78 public updateCapacity(newCapacity: number): void { 79 if (typeof newCapacity !== 'number') { 80 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${newCapacity} must be number`); 81 throw error; 82 } 83 if (newCapacity <= 0 || newCapacity % 1 !== 0 || newCapacity > this.maxNumber) { 84 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${newCapacity} must be small integer`); 85 throw error; 86 } 87 if (this.cache.size > newCapacity) { 88 this.changeCapacity(newCapacity); 89 } 90 this.length = this.cache.size; 91 this.maxSize = newCapacity; 92 } 93 94 public get(key: Object): Object | undefined { 95 if (typeof (key as Object) === 'undefined' || key === null) { 96 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${key} must be Object`); 97 throw error; 98 } 99 let value: Object; 100 if (this.cache.has(key)) { 101 value = this.cache.get(key); 102 this.hitCount++; 103 this.cache.delete(key); 104 this.cache.set(key, value); 105 return value; 106 } 107 this.missCount++; 108 let createValue: Object | undefined = this.createDefault(key); 109 if (createValue === undefined) { 110 return undefined; 111 } else { 112 value = this.put(key, createValue); 113 this.createCount++; 114 if (value !== undefined) { 115 this.put(key, value); 116 this.afterRemoval(false, key, createValue, value); 117 return value; 118 } 119 return createValue; 120 } 121 } 122 123 public put(key: Object, value: Object): Object | undefined { 124 if (typeof (key as Object) === 'undefined') { 125 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${key} must be Object`); 126 throw error; 127 } 128 if (typeof (value as Object) === 'undefined') { 129 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${value} must be Object`); 130 throw error; 131 } 132 if (key === null || value === null) { 133 let error = new BusinessError(typeErrorCode, `Parameter error. The type of key and value must be Object`); 134 throw error; 135 } 136 let former: Object | undefined = undefined; 137 this.putCount++; 138 if (this.cache.has(key)) { 139 former = this.cache.get(key); 140 this.cache.delete(key); 141 this.afterRemoval(false, key, former, value); 142 } else if (this.cache.size >= this.maxSize) { 143 this.afterRemoval(true, this.cache.keys().next().value, this.cache.values().next().value, null); 144 this.cache.delete(this.cache.keys().next().value); 145 this.evictionCount++; 146 } 147 this.cache.set(key, value); 148 this.length = this.cache.size; 149 former = this.cache.get(key); 150 return former; 151 } 152 153 public remove(key: Object): Object | undefined { 154 if (typeof (key as Object) === 'undefined' || key === null) { 155 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${key} must be Object`); 156 throw error; 157 } 158 let former: Object = undefined; 159 if (this.cache.has(key)) { 160 former = this.cache.get(key); 161 this.cache.delete(key); 162 if (former !== null) { 163 this.afterRemoval(false, key, former, null); 164 this.length = this.cache.size; 165 } 166 } else { 167 this.length = this.cache.size; 168 } 169 return former; 170 } 171 172 public contains(key: Object): boolean { 173 if (typeof (key as Object) === 'undefined') { 174 let error = new BusinessError(typeErrorCode, `Parameter error. The type of ${key} must be Object`); 175 throw error; 176 } 177 let flag: boolean = false; 178 if (this.cache.has(key)) { 179 flag = true; 180 this.hitCount++; 181 let value: Object = this.cache.get(key); 182 this.cache.delete(key); 183 this.cache.set(key, value); 184 this.length = this.cache.size; 185 } else { 186 this.missCount++; 187 } 188 return flag; 189 } 190 191 public getCreateCount(): number { 192 return this.createCount; 193 } 194 195 public getMissCount(): number { 196 return this.missCount; 197 } 198 199 public getRemoveCount(): number { 200 return this.evictionCount; 201 } 202 203 public getMatchCount(): number { 204 return this.hitCount; 205 } 206 207 public getPutCount(): number { 208 return this.putCount; 209 } 210 211 public getCapacity(): number { 212 return this.maxSize; 213 } 214 215 public clear(): void { 216 this.afterRemoval(false, this.cache.keys(), this.cache.values(), null); 217 this.cache.clear(); 218 this.length = this.cache.size; 219 } 220 221 public isEmpty(): boolean { 222 return this.cache.size === 0; 223 } 224 225 public toString(): string { 226 let peek: number = 0; 227 let hitRate: number = 0; 228 let str: string = ''; 229 peek = this.hitCount + this.missCount; 230 if (peek !== 0) { 231 // The value is 100 times larger 232 hitRate = 100 * this.hitCount / peek; 233 } 234 str = 'SendableLruCache[ maxSize = ' + this.maxSize + ', hits = ' + this.hitCount + 235 ', misses = ' + this.missCount + ', hitRate = ' + hitRate + '% ]'; 236 return str; 237 } 238 239 public values(): Object[] { 240 let arr: Array<Object> = []; 241 for (let value of this.cache.values()) { 242 arr.push(value); 243 } 244 return arr; 245 } 246 247 public keys(): Object[] { 248 let arr: Array<Object> = []; 249 for (let key of this.cache.keys()) { 250 arr.push(key); 251 } 252 return arr; 253 } 254 255 public entries(): IterableIterator<[Object, Object]> { 256 return this.cache.entries(); 257 } 258 259 public [Symbol.iterator](): IterableIterator<[Object, Object]> { 260 return this.cache.entries(); 261 } 262} 263 264export default { 265 SendableLruCache: SendableLruCache, 266 locks: locks, 267 ASON: ASON, 268 ConditionVariable: ConditionVariable, 269 isSendable: isSendable, 270}; 271