/*
* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ImageKnifeData } from '../model/ImageKnifeData';
import util from '@ohos.util';
import { IMemoryCache } from './IMemoryCache';
export class MemoryLruCache implements IMemoryCache {
maxMemory: number = 0
currentMemory: number = 0
maxSize: number = 0
private lruCache: util.LRUCache<string, ImageKnifeData>
readonly defaultMaxSize: number = 65536;
readonly defaultSize: number = 512;
readonly defaultMaxMemorySize: number = 10 * 1024 * 1024 * 1024;
readonly defaultMemorySize: number = 1024 * 1024 * 1024;
constructor(size: number, memory: number) {
if (size <= 0 || size > this.defaultMaxSize) {
size = this.defaultSize;
}
if (memory <= 0 || memory > this.defaultMaxMemorySize) {
memory = this.defaultMemorySize;
}
this.lruCache = new util.LRUCache(size);
this.maxMemory = memory;
this.maxSize = size;
this.currentMemory = 0;
}
// 添加缓存键值对
put(key: string, value: ImageKnifeData): void {
if (key == null || value == null) {
throw new Error('key or value is invalid ');
}
let size = this.getImageKnifeDataSize(value)
if (size <= 0 || size >= this.maxMemory) {
return
}
// 如果size满了的话,需要按照LRU的方式删除第一个
if (this.lruCache.length == this.maxSize && !this.lruCache.contains(key)) {
this.remove(this.lruCache.keys()[0])
} else if (this.lruCache.contains(key)) {
this.remove(key)
}
this.lruCache.put(key, value)
this.currentMemory += size
this.trimToSize()
}
get(key: string): ImageKnifeData | undefined {
return this.lruCache.get(key)
}
// 移除键为key的缓存
remove(key: string): ImageKnifeData | undefined {
if (key == null) {
throw new Error('key is null,checking the parameter');
}
let remove: ImageKnifeData | undefined = this.lruCache.remove(key)
if (remove !== undefined) {
this.removeMemorySize(remove)
}
return remove
}
removeAll(): void {
this.lruCache.clear()
this.currentMemory = 0;
}
size(): number {
return this.lruCache.length
}
// 移除较少使用的缓存数据
trimToSize(): void {
while (true) {
if (this.currentMemory <= this.maxMemory || this.lruCache.isEmpty()) {
break
}
let delkey = this.lruCache.keys()[0]
let data: ImageKnifeData | undefined = this.lruCache.get(delkey)
if (data != undefined) {
this.removeMemorySize(data)
}
this.lruCache.remove(delkey)
}
}
private removeMemorySize(value: ImageKnifeData): void {
if (value.source != undefined) {
if (typeof value.source === 'string' && value.source != '') {
this.currentMemory -= value.source.length
} else if (value.source == '') {
for (let index = 0;index < value.imageAnimator!.length;index++) {
let pixelMap = value.imageAnimator![index].src as PixelMap
this.currentMemory -= pixelMap.getPixelBytesNumber()
}
} else {
this.currentMemory -= value.source.getPixelBytesNumber();
// value.source.release()
}
}
}
private getImageKnifeDataSize(value: ImageKnifeData): number {
if (value.source != undefined) {
if (typeof value.source === 'string' && value.source != '') {
return value.source.length
} else if (value.source == '') {
let size: number = 0
for (let index = 0; index < value.imageAnimator!.length; index++) {
let pixelMap = value.imageAnimator![index].src as PixelMap
size += pixelMap.getPixelBytesNumber()
}
return size
} else {
return value.source.getPixelBytesNumber();
}
}
return 0
}
}