1/*
2 * Copyright (C) 2022 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 */
15import { info } from '../../log/Log';
16
17/**
18 * 数据缓存期限
19 */
20const file_cache_due = 24 * 60 * 60 * 1000;
21const db_version = 6;
22/**
23 * 初始化indexed db
24 */
25export function initIndexedDB(): Promise<IDBDatabase> {
26  return new Promise((resolve, reject) => {
27    let request = indexedDB.open('smart_perf', db_version);
28    request.onerror = function (event): void {};
29    request.onsuccess = function (event): void {
30      let db = request.result;
31      resolve(db);
32    };
33    request.onupgradeneeded = function (event): void {
34      // @ts-ignore
35      let db = event!.target!.result;
36      if (db.objectStoreNames.contains('trace_file')) {
37        info('delete trace_file table');
38        db.deleteObjectStore('trace_file');
39      }
40      info('create trace_file table');
41      let objectStore = db.createObjectStore('trace_file', { keyPath: 'file_index' });
42      objectStore.createIndex('file_no', 'file_no');
43      objectStore.createIndex('file_id', 'file_id');
44      objectStore.createIndex('file_time', 'file_time');
45      objectStore.createIndex('file_buffer', 'file_buffer');
46    };
47  });
48}
49
50/**
51 * 删除过期数据
52 * @param db
53 */
54export function deleteExpireData(db: IDBDatabase): void {
55  if (db?.objectStoreNames.contains('trace_file')) {
56    let objectStore = db.transaction(['trace_file'], 'readwrite').objectStore('trace_file');
57    let request = objectStore.getAll();
58    request.onsuccess = function (event): void {
59      let now = new Date().getTime();
60      for (let re of request.result) {
61        if (now - re.file_time > file_cache_due) {
62          objectStore.delete(re.file_index);
63        }
64      }
65      db.close();
66    };
67    request.onerror = function (): void {
68      info('delete expire data failed');
69    };
70  }
71}
72
73/**
74 * 缓存数据
75 * @param db 数据库链接对象
76 * @param oldFileId 上次打开的文件id
77 * @param fileId 当前打开的文件id
78 * @param buffer 二进制数据
79 */
80export function cacheTraceFileBuffer(db: IDBDatabase, oldFileId: string, fileId: string, buffer: ArrayBuffer): void {
81  if (db?.objectStoreNames.contains('trace_file')) {
82    let objectStore = db.transaction(['trace_file'], 'readwrite').objectStore('trace_file');
83    let request = objectStore.index('file_id').getAll(oldFileId);
84    request.onsuccess = function (event): void {
85      for (let re of request.result) {
86        objectStore.delete(re.file_index);
87      }
88      info('delete file success');
89      let size = buffer.byteLength;
90      let index = 0;
91      let no = 0;
92      let time = new Date().getTime();
93      while (index < size) {
94        let sliceLen = Math.min(size - index, 4 * 1024 * 1024);
95        objectStore.add({
96          file_index: randomUUID(),
97          file_no: no,
98          file_id: fileId,
99          file_time: time,
100          file_buffer: buffer.slice(index, index + sliceLen),
101        });
102        no++;
103        index += sliceLen;
104      }
105      info('cache file success', fileId, buffer.byteLength);
106      db.close();
107    };
108    request.onerror = function (ev): void {
109      info('delete error', fileId);
110    };
111  }
112}
113
114function randomUUID(): string {
115  // @ts-ignore
116  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c: unknown) =>
117    //@ts-ignore
118    (c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
119  );
120}
121
122/**
123 * 获取当前文件的 二进制数据
124 * @param fileId
125 */
126export function getTraceFileBuffer(fileId: string): Promise<ArrayBuffer | null> {
127  return new Promise((resolve) => {
128    if (fileId === null || fileId === undefined || fileId === '') {
129      resolve(new Uint8Array(0).buffer);
130    } else {
131      initIndexedDB().then((db) => {
132        if (db?.objectStoreNames.contains('trace_file')) {
133          let request = db
134            .transaction(['trace_file'], 'readwrite')
135            .objectStore('trace_file')
136            .index('file_id')
137            .getAll(fileId);
138          request.onsuccess = function (ev): void {
139            let totalLen = 0;
140            let arr = request.result.sort((a, b) => a.file_no - b.file_no);
141            for (let re of arr) {
142              totalLen += re.file_buffer.byteLength;
143            }
144            let buffer = new Uint8Array(totalLen);
145            let offset = 0;
146            for (let re of arr) {
147              let ua = new Uint8Array(re.file_buffer);
148              buffer.set(ua, offset);
149              offset += re.file_buffer.byteLength;
150            }
151            arr.length = 0;
152            request.result.length = 0;
153            resolve(buffer.buffer);
154          };
155        } else {
156          resolve(new Uint8Array(0).buffer);
157        }
158      });
159    }
160  });
161}
162