已开启
修复了kv4cj转arkts接口出现的三个问题 #11
修复了kv4cj转arkts接口出现的三个问题 #11
已开启
LH米乐创建于 6月25日
560 个文件变更+61613-8
@@ -0,0 +1 @@
1+third_party/MMKV-1.3.6/OpenHarmony/dependencies/hvigor-4.3.0.tgz filter=lfs diff=lfs merge=lfs -text
@@ -1,14 +1,27 @@
1.git/1.git/
2-.gitee/
3-.vscode/
4*.macrocall2*.macrocall
5*.cj.macrocall3*.cj.macrocall
6.cache4.cache
7lib5lib
8target6target
9cjpm.lock7cjpm.lock
10-ci_test8+test/report
11-test_temp9+/node_modules
12-test/log10+/oh_modules
13-test/tmp11+/local.properties
14-test/report12+/.idea
13+**/build
14+/.hvigor
15+.cxx
16+/.clangd
17+/.clang-format
18+/.clang-tidy
19+**/.test
20+/.appanalyzer
21+**/cj_res
22+**/*.cj.macrocall
23+**/ability_mainability_entry.cj
24+**/module_**_entry.cj
25+**/IDL_Dependencies_List~
26+**/build-script-cache
27+oh-package-lock.json5
@@ -0,0 +1,10 @@
1+{
2+ "app": {
3+ "bundleName": "com.example.kv4cj",
4+ "vendor": "example",
5+ "versionCode": 1000000,
6+ "versionName": "1.0.0",
7+ "icon": "$media:layered_image",
8+ "label": "$string:app_name"
9+ }
10+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "app_name",
5+ "value": "kv4cj"
6+ }
7+ ]
8+}
@@ -0,0 +1,7 @@
1+{
2+ "layered-image":
3+ {
4+ "background" : "$media:background",
5+ "foreground" : "$media:foreground"
6+ }
7+}
@@ -0,0 +1,8 @@
1+/node_modules
2+/oh_modules
3+/.preview
4+/build
5+/.cxx
6+/.test
7+**/IDL_Dependencies_List~
8+**/build-script-cache
@@ -0,0 +1,17 @@
1+/**
2+ * Use these variables when you tailor your ArkTS code. They must be of the const type.
3+ */
4+export const HAR_VERSION = '1.0.0';
5+export const BUILD_MODE_NAME = 'debug';
6+export const DEBUG = true;
7+export const TARGET_NAME = 'default';
8+ 
9+/**
10+ * BuildProfile Class is used only for compatibility purposes.
11+ */
12+export default class BuildProfile {
13+ static readonly HAR_VERSION = HAR_VERSION;
14+ static readonly BUILD_MODE_NAME = BUILD_MODE_NAME;
15+ static readonly DEBUG = DEBUG;
16+ static readonly TARGET_NAME = TARGET_NAME;
17+}
@@ -0,0 +1,26 @@
1+{
2+ "apiType": "stageMode",
3+ "buildOption": {
4+ "externalNativeOptions": {
5+ "path": "./src/main/cpp/CMakeLists.txt",
6+ "arguments": "-DOHOS_STL=c++_static",
7+ "cppFlags": "-DMMKV_STL_SHARED=0 -funwind-tables -fasynchronous-unwind-tables -O2",
8+ "abiFilters": ["arm64-v8a", "x86_64"],
9+ },
10+ "cangjieOptions": {
11+ "path": "./cjpm.toml",
12+ "abiFilters": ["arm64-v8a", "x86_64"],
13+ "strictCheckDependencies": false
14+ }
15+ },
16+ "buildOptionSet": [
17+ ],
18+ "targets": [
19+ {
20+ "name": "default"
21+ },
22+ {
23+ "name": "ohosTest"
24+ }
25+ ]
26+}
@@ -0,0 +1,32 @@
1+[package]
2+ cjc-version = "0.48.2"
3+ compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION}\""
4+ description = "CangjieUI Application"
5+ link-option = ""
6+ name = "kv4cj"
7+ output-type = "dynamic"
8+ src-dir = "./src/main/cangjie"
9+ target-dir = ""
10+ version = "1.0.0"
11+ package-configuration = {}
12+ scripts = {}
13+ 
14+[profile]
15+ build = {incremental = true, lto = ""}
16+ customized-option = {debug = "-g -Woff all -Won apilevel-check", release = "--fast-math -O2 -s -Woff all -Won apilevel-check"}
17+ test = {}
18+ 
19+[target.aarch64-linux-ohos]
20+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/compiler/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" -L \"${DEVECO_CANGJIE_HOME}/build/linux_ohos_aarch64_llvm/openssl\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
21+[target.aarch64-linux-ohos.bin-dependencies]
22+ path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
23+ package-option = {}
24+ 
25+[target.x86_64-linux-ohos]
26+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/compiler/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" -L \"${DEVECO_CANGJIE_HOME}/build/linux_ohos_x86_64_llvm/openssl\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
27+[target.x86_64-linux-ohos.bin-dependencies]
28+ path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}"]
29+ 
30+[target.x86_64-unknown-windows-gnu.bin-dependencies]
31+ path-option = ["${X86_64_LIBS}", "${X86_64_MACRO_LIBS}"]
32+ package-option = {}
@@ -0,0 +1,6 @@
1+import { harTasks } from '@ohos/hvigor-ohos-plugin';
2+ 
3+export default {
4+ system: harTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
5+ plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
6+}
@@ -0,0 +1,12 @@
1+{
2+ "name": "@cangjie-tpc/mmkv4cj",
3+ "version": "1.0.0",
4+ "description": "",
5+ "main": "",
6+ "author": "cangjie-tpc",
7+ "types": "",
8+ "license": "BSD 3-Clause",
9+ "repository": "https://gitcode.com/Cangjie-TPC/kv4cj",
10+ "homepage": "https://gitcode.com/Cangjie-TPC/kv4cj",
11+ "dependencies": {}
12+}
@@ -0,0 +1,1017 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
3+ */
4+ 
5+package kv4cj
6+ 
7+public type MMKVMode = UInt32
8+/**
9+ * Single-process mode. The default mode on an MMKV instance.
10+ */
11+public let MMKV_SINGLE_PROCESS: MMKVMode = 0x1
12+/**
13+ * Multi-process mode.
14+ * To enable multi-process accessing of an MMKV instance, you must set this mode whenever you getting that instance.
15+ */
16+public let MMKV_MULTI_PROCESS: MMKVMode = 0x2
17+ 
18+@C
19+struct ByteArray {
20+ var len: UIntNative = 0
21+ var data: CPointer<Byte> = CPointer<Byte>()
22+}
23+ 
24+@C
25+struct DataList {
26+ var size: UIntNative = 0
27+ var data: CPointer<UIntNative> = CPointer<UIntNative>()
28+}
29+ 
30+public struct NativeBuffer {
31+ public var pointer: CPointer<Unit> = CPointer<Unit>()
32+ public var size: Int32 = 0
33+}
34+ 
35+public class MMKV {
36+ var mmkv: UInt64
37+ let sizeofSize_t: Int64 = unsafe { getSizeofSize_t() }
38+ private static var gCallbackHandler: MMKVHandler = MMKVHandler()
39+ private static var rootDir: ?String = None
40+ 
41+ init(kv: UInt64) {
42+ this.mmkv = kv
43+ }
44+ 
45+ /**
46+ * Initialize MMKV with customize settings.
47+ * You must call one of the initializeMMKV() methods on App startup process before using MMKV.
48+ * @param rootDir The root folder of MMKV, defaults to $(FilesDir)/mmkv.
49+ */
50+ public static func initializeMMKV(rootDir: String): Unit {
51+ initializeMMKV(rootDir, MMKVLogInfo, gCallbackHandler)
52+ }
53+ 
54+ /**
55+ * Initialize MMKV with customize settings.
56+ * You must call one of the initializeMMKV() methods on App startup process before using MMKV.
57+ * @param rootDir The root folder of MMKV.
58+ * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.
59+ */
60+ public static func initializeMMKV(rootDir: String, logLevel: MMKVLogLevel): Unit {
61+ checkLogLevel(logLevel)
62+ initializeMMKV(rootDir, logLevel, gCallbackHandler)
63+ }
64+ 
65+ /**
66+ * Initialize MMKV with customize settings.
67+ * You must call one of the initializeMMKV() methods on App startup process before using MMKV.
68+ * @param rootDir The root folder of MMKV.
69+ * @param handler The log Redirecting handler.
70+ */
71+ public static func initializeMMKV(rootDir: String, handler: MMKVHandler): Unit {
72+ gCallbackHandler = handler
73+ initializeMMKV(rootDir, MMKVLogInfo, gCallbackHandler)
74+ }
75+ 
76+ /**
77+ * Initialize MMKV with customize settings.
78+ * You must call one of the initializeMMKV() methods on App startup process before using MMKV.
79+ * @param rootDir The root folder of MMKV.
80+ * @param logLevel The log level of MMKV, defaults to {@link MMKVLogLevel#LevelInfo}.
81+ * @param handler The log Redirecting handler.
82+ */
83+ public static func initializeMMKV(rootDir: String, logLevel: MMKVLogLevel, handler: MMKVHandler): Unit {
84+ checkLogLevel(logLevel)
85+ gCallbackHandler = handler
86+ unsafe {
87+ let cstr = LibC.mallocCString(rootDir)
88+ initializeMMKV_c(cstr, logLevel, gCallbackHandler)
89+ LibC.free(cstr)
90+ }
91+ MMKV.rootDir = rootDir
92+ }
93+ 
94+ /**
95+ * Notify MMKV that App is about to exit. It's totally fine not calling it at all.
96+ */
97+ public static func onExit(): Unit {
98+ unsafe { onExit_c() }
99+ }
100+ 
101+ private static func checkLogLevel(level: MMKVLogLevel): Unit {
102+ if (level < MMKVLogDebug || level > MMKVLogNone) {
103+ throw MMKVException("Log level ${level} is illegal value!")
104+ }
105+ }
106+ 
107+ private static func checkMMKVMode(mode: MMKVMode): Unit {
108+ if (mode < MMKV_SINGLE_PROCESS || mode > MMKV_MULTI_PROCESS) {
109+ throw MMKVException("MMKVMode [${mode}] is illegal value!")
110+ }
111+ }
112+ 
113+ private static func checkMMKVHandle(kv: UInt64, mmapID: String): MMKV {
114+ if (kv == 0) {
115+ throw MMKVException("Fail to create an MMKV instance [${mmapID}] in native.")
116+ }
117+ return MMKV(kv)
118+ }
119+ 
120+ private static func checkRootDir(): Unit {
121+ match (rootDir) {
122+ case None => throw MMKVException("You should Call MMKV.initializeMMKV() first.")
123+ case Some(_) => return
124+ }
125+ }
126+ 
127+ /**
128+ * Create the default MMKV instance in single-process mode.
129+ * @return An MMKV instance.
130+ */
131+ public static func defaultMMKV(): MMKV {
132+ checkRootDir()
133+ let mode: UInt32 = MMKV_SINGLE_PROCESS
134+ let cryptKey = CPointer<UInt8>()
135+ let kv: UInt64 = unsafe { defaultMMKV_c(mode, CString(cryptKey)) }
136+ return checkMMKVHandle(kv, "mmkv.default")
137+ }
138+ 
139+ /**
140+ * Create the default MMKV instance in customize process mode, with an encryption key.
141+ * @param mode The process mode of the MMKV instance.
142+ * @return An MMKV instance.
143+ */
144+ public static func defaultMMKV(mode: MMKVMode): MMKV {
145+ checkRootDir()
146+ checkMMKVMode(mode)
147+ let cryptKey = CPointer<UInt8>()
148+ let kv: UInt64 = unsafe { defaultMMKV_c(mode, CString(cryptKey)) }
149+ return checkMMKVHandle(kv, "mmkv.default")
150+ }
151+ 
152+ /**
153+ * Create the default MMKV instance in customize process mode, with an encryption key.
154+ * @param mode The process mode of the MMKV instance.
155+ * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).
156+ * @return An MMKV instance.
157+ */
158+ public static func defaultMMKV(mode: MMKVMode, cryptKey: String): MMKV {
159+ checkRootDir()
160+ checkMMKVMode(mode)
161+ var kv: UInt64 = 0
162+ unsafe {
163+ let cstr = LibC.mallocCString(cryptKey)
164+ kv = defaultMMKV_c(mode, cstr)
165+ LibC.free(cstr)
166+ }
167+ return checkMMKVHandle(kv, "mmkv.default")
168+ }
169+ 
170+ /**
171+ * Create an MMKV instance with an unique ID (in single-process mode).
172+ * @param mmapID The unique ID of the MMKV instance.
173+ * @return An MMKV instance.
174+ */
175+ public static func mmkvWithID(mmapID: String): MMKV {
176+ checkRootDir()
177+ var kv: UInt64 = 0
178+ let mode: UInt32 = MMKV_SINGLE_PROCESS
179+ let cryptKey = CString(CPointer<UInt8>())
180+ let rootPath = CString(CPointer<UInt8>())
181+ unsafe {
182+ let cstr = LibC.mallocCString(mmapID)
183+ kv = mmkvWithID_c(cstr, mode, cryptKey, rootPath)
184+ LibC.free(cstr)
185+ }
186+ return checkMMKVHandle(kv, mmapID)
187+ }
188+ 
189+ /**
190+ * Create an MMKV instance in single-process or multi-process mode.
191+ * @param mmapID The unique ID of the MMKV instance.
192+ * @param mode The process mode of the MMKV instance.
193+ * @return An MMKV instance.
194+ */
195+ public static func mmkvWithID(mmapID: String, mode: MMKVMode): MMKV {
196+ checkRootDir()
197+ checkMMKVMode(mode)
198+ var kv: UInt64 = 0
199+ let cryptKey = CString(CPointer<UInt8>())
200+ let rootPath = CString(CPointer<UInt8>())
201+ unsafe {
202+ let cstr = LibC.mallocCString(mmapID)
203+ kv = mmkvWithID_c(cstr, mode, cryptKey, rootPath)
204+ LibC.free(cstr)
205+ }
206+ return checkMMKVHandle(kv, mmapID)
207+ }
208+ 
209+ /**
210+ * Create an MMKV instance in customize process mode, with an encryption key.
211+ * @param mmapID The unique ID of the MMKV instance.
212+ * @param mode The process mode of the MMKV instance.
213+ * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).
214+ * @return An MMKV instance.
215+ */
216+ public static func mmkvWithID(mmapID: String, mode: MMKVMode, cryptKey: String): MMKV {
217+ checkRootDir()
218+ checkMMKVMode(mode)
219+ var kv: UInt64 = 0
220+ let rootPath = CString(CPointer<UInt8>())
221+ unsafe {
222+ let cstrMmapID = LibC.mallocCString(mmapID)
223+ let cstrCryptKey = LibC.mallocCString(cryptKey)
224+ kv = mmkvWithID_c(cstrMmapID, mode, cstrCryptKey, rootPath)
225+ LibC.free(cstrMmapID)
226+ LibC.free(cstrCryptKey)
227+ }
228+ return checkMMKVHandle(kv, mmapID)
229+ }
230+ 
231+ /**
232+ * Create an MMKV instance in customize folder.
233+ * @param mmapID The unique ID of the MMKV instance.
234+ * @param rootPath The folder of the MMKV instance.
235+ * @return An MMKV instance.
236+ */
237+ public static func mmkvWithID(mmapID: String, rootPath: String): MMKV {
238+ checkRootDir()
239+ var kv: UInt64 = 0
240+ let mode: UInt32 = MMKV_SINGLE_PROCESS
241+ let cryptKey = CString(CPointer<UInt8>())
242+ unsafe {
243+ let cstrMmapID = LibC.mallocCString(mmapID)
244+ let cstrRootPath = LibC.mallocCString(rootPath)
245+ kv = mmkvWithID_c(cstrMmapID, mode, cryptKey, cstrRootPath)
246+ LibC.free(cstrMmapID)
247+ LibC.free(cstrRootPath)
248+ }
249+ return checkMMKVHandle(kv, mmapID)
250+ }
251+ 
252+ /**
253+ * Create an MMKV instance with customize settings all in one.
254+ * @param mmapID The unique ID of the MMKV instance.
255+ * @param mode The process mode of the MMKV instance.
256+ * @param cryptKey The encryption key of the MMKV instance (no more than 16 bytes).
257+ * @param rootPath The folder of the MMKV instance.
258+ * @return An MMKV instance.
259+ */
260+ public static func mmkvWithID(mmapID: String, mode: MMKVMode, cryptKey: String, rootPath: String): MMKV {
261+ checkRootDir()
262+ checkMMKVMode(mode)
263+ var kv: UInt64 = 0
264+ unsafe {
265+ let cstrMmapID = LibC.mallocCString(mmapID)
266+ let cstrCryptKey = LibC.mallocCString(cryptKey)
267+ let cstrRootPath = LibC.mallocCString(rootPath)
268+ kv = mmkvWithID_c(cstrMmapID, mode, cstrCryptKey, cstrRootPath)
269+ LibC.free(cstrMmapID)
270+ LibC.free(cstrCryptKey)
271+ LibC.free(cstrRootPath)
272+ }
273+ return checkMMKVHandle(kv, mmapID)
274+ }
275+ 
276+ public func set(key: String, value: Bool): Bool {
277+ var ret: Bool
278+ unsafe {
279+ let cstr = LibC.mallocCString(key)
280+ ret = setBool_c(mmkv, cstr, value)
281+ LibC.free(cstr)
282+ }
283+ return ret
284+ }
285+ 
286+ public func set(key: String, value: Int32): Bool {
287+ var ret: Bool
288+ unsafe {
289+ let cstr = LibC.mallocCString(key)
290+ ret = setInt32_t_c(mmkv, cstr, value)
291+ LibC.free(cstr)
292+ }
293+ return ret
294+ }
295+ 
296+ public func set(key: String, value: UInt32): Bool {
297+ var ret: Bool
298+ unsafe {
299+ let cstr = LibC.mallocCString(key)
300+ ret = setUint32_t_c(mmkv, cstr, value)
301+ LibC.free(cstr)
302+ }
303+ return ret
304+ }
305+ 
306+ public func set(key: String, value: Int64): Bool {
307+ var ret: Bool
308+ unsafe {
309+ let cstr = LibC.mallocCString(key)
310+ ret = setInt64_t_c(mmkv, cstr, value)
311+ LibC.free(cstr)
312+ }
313+ return ret
314+ }
315+ 
316+ public func set(key: String, value: UInt64): Bool {
317+ var ret: Bool
318+ unsafe {
319+ let cstr = LibC.mallocCString(key)
320+ ret = setUint64_t_c(mmkv, cstr, value)
321+ LibC.free(cstr)
322+ }
323+ return ret
324+ }
325+ 
326+ public func set(key: String, value: Float32): Bool {
327+ var ret: Bool
328+ unsafe {
329+ let cstr = LibC.mallocCString(key)
330+ ret = setFloat_c(mmkv, cstr, value)
331+ LibC.free(cstr)
332+ }
333+ return ret
334+ }
335+ 
336+ public func set(key: String, value: Float64): Bool {
337+ var ret: Bool
338+ unsafe {
339+ let cstr = LibC.mallocCString(key)
340+ ret = setDouble_c(mmkv, cstr, value)
341+ LibC.free(cstr)
342+ }
343+ return ret
344+ }
345+ 
346+ public func set(key: String, value: String): Bool {
347+ var ret: Bool
348+ unsafe {
349+ let cstrKey = LibC.mallocCString(key)
350+ let cstrValue =LibC.mallocCString(value)
351+ ret = setString_c(mmkv, cstrKey, cstrValue)
352+ LibC.free(cstrKey)
353+ LibC.free(cstrValue)
354+ }
355+ return ret
356+ }
357+ 
358+ public func set(key: String, value: Array<Byte>): Bool {
359+ let ptrValue = unsafe { acquireArrayRawData(value) }
360+ var ptrArr = CPointer<ByteArray>(unsafe { malloc(UIntNative(sizeofSize_t * 2)) }) // arr
361+ if (ptrArr.isNull()) {
362+ throw MMKVException("Malloc failed!")
363+ }
364+ var arr = ByteArray()
365+ arr.len = UIntNative(value.size)
366+ arr.data = CPointer<Byte>(ptrValue.pointer)
367+ unsafe { ptrArr.write(arr) }
368+ 
369+ var ret: Bool
370+ unsafe {
371+ let cstr = LibC.mallocCString(key)
372+ ret = setBytes_c(mmkv, cstr, ptrArr)
373+ LibC.free(cstr)
374+ releaseArrayRawData(ptrValue)
375+ free(CPointer<Unit>(ptrArr))
376+ }
377+ 
378+ return ret
379+ }
380+ 
381+ public func set(key: String, value: HashSet<String>): Bool {
382+ var pDataList: CPointer<DataList> = CPointer<DataList>()
383+ 
384+ if (!value.isEmpty()) {
385+ var itset = value.iterator()
386+ let size = value.size
387+ pDataList = CPointer<DataList>(unsafe { malloc(UIntNative(sizeofSize_t * (size + 1))) })
388+ if (pDataList.isNull()) {
389+ throw MMKVException("Malloc failed!")
390+ }
391+ var pList = CPointer<UIntNative>(pDataList)
392+ unsafe { pList.write(0, UIntNative(size)) }
393+ for (i in 0..size) {
394+ var element = itset.next()
395+ match (element) {
396+ case Some(v) =>
397+ unsafe {
398+ let cstr = LibC.mallocCString(v)
399+ var ptr = cstr.getChars()
400+ pList.write(i + 1, ptr.toUIntNative())
401+ }
402+ case None => break
403+ }
404+ }
405+ } else {
406+ pDataList = CPointer<DataList>(unsafe { malloc(UIntNative(sizeofSize_t * 2)) })
407+ if (pDataList.isNull()) {
408+ throw MMKVException("Malloc failed!")
409+ }
410+ let dataList = DataList()
411+ unsafe { pDataList.write(dataList) }
412+ }
413+ 
414+ var ret: Bool
415+ unsafe {
416+ let cstr = LibC.mallocCString(key)
417+ ret = setStringSet_c(mmkv, cstr, pDataList)
418+ LibC.free(cstr)
419+ }
420+ 
421+ if (!value.isEmpty()) {
422+ for (i in 0..value.size) {
423+ var pList = CPointer<CPointer<UIntNative>>(pDataList)
424+ unsafe { pList.read(i + 1).asResource().close() }
425+ }
426+ }
427+ unsafe{ free(CPointer<Unit>(pDataList)) }
428+ 
429+ return ret
430+ 
431+ }
432+ 
433+ public func getBool(key: String, defaultValue!: Bool = false): Bool {
434+ var ret: Bool
435+ unsafe {
436+ let cstr = LibC.mallocCString(key)
437+ ret = getBool_c(mmkv, cstr, defaultValue)
438+ LibC.free(cstr)
439+ }
440+ return ret
441+ }
442+ 
443+ public func getInt32(key: String, defaultValue!: Int32 = 0): Int32 {
444+ var ret: Int32
445+ unsafe {
446+ let cstr = LibC.mallocCString(key)
447+ ret = getInt32_c(mmkv, cstr, defaultValue)
448+ LibC.free(cstr)
449+ }
450+ return ret
451+ }
452+ 
453+ public func getUInt32(key: String, defaultValue!: UInt32 = 0): UInt32 {
454+ var ret: UInt32
455+ unsafe {
456+ let cstr = LibC.mallocCString(key)
457+ ret = getUInt32_c(mmkv, cstr, defaultValue)
458+ LibC.free(cstr)
459+ }
460+ return ret
461+ }
462+ 
463+ public func getInt64(key: String, defaultValue!: Int64 = 0): Int64 {
464+ var ret: Int64
465+ unsafe {
466+ let cstr = LibC.mallocCString(key)
467+ ret = getInt64_c(mmkv, cstr, defaultValue)
468+ LibC.free(cstr)
469+ }
470+ return ret
471+ }
472+ 
473+ public func getUInt64(key: String, defaultValue!: UInt64 = 0): UInt64 {
474+ var ret: UInt64
475+ unsafe {
476+ let cstr = LibC.mallocCString(key)
477+ ret = getUInt64_c(mmkv, cstr, defaultValue)
478+ LibC.free(cstr)
479+ }
480+ return ret
481+ }
482+ 
483+ public func getFloat32(key: String, defaultValue!: Float32 = 0.0): Float32 {
484+ var ret: Float32
485+ unsafe {
486+ let cstr = LibC.mallocCString(key)
487+ ret = getFloat_c(mmkv, cstr, defaultValue)
488+ LibC.free(cstr)
489+ }
490+ return ret
491+ }
492+ 
493+ public func getFloat64(key: String, defaultValue!: Float64 = 0.0): Float64 {
494+ var ret: Float64
495+ unsafe {
496+ let cstr = LibC.mallocCString(key)
497+ ret = getDouble_c(mmkv, cstr, defaultValue)
498+ LibC.free(cstr)
499+ }
500+ return ret
501+ }
502+ 
503+ public func getString(key: String, defaultValue!: String = ""): String {
504+ var ptr: CString
505+ unsafe {
506+ let cstr = LibC.mallocCString(key)
507+ ptr = getString_c(mmkv, cstr)
508+ LibC.free(cstr)
509+ }
510+ 
511+ if (!ptr.isNull()) {
512+ var str = ptr.toString()
513+ unsafe { ptr.asResource().close() }
514+ return str
515+ } else {
516+ return defaultValue
517+ }
518+ }
519+ 
520+ public func getBytes(key: String, defaultValue!: Array<Byte> = Array<Byte>()): Array<Byte> {
521+ var array: Array<Byte> = Array<Byte>()
522+ var ptrArr: CPointer<ByteArray>
523+ unsafe {
524+ let cstr = LibC.mallocCString(key)
525+ ptrArr = getBytes_c(mmkv, cstr)
526+ LibC.free(cstr)
527+ }
528+ if (ptrArr.isNotNull()) {
529+ var arr = unsafe { ptrArr.read() }
530+ var len: Int64 = Int64(arr.len)
531+ var ptrArrData = CPointer<Byte>(arr.data)
532+ 
533+ array = Array<Byte>(len, repeat: 0)
534+ for (i in 0..len) {
535+ array[i] = unsafe { ptrArrData.read(i) }
536+ }
537+ 
538+ if (len > 0) {
539+ unsafe { free(CPointer<Unit>(ptrArrData)) }
540+ }
541+ unsafe { free(CPointer<Unit>(ptrArr)) }
542+ 
543+ return array
544+ } else {
545+ return defaultValue
546+ }
547+ }
548+ 
549+ public func getStringSet(key: String, defaultValue!: HashSet<String> = HashSet<String>()): HashSet<String> {
550+ var set = HashSet<String>()
551+ var pDataList: CPointer<DataList>
552+ unsafe {
553+ let cstr = LibC.mallocCString(key)
554+ pDataList = getStringSet_c(mmkv, cstr)
555+ LibC.free(cstr)
556+ }
557+ if (pDataList.isNotNull()) {
558+ var dataList = unsafe { pDataList.read() }
559+ let size: Int64 = Int64(dataList.size)
560+ var ptr = unsafe { CPointer<CPointer<UIntNative>>(pDataList) + 1 }
561+ for (i in 0..size) {
562+ var pcstr = unsafe { CPointer<UInt8>(ptr.read(i)) }
563+ set.add(CString(pcstr).toString())
564+ unsafe { free(CPointer<Unit>(pcstr)) }
565+ }
566+ 
567+ unsafe { free(CPointer<Unit>(pDataList)) }
568+ 
569+ return set
570+ } else {
571+ return defaultValue
572+ }
573+ }
574+ 
575+ /**
576+ * Batch remove some keys from the MMKV instance.
577+ * @param arrKeys The keys to be removed.
578+ */
579+ public func removeValuesForKeys(arrKeys: Array<String>): Unit {
580+ if (!arrKeys.isEmpty()) {
581+ var size: Int64 = arrKeys.size
582+ var ptr: CPointer<UIntNative> = CPointer<UIntNative>(unsafe { malloc(UIntNative(sizeofSize_t * (size + 1))) })
583+ if (ptr.isNull()) {
584+ throw MMKVException("Malloc failed!")
585+ }
586+ unsafe { ptr.write(0, UIntNative(size)) }
587+ for (i in 0..size) {
588+ unsafe {
589+ let cstr = LibC.mallocCString(arrKeys[i])
590+ ptr.write(i + 1, cstr.getChars().toUIntNative())
591+ }
592+ }
593+ var dataList: CPointer<DataList> = CPointer<DataList>(ptr)
594+ unsafe { removeValuesForKeys_c(mmkv, dataList) }
595+ unsafe {
596+ let p = CPointer<CPointer<UIntNative>>(ptr)
597+ for (i in 0..size) {
598+ p.read(i + 1).asResource().close()
599+ }
600+ free(CPointer<Unit>(ptr))
601+ }
602+ }
603+ }
604+ 
605+ public func removeValueForKey(key: String): Unit {
606+ unsafe {
607+ let cstr = LibC.mallocCString(key)
608+ removeValueForKey_c(mmkv, cstr)
609+ LibC.free(cstr)
610+ }
611+ }
612+ 
613+ /**
614+ * Clear all the key-values inside the MMKV instance.
615+ */
616+ public func clearAll(): Unit {
617+ unsafe { clearAll_c(mmkv) }
618+ }
619+ 
620+ /**
621+ * The {@link #totalSize()} of an MMKV instance won't reduce after deleting key-values,
622+ * call this method after lots of deleting if you care about disk usage.
623+ * Note that {@link #clearAll()} has a similar effect.
624+ */
625+ public func trim(): Unit {
626+ unsafe { trim_c(mmkv) }
627+ }
628+ 
629+ /**
630+ * Call this method if the MMKV instance is no longer needed in the near future.
631+ * Any subsequent call to any MMKV instances with the same ID is undefined behavior.
632+ */
633+ public func close(): Unit {
634+ unsafe { close_c(mmkv) }
635+ mmkv = 0
636+ }
637+ 
638+ /**
639+ * Save all mmap memory to file synchronously.
640+ * You don't need to call this, really, I mean it.
641+ * Unless you worry about the device running out of battery.
642+ */
643+ public func sync(): Unit {
644+ unsafe { sync_c(mmkv, true) }
645+ }
646+ 
647+ /**
648+ * Save all mmap memory to file asynchronously.
649+ * No need to call this unless you worry about the device running out of battery.
650+ */
651+ public func async(): Unit {
652+ unsafe { sync_c(mmkv, false) }
653+ }
654+ 
655+ /**
656+ * Clear memory cache of the MMKV instance.
657+ * You can call it on memory warning.
658+ * Any subsequent call to the MMKV instance will trigger all key-values loading from the file again.
659+ */
660+ public func clearMemoryCache(): Unit {
661+ unsafe { clearMemoryCache_c(mmkv) }
662+ }
663+ 
664+ /**
665+ * @return The root folder of MMKV, defaults to $(FilesDir)/mmkv.
666+ */
667+ public static func getRootDir(): String {
668+ return rootDir.getOrThrow()
669+ }
670+ 
671+ /**
672+ * backup one MMKV instance to dstDir
673+ * @param mmapID the MMKV ID to backup
674+ * @param dstDir the backup destination directory
675+ */
676+ public static func backupOneToDirectory(mmapID: String, dstDir: String): Bool {
677+ let ret: Bool
678+ unsafe {
679+ let cstrMmapID = LibC.mallocCString(mmapID)
680+ let cstrDstDir = LibC.mallocCString(dstDir)
681+ ret = backupOne(cstrMmapID, cstrDstDir, CString(CPointer<UInt8>()))
682+ LibC.free(cstrMmapID)
683+ LibC.free(cstrDstDir)
684+ }
685+ return ret
686+ }
687+ 
688+ /**
689+ * backup one MMKV instance to dstDir
690+ * @param mmapID the MMKV ID to backup
691+ * @param rootPath the customize root path of the MMKV, if null then backup from the root dir of MMKV
692+ * @param dstDir the backup destination directory
693+ */
694+ public static func backupOneToDirectory(mmapID: String, dstDir: String, rootPath: String): Bool {
695+ let ret: Bool
696+ unsafe {
697+ let cstrMmapID = LibC.mallocCString(mmapID)
698+ let cstrDstDir = LibC.mallocCString(dstDir)
699+ let cstrRootPath = LibC.mallocCString(rootPath)
700+ ret = backupOne(cstrMmapID, cstrDstDir, cstrRootPath)
701+ LibC.free(cstrMmapID)
702+ LibC.free(cstrDstDir)
703+ LibC.free(cstrRootPath)
704+ }
705+ return ret
706+ }
707+ 
708+ /**
709+ * restore one MMKV instance from srcDir
710+ * @param mmapID the MMKV ID to restore
711+ * @param srcDir the restore source directory
712+ */
713+ public static func restoreOneFromDirectory(mmapID: String, srcDir: String): Bool {
714+ let ret: Bool
715+ unsafe {
716+ let cstrMmapID = LibC.mallocCString(mmapID)
717+ let cstrSrcDir = LibC.mallocCString(srcDir)
718+ ret = restoreOne(cstrMmapID, cstrSrcDir, CString(CPointer<UInt8>()))
719+ LibC.free(cstrMmapID)
720+ LibC.free(cstrSrcDir)
721+ }
722+ return ret
723+ }
724+ 
725+ /**
726+ * restore one MMKV instance from srcDir
727+ * @param mmapID the MMKV ID to restore
728+ * @param srcDir the restore source directory
729+ * @param rootPath the customize root path of the MMKV, if null then restore to the root dir of MMKV
730+ */
731+ public static func restoreOneFromDirectory(mmapID: String, srcDir: String, rootPath: String): Bool {
732+ let ret: Bool
733+ unsafe {
734+ let cstrMmapID = LibC.mallocCString(mmapID)
735+ let cstrSrcDir = LibC.mallocCString(srcDir)
736+ let cstrRootPath = LibC.mallocCString(rootPath)
737+ ret = restoreOne(cstrMmapID, cstrSrcDir, cstrRootPath)
738+ LibC.free(cstrMmapID)
739+ LibC.free(cstrSrcDir)
740+ LibC.free(cstrRootPath)
741+ }
742+ return ret
743+ }
744+ 
745+ /**
746+ * backup all MMKV instance to dstDir
747+ * @param dstDir the backup destination directory
748+ * @return count of MMKV successfully backuped
749+ */
750+ public static func backupAllToDirectory(dstDir: String): UInt64 {
751+ let ret: UInt64
752+ unsafe {
753+ let cstrDstDir = LibC.mallocCString(dstDir)
754+ ret = backupAll(cstrDstDir, CString(CPointer<UInt8>()))
755+ LibC.free(cstrDstDir)
756+ }
757+ return ret
758+ }
759+ 
760+ /**
761+ * backup all MMKV instance to dstDir
762+ * @param dstDir the backup destination directory
763+ * @param rootPath the customize root path of the MMKV, if null then backup from the root dir of MMKV
764+ * @return count of MMKV successfully backuped
765+ */
766+ public static func backupAllToDirectory(dstDir: String, rootPath: String): UInt64 {
767+ let ret: UInt64
768+ unsafe {
769+ let cstrDstDir = LibC.mallocCString(dstDir)
770+ let cstrRootPath = LibC.mallocCString(rootPath)
771+ ret = backupAll(cstrDstDir, cstrRootPath)
772+ LibC.free(cstrDstDir)
773+ LibC.free(cstrRootPath)
774+ }
775+ return ret
776+ }
777+ 
778+ /**
779+ * restore all MMKV instance from srcDir
780+ * @param srcDir the restore source directory
781+ * @return count of MMKV successfully restored
782+ */
783+ public static func restoreAllFromDirectory(srcDir: String): UInt64 {
784+ let ret: UInt64
785+ unsafe {
786+ let cstrSrcDir = LibC.mallocCString(srcDir)
787+ ret = restoreAll(cstrSrcDir, CString(CPointer<UInt8>()))
788+ LibC.free(cstrSrcDir)
789+ }
790+ return ret
791+ }
792+ 
793+ /**
794+ * restore all MMKV instance from srcDir
795+ * @param srcDir the restore source directory
796+ * @param rootPath the customize root path of the MMKV, if null then restore to the root dir of MMKV
797+ * @return count of MMKV successfully restored
798+ */
799+ public static func restoreAllFromDirectory(srcDir: String, rootPath: String): UInt64 {
800+ let ret: UInt64
801+ unsafe {
802+ let cstrSrcDir = LibC.mallocCString(srcDir)
803+ let cstrRootPath = LibC.mallocCString(rootPath)
804+ ret = restoreAll(cstrSrcDir, cstrRootPath)
805+ LibC.free(cstrSrcDir)
806+ LibC.free(cstrRootPath)
807+ }
808+ return ret
809+ }
810+ 
811+ /**
812+ * Set the log level of MMKV.
813+ * @param level Defaults to {@link MMKVLogLevel#LevelInfo}.
814+ */
815+ public static func setLogLevel(level: MMKVLogLevel): Unit {
816+ checkLogLevel(level)
817+ unsafe { setLogLevel_c(level) }
818+ }
819+ 
820+ /**
821+ * Register a handler for MMKV log redirecting, and error handling.
822+ * @deprecated This method is deprecated.
823+ * Use the {@link #initialize(Context, String, LibLoader, MMKVLogLevel, MMKVHandler)} method instead.
824+ */
825+ public static func registerHandler(handler: MMKVHandler): Unit {
826+ gCallbackHandler = handler
827+ unsafe { setCallbackHandler(gCallbackHandler) }
828+ }
829+ 
830+ /**
831+ * Unregister the handler for MMKV.
832+ */
833+ public static func unregiserHandler(): Unit {
834+ gCallbackHandler = MMKVHandler()
835+ unsafe { setCallbackHandler(gCallbackHandler) }
836+ }
837+ 
838+ /**
839+ * Check whether the MMKV file is valid or not.
840+ * Note: Don't use this to check the existence of the instance, the result is undefined on nonexistent files.
841+ */
842+ public static func isFileValid(mmapID: String): Bool {
843+ let ret: Bool
844+ unsafe {
845+ let cstrMmapID = LibC.mallocCString(mmapID)
846+ ret = isFileValid_c(cstrMmapID, CString(CPointer<UInt8>()))
847+ LibC.free(cstrMmapID)
848+ }
849+ return ret
850+ }
851+ 
852+ /**
853+ * Check whether the MMKV file is valid or not on customize folder.
854+ * @param mmapID The unique ID of the MMKV instance.
855+ * @param rootPath The folder of the MMKV instance, defaults to $(FilesDir)/mmkv.
856+ */
857+ public static func isFileValid(mmapID: String, rootPath: String): Bool {
858+ let ret: Bool
859+ unsafe {
860+ let cstrMmapID = LibC.mallocCString(mmapID)
861+ let cstrRootPath = LibC.mallocCString(rootPath)
862+ ret = isFileValid_c(cstrMmapID, cstrRootPath)
863+ LibC.free(cstrMmapID)
864+ LibC.free(cstrRootPath)
865+ }
866+ return ret
867+ }
868+ 
869+ public static func pageSize(): Int64 {
870+ unsafe { pageSize_c() }
871+ }
872+ 
873+ public func creatBuffer(size: UIntNative): NativeBuffer {
874+ var CPtrAddr: CPointer<Unit> = unsafe { malloc(size) }
875+ if (CPtrAddr.isNull()) {
876+ throw MMKVException("Malloc failed!")
877+ }
878+ var buffer: NativeBuffer = NativeBuffer()
879+ buffer.pointer = CPtrAddr
880+ buffer.size = Int32(size)
881+ return buffer
882+ }
883+ 
884+ public func destoryBuffer(buffer: NativeBuffer): Unit {
885+ unsafe { free(buffer.pointer) }
886+ }
887+ 
888+ /**
889+ * Write the value of the key to the native buffer.
890+ * @return The size written. Return -1 on any error.
891+ */
892+ public func writeValueToBuffer(key: String, buffer: NativeBuffer): Int32 {
893+ unsafe {
894+ var cstr = LibC.mallocCString(key)
895+ var ret = writeValueToBuffer_c(mmkv, cstr, buffer.pointer, buffer.size)
896+ LibC.free(cstr)
897+ return ret
898+ }
899+ }
900+ 
901+ /**
902+ * Get the size consumption of the key's value.
903+ * Note: might be a little bigger than value's length.
904+ * @param key The key of the value.
905+ * @param actualSize whether or not actual Size
906+ */
907+ public func getValueSize(key: String, actualSize: Bool): UInt64 {
908+ unsafe {
909+ var cstr = LibC.mallocCString(key)
910+ var ret = getValueSize_c(mmkv, cstr, actualSize)
911+ LibC.free(cstr)
912+ return ret
913+ }
914+ }
915+ 
916+ /**
917+ * Check whether or not MMKV contains the key.
918+ * @param key The key of the value.
919+ */
920+ public func containsKey(key: String): Bool {
921+ unsafe {
922+ var cstr = LibC.mallocCString(key)
923+ var ret = containsKey_c(mmkv, cstr)
924+ LibC.free(cstr)
925+ return ret
926+ }
927+ }
928+ 
929+ /**
930+ * @return The total count of all the keys.
931+ */
932+ public func count(): UInt64 {
933+ return unsafe {count_c(mmkv)}
934+ }
935+ 
936+ /**
937+ * Get the size of the underlying file. Align to the disk block size, typically 4K for an Android device.
938+ */
939+ public func totalSize(): UInt64 {
940+ return unsafe {totalSize_c(mmkv)}
941+ }
942+ 
943+ /**
944+ * Get the actual used size of the MMKV instance.
945+ * This size might increase and decrease as MMKV doing insertion and full write back.
946+ */
947+ public func actualSize(): UInt64 {
948+ return unsafe {actualSize_c(mmkv)}
949+ }
950+ 
951+ /**
952+ * @return All the keys.
953+ */
954+ public func allKeys():HashSet<String> {
955+ var set = HashSet<String>()
956+ let pDataList: CPointer<DataList> = unsafe { allKeys_c(mmkv) }
957+ if (pDataList.isNotNull()) {
958+ var dataList = unsafe { pDataList.read() }
959+ let len: Int64 = Int64(dataList.size)
960+ var ptr = unsafe { CPointer<CPointer<UIntNative>>(pDataList) + 1 }
961+ for (i in 0..len) {
962+ var pcstr = unsafe { CPointer<UInt8>(ptr.read(i)) }
963+ set.add(CString(pcstr).toString())
964+ unsafe { free(CPointer<Unit>(pcstr)) }
965+ }
966+ 
967+ unsafe { free(CPointer<Unit>(pDataList)) }
968+ return set
969+ } else {
970+ return set //HashSet<None<String>>
971+ }
972+ }
973+ 
974+ /**
975+ * @return The unique ID of the MMKV instance.
976+ */
977+ public func mmapID(): String {
978+ var ret_str: CString = unsafe {mmapID_c(mmkv)}
979+ return ret_str.toString()
980+ }
981+ 
982+ /**
983+ * @return The encryption key (no more than 16 bytes).
984+ */
985+ public func cryptKey(): String {
986+ var ret_str: CString = unsafe {cryptKey_c(mmkv)}
987+ return ret_str.toString()
988+ }
989+ 
990+ /**
991+ * Transform plain text into encrypted text, or vice versa by passing a null encryption key.
992+ * You can also change existing crypt key with a different cryptKey.
993+ * @param cryptKey The new encryption key (no more than 16 bytes).
994+ * @return True if success, otherwise False.
995+ */
996+ public func reKey(cryptKey: String): Bool {
997+ unsafe {
998+ var cstr = LibC.mallocCString(cryptKey)
999+ var ret = reKey_c(mmkv, cstr)
1000+ LibC.free(cstr)
1001+ return ret
1002+ }
1003+ }
1004+ 
1005+ /**
1006+ * Just reset the encryption key (will not encrypt or decrypt anything).
1007+ * Usually you should call this method after another process has {@link #reKey(String)} the multi-process MMKV instance.
1008+ * @param cryptKey The new encryption key (no more than 16 bytes).
1009+ */
1010+ public func checkReSetCryptKey(cryptKey: String): Unit {
1011+ unsafe {
1012+ var cstr = LibC.mallocCString(cryptKey)
1013+ checkReSetCryptKey_c(mmkv, cstr)
1014+ LibC.free(cstr)
1015+ }
1016+ }
1017+}
@@ -0,0 +1,71 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
3+ */
4+ 
5+package kv4cj
6+ 
7+public type MMKVLogLevel = Int32
8+public let MMKVLogDebug: MMKVLogLevel = 0 // not available for release/product build
9+public let MMKVLogInfo: MMKVLogLevel = 1 // default level
10+public let MMKVLogWarning: MMKVLogLevel = 2
11+public let MMKVLogError: MMKVLogLevel = 3
12+public let MMKVLogNone: MMKVLogLevel = 4 // special level used to disable all log messages
13+ 
14+/**
15+ * The recover strategic of MMKV on errors. {@link MMKV#registerHandler}
16+ */
17+public type MMKVRecoverStrategic = Int32
18+/**
19+ * The default strategic is to discard everything on errors.
20+ */
21+public let OnErrorDiscard: MMKVRecoverStrategic = 0
22+/**
23+ * The recover strategic will try to recover as much data as possible.
24+ */
25+public let OnErrorRecover: MMKVRecoverStrategic = 1
26+ 
27+public type MMKVErrorType = Int32
28+public let MMKVCRCCheckFail: MMKVErrorType = 0
29+public let MMKVFileLength: MMKVErrorType = 1
30+ 
31+public type LogHandler = CFunc<(MMKVLogLevel, CString, Int32, CString, CString) -> Unit>
32+public type ErrorHandler = CFunc<(CString) -> Int32>
33+public type ContentChangeHandler = CFunc<(CString) -> Unit>
34+ 
35+/**
36+ * Callback handler for MMKV.
37+ * Callback is called on the operating thread of the MMKV instance.
38+ */
39+@C
40+public struct MMKVHandler {
41+ /**
42+ * Log Redirecting.
43+ * @param level The level of this log.
44+ * @param file The file name of this log.
45+ * @param line The line of code of this log.
46+ * @param function The function name of this log.
47+ * @param message The content of this log.
48+ */
49+ public var mmkvLog: LogHandler = CFunc<(MMKVLogLevel, CString, Int32, CString, CString) -> Unit>(CPointer<Int8>())
50+ 
51+ /**
52+ * By default MMKV will discard all data on crc32-check failure. {@link MMKVRecoverStrategic#OnErrorDiscard}
53+ * @param mmapID The unique ID of the MMKV instance.
54+ * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.
55+ */
56+ public var onMMKVCRCCheckFail: ErrorHandler = CFunc<(CString) -> MMKVRecoverStrategic>(CPointer<Int8>())
57+ 
58+ /**
59+ * By default MMKV will discard all data on file length mismatch. {@link MMKVRecoverStrategic#OnErrorDiscard}
60+ * @param mmapID The unique ID of the MMKV instance.
61+ * @return Return {@link MMKVRecoverStrategic#OnErrorRecover} to recover any data on the file.
62+ */
63+ public var onMMKVFileLengthError: ErrorHandler = CFunc<(CString) -> MMKVRecoverStrategic>(CPointer<Int8>())
64+ 
65+ /**
66+ * Inter-process content change notification.
67+ * Triggered by any method call, such as getXXX() or setXXX() or {@link MMKV#onMMKVContentChange()}.
68+ * @param mmapID The unique ID of the changed MMKV instance.
69+ */
70+ public var onMMKVContentChange: ContentChangeHandler = CFunc<(CString) -> Unit>(CPointer<Int8>())
71+}
@@ -0,0 +1,74 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
3+ */
4+ 
5+package kv4cj
6+ 
7+foreign {
8+ func initializeMMKV_c(rootDir: CString, logLevel: MMKVLogLevel, handler: MMKVHandler): Unit
9+ func onExit_c(): Unit
10+ func defaultMMKV_c(mode: UInt32, cryptKey: CString): UInt64
11+ func mmkvWithID_c(mmapID: CString, mode: UInt32, cryptKey: CString, rootPath: CString): UInt64
12+ 
13+ func setBool_c(kv: UInt64, key: CString, value: Bool): Bool
14+ func setInt32_t_c(mmkv: UInt64, key: CString, value: Int32): Bool
15+ func setUint32_t_c(mmkv: UInt64, key: CString, value: UInt32): Bool
16+ func setInt64_t_c(mmkv: UInt64, key: CString, value: Int64): Bool
17+ func setUint64_t_c(mmkv: UInt64, key: CString, value: UInt64): Bool
18+ func setFloat_c(mmkv: UInt64, key: CString, value: Float32): Bool
19+ func setDouble_c(mmkv: UInt64, key: CString, value: Float64): Bool
20+ func setString_c(mmkv: UInt64, key: CString, value: CString): Bool
21+ func setBytes_c(kv: UInt64, key: CString, value: CPointer<ByteArray>): Bool
22+ func setStringSet_c(kv: UInt64, key: CString, value: CPointer<DataList>): Bool
23+ 
24+ func getBool_c(kv: UInt64, key: CString, defaultValue: Bool): Bool
25+ func getInt32_c(mmkv: UInt64, key: CString, defaultValue: Int32): Int32
26+ func getUInt32_c(mmkv: UInt64, key: CString, defaultValue: UInt32): UInt32
27+ func getInt64_c(mmkv: UInt64, key: CString, defaultValue: Int64): Int64
28+ func getUInt64_c(mmkv: UInt64, key: CString, defaultValue: UInt64): UInt64
29+ func getFloat_c(mmkv: UInt64, key: CString, defaultValue: Float32): Float32
30+ func getDouble_c(mmkv: UInt64, key: CString, defaultValue: Float64): Float64
31+ func getString_c(mmkv: UInt64, key: CString): CString
32+ func getBytes_c(kv: UInt64, key: CString): CPointer<ByteArray>
33+ func getStringSet_c(kv: UInt64, key: CString): CPointer<DataList>
34+ 
35+ func removeValuesForKeys_c(kv: UInt64, arrKeys: CPointer<DataList>): Unit
36+ func removeValueForKey_c(kv: UInt64, key: CString): Unit
37+ func clearAll_c(kv: UInt64): Unit
38+ func trim_c(kv: UInt64): Unit
39+ func close_c(kv: UInt64): Unit
40+ func sync_c(kv: UInt64, sync: Bool): Unit
41+ func clearMemoryCache_c(kv: UInt64): Unit
42+ 
43+ func lock_c(kv: UInt64): Unit
44+ func unlock_c(kv: UInt64): Unit
45+ func tryLock_c(kv: UInt64): Bool
46+ 
47+ func backupOne(mmapID: CString, dstDir: CString, srcDir: CString): Bool
48+ func restoreOne(mmapID: CString, srcDir: CString, dstDir: CString): Bool
49+ func backupAll(dstDir: CString, srcDir: CString): UInt64
50+ func restoreAll(srcDir: CString, dstDir: CString): UInt64
51+ 
52+ func setLogLevel_c(level: MMKVLogLevel): Unit
53+ func setCallbackHandler(handler: MMKVHandler): Unit
54+ func isFileValid_c(mmapID: CString, rootPath: CString): Bool
55+ func pageSize_c(): Int64
56+ 
57+ func writeValueToBuffer_c(mmkv: UInt64, key: CString, ptr: CPointer<Unit>, size: Int32): Int32
58+ func getValueSize_c(mmkv: UInt64, key: CString, actualSize: Bool): UInt64
59+ func containsKey_c(mmkv: UInt64, key: CString): Bool
60+ func count_c(mmkv: UInt64): UInt64
61+ func totalSize_c(mmkv: UInt64): UInt64
62+ func actualSize_c(mmkv: UInt64): UInt64
63+ func allKeys_c(mmkv: UInt64): CPointer<DataList>
64+ func mmapID_c(mmkv: UInt64): CString
65+ 
66+ func cryptKey_c(mmkv: UInt64): CString
67+ func reKey_c(mmkv: UInt64, cryptKey: CString): Bool
68+ func checkReSetCryptKey_c(mmkv: UInt64, cryptKey: CString): Bool
69+ 
70+ 
71+ func getSizeofSize_t(): Int64
72+ func malloc(size: UIntNative): CPointer<Unit>
73+ func free(ptr: CPointer<Unit>): Unit
74+}
@@ -0,0 +1,8 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
3+ */
4+ 
5+package kv4cj
6+ 
7+internal import std.core.*
8+internal import std.collection.*
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved.
3+ */
4+ 
5+package kv4cj
6+ 
7+/*
8+ * The class is MMKV exception handling
9+ */
10+public class MMKVException <: Exception {
11+ private var messages: String = ""
12+ public init() {
13+ super()
14+ }
15+ 
16+ public init(messages: String) {
17+ super(messages)
18+ this.messages = messages
19+ }
20+ 
21+ public override func toString(): String {
22+ return "MMKVException: ${messages}"
23+ }
24+}
@@ -0,0 +1,28 @@
1+# the minimum version of CMake.
2+cmake_minimum_required(VERSION 3.4.1)
3+project(mmkv4cj)
4+ 
5+set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
6+ 
7+add_subdirectory(../../../../third_party/MMKV-1.3.6/Core Core)
8+ 
9+if(DEFINED PACKAGE_FIND_FILE)
10+ include(${PACKAGE_FIND_FILE})
11+endif()
12+ 
13+include_directories(${NATIVERENDER_ROOT_PATH}
14+ ${NATIVERENDER_ROOT_PATH}/include)
15+ 
16+add_library(mmkv4cj SHARED native-mmkv.cpp)
17+ 
18+set_target_properties(mmkv4cj PROPERTIES
19+ CXX_STANDARD 20
20+ CXX_EXTENSIONS OFF
21+ POSITION_INDEPENDENT_CODE ON
22+ )
23+ 
24+#add_compile_definitions(_WIN32)
25+ 
26+target_link_libraries(mmkv4cj PUBLIC
27+ libhilog_ndk.z.so
28+ core)
@@ -0,0 +1,813 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
3+ * Description: MMKV for Cangjie API
4+ * Author: xwx1230846
5+ * Create: 2023-04-12
6+ * Notes: 2023-04-12 Create
7+ */
8+ 
9+#include "native-mmkv.h"
10+#include "MMKVPredef.h"
11+#include "MMBuffer.h"
12+#include "MMKV.h"
13+#include "MMKVLog.h"
14+#include "MemoryFile.h"
15+#include <cstdint>
16+#include <string>
17+ 
18+#ifdef __cplusplus
19+extern "C" {
20+#endif
21+ 
22+using namespace std;
23+using namespace mmkv;
24+ 
25+static MMKVHandler gCallbackHandler;
26+ 
27+#ifdef _WIN32
28+static wchar_t *char2wchar(const char *cchar)
29+{
30+ wchar_t *m_wchar = nullptr;
31+ int len = MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), NULL, 0);
32+ if (len >= 0) {
33+ m_wchar= new wchar_t[len + 1];
34+ }
35+ MultiByteToWideChar(CP_ACP, 0, cchar, strlen(cchar), m_wchar, len);
36+ m_wchar[len]= '\0';
37+ return m_wchar;
38+}
39+#endif
40+ 
41+int64_t getSizeofSize_t()
42+{
43+ return sizeof(size_t);
44+}
45+ 
46+static void mmkvLog(MMKVLogLevel level, const char *file, int line, const char *function, MMKVLog_t message)
47+{
48+ if (gCallbackHandler.logHandler) {
49+ gCallbackHandler.logHandler(level, file, line, function, message.c_str());
50+ }
51+}
52+ 
53+static MMKVRecoverStrategic onMMKVError(const std::string &mmapID, MMKVErrorType errorType)
54+{
55+ if (errorType == MMKVCRCCheckFail) {
56+ if (gCallbackHandler.onMMKVCRCCheckFail) {
57+ MMKVRecoverStrategic ret = gCallbackHandler.onMMKVCRCCheckFail(mmapID.c_str());
58+ return ret;
59+ }
60+ } else {
61+ if (gCallbackHandler.onMMKVFileLengthError) {
62+ MMKVRecoverStrategic ret = gCallbackHandler.onMMKVFileLengthError(mmapID.c_str());
63+ return ret;
64+ }
65+ }
66+ return OnErrorDiscard;
67+}
68+ 
69+static void onMMKVContentChange(const string &mmapID)
70+{
71+ if (gCallbackHandler.onMMKVContentChange) {
72+ gCallbackHandler.onMMKVContentChange(mmapID.c_str());
73+ }
74+}
75+ 
76+void initializeMMKV_c(char *rootDir, int logLevel, MMKVHandler handler)
77+{
78+#ifdef _WIN32
79+ wstring strRootDir(char2wchar(rootDir));
80+#else
81+ string strRootDir(rootDir);
82+#endif
83+ gCallbackHandler = handler;
84+ if (gCallbackHandler.logHandler) {
85+ MMKV::initializeMMKV(strRootDir, (MMKVLogLevel)logLevel, mmkvLog);
86+ } else {
87+ MMKV::initializeMMKV(strRootDir, (MMKVLogLevel)logLevel, nullptr);
88+ }
89+ 
90+ if (gCallbackHandler.onMMKVCRCCheckFail || gCallbackHandler.onMMKVFileLengthError) {
91+ MMKV::registerErrorHandler(onMMKVError);
92+ }
93+ 
94+ if (gCallbackHandler.onMMKVContentChange) {
95+ MMKV::registerContentChangeHandler(onMMKVContentChange);
96+ }
97+}
98+ 
99+void onExit_c()
100+{
101+ MMKV::onExit();
102+}
103+ 
104+MMKV *defaultMMKV_c(MMKVMode mode, char *cryptKey)
105+{
106+ MMKV *kv = nullptr;
107+ 
108+ if (cryptKey != NULL) {
109+ if (strlen(cryptKey) > 0) {
110+ string strCryptKey(cryptKey);
111+ kv = MMKV::defaultMMKV(mode, &strCryptKey);
112+ }
113+ }
114+ 
115+ if (!kv) {
116+ kv = MMKV::defaultMMKV((MMKVMode) mode, nullptr);
117+ }
118+ 
119+ return kv;
120+}
121+ 
122+MMKV *mmkvWithID_c(char *mmapID, MMKVMode mode, char *cryptKey, char *rootPath)
123+{
124+ MMKV *kv = nullptr;
125+ if (!mmapID) {
126+ return kv;
127+ }
128+ 
129+ string strMmapID(mmapID);
130+ 
131+ if (cryptKey && strlen(cryptKey) > 0) {
132+ string strCryptKey(cryptKey);
133+ if (rootPath) {
134+#ifdef _WIN32
135+ wstring strRootPath(char2wchar(rootPath));
136+#else
137+ string strRootPath(rootPath);
138+#endif
139+ kv = MMKV::mmkvWithID(strMmapID, mmkv::DEFAULT_MMAP_SIZE, (MMKVMode)mode, &strCryptKey, &strRootPath);
140+ } else {
141+ kv = MMKV::mmkvWithID(strMmapID, mmkv::DEFAULT_MMAP_SIZE, (MMKVMode)mode, &strCryptKey, nullptr);
142+ }
143+ } else {
144+ if (rootPath) {
145+#ifdef _WIN32
146+ wstring strRootPath(char2wchar(rootPath));
147+#else
148+ string strRootPath(rootPath);
149+#endif
150+ kv = MMKV::mmkvWithID(strMmapID, mmkv::DEFAULT_MMAP_SIZE, (MMKVMode)mode, nullptr, &strRootPath);
151+ } else {
152+ kv = MMKV::mmkvWithID(strMmapID, mmkv::DEFAULT_MMAP_SIZE, (MMKVMode)mode, nullptr, nullptr);
153+ }
154+ }
155+ 
156+ return kv;
157+}
158+ 
159+bool setBool_c(MMKV *kv, char *key, bool value)
160+{
161+ if (kv && key) {
162+ string str_key(key);
163+ return kv->set(value, str_key);
164+ }
165+ return false;
166+}
167+ 
168+bool setInt32_t_c(MMKV *kv, char* key, int32_t value)
169+{
170+ if (kv && key) {
171+ string str_key(key);
172+ return kv->set(value, str_key);
173+ }
174+ return false;
175+}
176+ 
177+bool setUint32_t_c(MMKV *kv, char* key, uint32_t value)
178+{
179+ if (kv && key) {
180+ string str_key(key);
181+ return kv->set(value, str_key);
182+ }
183+ return false;
184+}
185+ 
186+bool setInt64_t_c(MMKV *kv, char* key, int64_t value)
187+{
188+ if (kv && key) {
189+ string str_key(key);
190+ return kv->set(value, str_key);
191+ }
192+ return false;
193+}
194+ 
195+bool setUint64_t_c(MMKV *kv, char* key, uint64_t value)
196+{
197+ if (kv && key) {
198+ string str_key(key);
199+ return kv->set(value, str_key);
200+ }
201+ return false;
202+}
203+ 
204+bool setFloat_c(MMKV *kv, char* key, float value)
205+{
206+ if (kv && key) {
207+ string str_key(key);
208+ return kv->set(value, str_key);
209+ }
210+ return false;
211+}
212+ 
213+bool setDouble_c(MMKV *kv, char* key, double value)
214+{
215+ if (kv && key) {
216+ string str_key(key);
217+ return kv->set(value, str_key);
218+ }
219+ return false;
220+}
221+ 
222+bool setString_c(MMKV *kv, char* key, char* value)
223+{
224+ if (kv && key) {
225+ string str_key(key);
226+ string str_value(value);
227+ return kv->set(str_value, str_key);
228+ }
229+ return false;
230+}
231+ 
232+bool setBytes_c(MMKV *kv, char *key, byteArray *value)
233+{
234+ if (kv && key) {
235+ if (value) {
236+ MMBuffer buffer(0);
237+ buffer = MMBuffer(value->data, value->len);
238+ return kv->set(buffer, key);
239+ } else {
240+ kv->removeValueForKey(key);
241+ return true;
242+ }
243+ }
244+ 
245+ return false;
246+}
247+ 
248+bool setStringSet_c(MMKV *kv, char *key, DataList *value)
249+{
250+ vector<string> keys;
251+ 
252+ if (kv && key) {
253+ if (value) {
254+ keys.reserve(value->size);
255+ for (size_t i = 0; i < value->size; i++) {
256+ string str(value->data[i].charArrayData);
257+ keys.push_back(str);
258+ }
259+ return kv->set(keys, key);
260+ } else {
261+ kv->removeValueForKey(key);
262+ return true;
263+ }
264+ }
265+ 
266+ return false;
267+}
268+ 
269+bool getBool_c(MMKV *kv, char *key, bool defaultValue)
270+{
271+ if (kv && key) {
272+ string str_key(key);
273+ return kv->getBool(str_key, defaultValue);
274+ }
275+ return defaultValue;
276+}
277+ 
278+int32_t getInt32_c(MMKV *kv, char *key, int32_t defaultValue)
279+{
280+ if (kv && key) {
281+ string str_key(key);
282+ return kv->getInt32(str_key, defaultValue);
283+ }
284+ return defaultValue;
285+}
286+ 
287+uint32_t getUInt32_c(MMKV *kv, char *key, uint32_t defaultValue)
288+{
289+ if (kv && key) {
290+ string str_key(key);
291+ return kv->getUInt32(str_key, defaultValue);
292+ }
293+ return defaultValue;
294+}
295+ 
296+int64_t getInt64_c(MMKV *kv, char *key, int64_t defaultValue)
297+{
298+ if (kv && key) {
299+ string str_key(key);
300+ return kv->getInt64(str_key, defaultValue);
301+ }
302+ return defaultValue;
303+}
304+ 
305+uint64_t getUInt64_c(MMKV *kv, char *key, uint64_t defaultValue)
306+{
307+ if (kv && key) {
308+ string str_key(key);
309+ return kv->getUInt64(str_key, defaultValue);
310+ }
311+ return defaultValue;
312+}
313+ 
314+float getFloat_c(MMKV *kv, char *key, float defaultValue)
315+{
316+ if (kv && key) {
317+ string str_key(key);
318+ return kv->getFloat(str_key, defaultValue);
319+ }
320+ return defaultValue;
321+}
322+ 
323+double getDouble_c(MMKV *kv, char *key, double defaultValue)
324+{
325+ if (kv && key) {
326+ string str_key(key);
327+ return kv->getDouble(str_key, defaultValue);
328+ }
329+ return defaultValue;
330+}
331+ 
332+char *getString_c(MMKV *kv, char *key)
333+{
334+ if (kv && key) {
335+ string str_key(key);
336+ string str_value;
337+ 
338+ if (kv->getString(str_key, str_value)) {
339+ char *value = (char *)malloc(str_value.length() + 1);
340+ if (value == NULL) {
341+ return NULL;
342+ }
343+
344+ #ifdef _WIN32
345+ if (strcpy_s(value, str_value.length() + 1, str_value.c_str()) != 0) {
346+ free(value);
347+ return NULL;
348+ }
349+ #else
350+ if (!strncpy(value, str_value.c_str(), str_value.length() + 1)) {
351+ free(value);
352+ return NULL;
353+ }
354+ #endif
355+ return value;
356+ }
357+ }
358+ return NULL;
359+}
360+ 
361+byteArray *getBytes_c(MMKV *kv, char *key)
362+{
363+ if (kv && key) {
364+ string strKey(key);
365+ MMBuffer value = kv->getBytes(strKey);
366+ byteArray *array = (byteArray *)malloc(sizeof(byteArray));
367+ if (value.length() > 0) {
368+ array->data = malloc(value.length());
369+ if (array->data == NULL) {
370+ return NULL;
371+ }
372+ #ifdef _WIN32
373+ memcpy_s(array->data, value.length(), value.getPtr(), value.length());
374+ #else
375+ memcpy(array->data, value.getPtr(), value.length());
376+ #endif
377+ array->len = value.length();
378+ return array;
379+ }
380+ }
381+ return NULL;
382+}
383+ 
384+static void freeData(DataList *array, size_t cnt)
385+{
386+ for (size_t i = 0; i < cnt; i++) {
387+ free(array->data[i].charArrayData);
388+ }
389+}
390+ 
391+static DataList *vector2array(const vector<string> &arr)
392+{
393+ DataList *array;
394+ size_t size;
395+ 
396+ if (arr.size() == 0) {
397+ array = (DataList *)malloc(sizeof(DataList));
398+ if (array == NULL) {
399+ return NULL;
400+ }
401+ } else {
402+ size = arr.size() + 1;
403+ array = (DataList *)malloc(size * getSizeofSize_t());
404+ if (array == NULL) {
405+ return NULL;
406+ }
407+ }
408+ 
409+ array->size = arr.size();
410+ for (size_t i = 0; i < arr.size(); i++) {
411+ char *cstr = (char *)malloc(arr[i].length() + 1);
412+ #ifdef _WIN32
413+ if (strcpy_s(cstr, arr[i].length() + 1, arr[i].c_str()) != 0) {
414+ free(cstr);
415+ freeData(array, i);
416+ free(array);
417+ return NULL;
418+ }
419+ #else
420+ if (!strncpy(cstr, arr[i].c_str(), arr[i].length() + 1)) {
421+ free(cstr);
422+ freeData(array, i);
423+ free(array);
424+ return NULL;
425+ }
426+#endif
427+ array->data[i].charArrayData = cstr;
428+ }
429+ 
430+ return array;
431+}
432+ 
433+DataList *getStringSet_c(MMKV *kv, char *key)
434+{
435+ if (kv && key) {
436+ string strKey(key);
437+ vector<string> value;
438+ bool hasValue = kv->getVector(strKey, value);
439+ if (hasValue) {
440+ return vector2array(value);
441+ }
442+ }
443+ 
444+ return NULL;
445+}
446+ 
447+static vector<string> array2vector(DataList *arrList)
448+{
449+ vector<string> keys;
450+ if (arrList) {
451+ size_t size = arrList->size;
452+ keys.reserve(size);
453+ for (size_t i = 0; i < size; i++) {
454+ keys.push_back(arrList->data[i].charArrayData);
455+ }
456+ }
457+ 
458+ return keys;
459+}
460+ 
461+void removeValuesForKeys_c(MMKV *kv, DataList *arrKeys)
462+{
463+ if (kv && arrKeys) {
464+ vector<string> keys = array2vector(arrKeys);
465+ if (!keys.empty()) {
466+ kv->removeValuesForKeys(keys);
467+ }
468+ }
469+}
470+ 
471+void removeValueForKey_c(MMKV *kv, char *key)
472+{
473+ if (kv) {
474+ kv->removeValueForKey(key);
475+ }
476+}
477+ 
478+void clearAll_c(MMKV *kv)
479+{
480+ if (kv) {
481+ kv->clearAll();
482+ }
483+}
484+ 
485+// MMKV's size won't reduce after deleting key-values
486+// call this method after lots of deleting if you care about disk usage
487+// note that `clearAll` has the similar effect of `trim`
488+void trim_c(MMKV *kv)
489+{
490+ if (kv) {
491+ kv->trim();
492+ }
493+}
494+ 
495+// call this method if the instance is no longer needed in the near future
496+// any subsequent call to the instance is undefined behavior
497+void close_c(MMKV *kv)
498+{
499+ if (kv) {
500+ kv->close();
501+ }
502+}
503+ 
504+void sync_c(MMKV *kv, bool sync)
505+{
506+ if (kv) {
507+ kv->sync((SyncFlag)sync);
508+ }
509+}
510+ 
511+// call this method if you are facing memory-warning
512+// any subsequent call to the instance will load all key-values from file again
513+void clearMemoryCache_c(MMKV *kv)
514+{
515+ if (kv) {
516+ kv->clearMemoryCache();
517+ }
518+}
519+ 
520+void lock_c(MMKV *kv)
521+{
522+ if (kv) {
523+ kv->lock();
524+ }
525+}
526+ 
527+void unlock_c(MMKV *kv)
528+{
529+ if (kv) {
530+ kv->unlock();
531+ }
532+}
533+ 
534+bool tryLock_c(MMKV *kv)
535+{
536+ if (kv) {
537+ return kv->try_lock();
538+ }
539+ 
540+ return false;
541+}
542+ 
543+static MMKVPath_t getString(char *value)
544+{
545+#ifdef _WIN32
546+ return char2wchar(value);
547+#else
548+ return value;
549+#endif
550+}
551+ 
552+// backup one MMKV instance from srcDir to dstDir
553+// if srcDir is null, then backup from the root dir of MMKV
554+bool backupOne(char *mmapID, char *dstDir, char *srcDir)
555+{
556+ MMKVPath_t strdstDir = getString(dstDir);
557+ if (srcDir) {
558+ MMKVPath_t root = getString(srcDir);
559+ if (root.length() > 0) {
560+ return MMKV::backupOneToDirectory(mmapID, strdstDir, &root);
561+ }
562+ }
563+ return MMKV::backupOneToDirectory(mmapID, strdstDir);
564+}
565+ 
566+// restore one MMKV instance from srcDir to dstDir
567+// if dstDir is null, then restore to the root dir of MMKV
568+bool restoreOne(char *mmapID, char *srcDir, char *dstDir)
569+{
570+ MMKVPath_t strSrcDir = getString(srcDir);
571+ if (dstDir) {
572+ MMKVPath_t root = getString(dstDir);
573+ if (root.length() > 0) {
574+ return MMKV::restoreOneFromDirectory(mmapID, strSrcDir, &root);
575+ }
576+ }
577+ return MMKV::restoreOneFromDirectory(mmapID, strSrcDir);
578+}
579+ 
580+// backup all MMKV instance from srcDir to dstDir
581+// if srcDir is null, then backup from the root dir of MMKV
582+// return count of MMKV successfully backuped
583+size_t backupAll(char *dstDir, char *srcDir)
584+{
585+ // historically Android mistakenly use mmapKey as mmapID
586+ // makes everything tricky with customize root
587+ MMKVPath_t strdstDir = getString(dstDir);
588+ if (srcDir) {
589+ MMKVPath_t root = getString(srcDir);
590+ if (root.length() > 0) {
591+ return MMKV::backupAllToDirectory(strdstDir, &root);
592+ }
593+ }
594+ return MMKV::backupAllToDirectory(strdstDir);
595+}
596+ 
597+// restore all MMKV instance from srcDir to dstDir
598+// if dstDir is null, then restore to the root dir of MMKV
599+// return count of MMKV successfully restored
600+size_t restoreAll(char *srcDir, char *dstDir)
601+{
602+ // historically Android mistakenly use mmapKey as mmapID
603+ // makes everything tricky with customize root
604+ MMKVPath_t strSrcDir = getString(srcDir);
605+ if (dstDir) {
606+ MMKVPath_t root = getString(dstDir);
607+ if (root.length() > 0) {
608+ return MMKV::restoreAllFromDirectory(strSrcDir, &root);
609+ }
610+ }
611+ return MMKV::restoreAllFromDirectory(strSrcDir);
612+}
613+ 
614+// check if content been changed by other process
615+void checkContentChanged_c(MMKV *kv)
616+{
617+ if (kv) {
618+ kv->checkContentChanged();
619+ }
620+}
621+ 
622+// MMKVLogInfo by default
623+// pass MMKVLogNone to disable all logging
624+void setLogLevel_c(MMKVLogLevel level)
625+{
626+ MMKV::setLogLevel(level);
627+}
628+ 
629+void setCallbackHandler(MMKVHandler handler)
630+{
631+ gCallbackHandler = handler;
632+ if (gCallbackHandler.logHandler) {
633+ MMKV::registerLogHandler(mmkvLog);
634+ } else {
635+ MMKV::unRegisterLogHandler();
636+ }
637+ 
638+ if (gCallbackHandler.onMMKVCRCCheckFail || gCallbackHandler.onMMKVFileLengthError) {
639+ MMKV::registerErrorHandler(onMMKVError);
640+ } else {
641+ MMKV::unRegisterErrorHandler();
642+ }
643+ 
644+ if (gCallbackHandler.onMMKVContentChange) {
645+ MMKV::registerContentChangeHandler(onMMKVContentChange);
646+ } else {
647+ MMKV::unRegisterContentChangeHandler();
648+ }
649+}
650+ 
651+// detect if the MMKV file is valid or not
652+// Note: Don't use this to check the existence of the instance,
653+// the return value is undefined if the file was never created.
654+bool isFileValid_c(const char *mmapID, char *relatePath)
655+{
656+ const string strMmapID(mmapID);
657+ if (relatePath) {
658+#ifdef _WIN32
659+ wstring strRelatePath(char2wchar(relatePath));
660+#else
661+ string strRelatePath(relatePath);
662+#endif
663+ return MMKV::isFileValid(strMmapID, &strRelatePath);
664+ } else {
665+ return MMKV::isFileValid(strMmapID, nullptr);
666+ }
667+}
668+ 
669+size_t pageSize_c()
670+{
671+ return DEFAULT_MMAP_SIZE;
672+}
673+ 
674+const char *version_c()
675+{
676+ return MMKV_VERSION;
677+}
678+ 
679+int32_t writeValueToBuffer_c(MMKV *kv, char* key, void *ptr, int32_t size)
680+{
681+ if (kv && key && ptr != NULL) {
682+ string strkey(key);
683+ return kv->writeValueToBuffer(strkey, ptr, size);
684+ }
685+ return -1;
686+}
687+ 
688+size_t getValueSize_c(MMKV *kv, char* key, bool actualSize)
689+{
690+ if (kv && key) {
691+ string strkey(key);
692+ return kv->getValueSize(strkey, actualSize);
693+ }
694+ return 0;
695+}
696+ 
697+bool containsKey_c(MMKV *kv, char* key)
698+{
699+ if (kv && key) {
700+ string strkey(key);
701+ return kv->containsKey(strkey);
702+ }
703+ return false;
704+}
705+ 
706+size_t count_c(MMKV* kv)
707+{
708+ if (kv) {
709+ return kv->count();
710+ }
711+ return 0;
712+}
713+ 
714+size_t totalSize_c(MMKV* kv)
715+{
716+ if (kv) {
717+ return kv->totalSize();
718+ }
719+ return 0;
720+}
721+ 
722+size_t actualSize_c(MMKV* kv)
723+{
724+ if (kv) {
725+ return kv->actualSize();
726+ }
727+ return 0;
728+}
729+ 
730+DataList *allKeys_c(MMKV* kv)
731+{
732+ if (kv) {
733+ vector<string> value = kv->allKeys();
734+ return vector2array(value);
735+ }
736+ return NULL;
737+}
738+ 
739+char* mmapID_c(MMKV* kv)
740+{
741+ if (kv) {
742+ string str_mmid = kv->mmapID();
743+ char* char_mmid = (char*)malloc(str_mmid.length() + 1);
744+ if (char_mmid != NULL) {
745+ #ifdef _WIN32
746+ if (strcpy_s(char_mmid, str_mmid.length() + 1, str_mmid.c_str()) != 0) {
747+ free(char_mmid);
748+ return NULL;
749+ }
750+#else
751+ if (!strncpy(char_mmid, str_mmid.c_str(), str_mmid.length() + 1)) {
752+ free(char_mmid);
753+ return NULL;
754+ }
755+#endif
756+ return char_mmid;
757+ }
758+ }
759+ return NULL;
760+}
761+ 
762+char *cryptKey_c(MMKV* kv)
763+{
764+ if (kv) {
765+ string str_cryptkey = kv->cryptKey();
766+ char *char_cryptkey = (char*)malloc(str_cryptkey.length() + 1);
767+ if (char_cryptkey != NULL) {
768+ #ifdef _WIN32
769+ if (strcpy_s(char_cryptkey, str_cryptkey.length() + 1, str_cryptkey.c_str()) != 0) {
770+ free(char_cryptkey);
771+ return NULL;
772+ }
773+ #else
774+ if (!strncpy(char_cryptkey, str_cryptkey.c_str(), str_cryptkey.length() + 1)) {
775+ free(char_cryptkey);
776+ return NULL;
777+ }
778+#endif
779+ return char_cryptkey;
780+ }
781+ }
782+ return NULL;
783+}
784+ 
785+bool reKey_c(MMKV *kv, char *cryptKey)
786+{
787+ if (kv) {
788+ if (cryptKey) {
789+ string str_cryptKey0(cryptKey);
790+ return kv->reKey(str_cryptKey0);
791+ } else {
792+ string str_cryptKey1("");
793+ return kv->reKey(str_cryptKey1);
794+ }
795+ }
796+ return false;
797+}
798+ 
799+void checkReSetCryptKey_c(MMKV *kv, char *cryptKey)
800+{
801+ if (kv) {
802+ if (cryptKey) {
803+ string str_cryptKey(cryptKey);
804+ kv->checkReSetCryptKey(&str_cryptKey);
805+ } else {
806+ kv->checkReSetCryptKey(NULL);
807+ }
808+ }
809+}
810+ 
811+#ifdef __cplusplus
812+}
813+#endif
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023. All rights reserved.
3+ * Description: MMKV for Cangjie API
4+ * Author: xwx1230846
5+ * Create: 2023-04-12
6+ * Notes: 2023-04-12 Create
7+ */
8+ 
9+#include "MMKVPredef.h"
10+#include "MMBuffer.h"
11+#include "MMKV.h"
12+#include "MMKVLog.h"
13+#include "MemoryFile.h"
14+#include <stdint.h>
15+#include <string.h>
16+ 
17+#ifdef __cplusplus
18+extern "C" {
19+#endif
20+ 
21+#ifdef _WIN32
22+extern int memcpy_s(void *dest, uint32_t destsz, const void *src, uint32_t count);
23+extern int strcpy_s(char *dest, uint32_t destsz, const char *src);
24+#endif
25+ 
26+typedef struct {
27+ size_t len;
28+ void *data;
29+}byteArray;
30+ 
31+typedef struct {
32+ size_t size;
33+ union data_t {
34+ byteArray *byteArrayData;
35+ char *charArrayData;
36+ }data[1];
37+}DataList;
38+ 
39+typedef void (*LogHandler4Cj)(MMKVLogLevel level, const char *file, int line, const char *function,
40+ const char *message);
41+typedef MMKVRecoverStrategic (*ErrorHandler4Cj)(const char *mmapID);
42+typedef void (*ContentChangeHandler4Cj)(const char *mmapID);
43+ 
44+typedef struct {
45+ LogHandler4Cj logHandler;
46+ ErrorHandler4Cj onMMKVCRCCheckFail;
47+ ErrorHandler4Cj onMMKVFileLengthError;
48+ ContentChangeHandler4Cj onMMKVContentChange;
49+}MMKVHandler;
50+ 
51+extern int64_t getSizeofSize_t();
52+ 
53+#ifdef __cplusplus
54+}
55+#endif
@@ -0,0 +1 @@
1+export {}
@@ -0,0 +1,6 @@
1+{
2+ "name": "libmmkv4cj.so",
3+ "types": "./Index.d.ts",
4+ "version": "1.0.0",
5+ "description": "MMKV native library for Cangjie"
6+}
@@ -0,0 +1,9 @@
1+{
2+ "module": {
3+ "name": "MMKV",
4+ "type": "har",
5+ "deviceTypes": [
6+ "default"
7+ ]
8+ }
9+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "page_show",
5+ "value": "page from npm package"
6+ }
7+ ]
8+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "page_show",
5+ "value": "page from npm package"
6+ }
7+ ]
8+}
@@ -0,0 +1,8 @@
1+{
2+ "string": [
3+ {
4+ "name": "page_show",
5+ "value": "page from npm package"
6+ }
7+ ]
8+}
@@ -0,0 +1,9 @@
1+package ohos_app_cangjie_MMKV_test
2+ 
3+internal import ohos.ability.AbilityStage
4+ 
5+class MyAbilityStage <: AbilityStage {
6+ public override func onCreate(): Unit {
7+ AppLog.info("MyAbilityStage onCreated.")
8+ }
9+}
@@ -0,0 +1,43 @@
1+[package]
2+ cjc-version = "0.48.2"
3+ compile-option = "--dy-std --cfg=\"${COMPILE_CONDITION}\""
4+ description = "CangjieUI Application"
5+ link-option = ""
6+ name = "ohos_app_cangjie_MMKV_test"
7+ output-type = "dynamic"
8+ src-dir = "."
9+ target-dir = ""
10+ version = "1.0.0"
11+ package-configuration = {}
12+ scripts = {}
13+ 
14+[dependencies]
15+ [dependencies.ohos_app_cangjie_MMKV]
16+ path = "../../../"
17+ version = "1.0.0"
18+ 
19+[profile]
20+ [profile.build]
21+ incremental = true
22+ lto = ""
23+ [profile.customized-option]
24+ debug = "-g -Woff all -Won apilevel-check"
25+ release = "--fast-math -O2 -s -Woff all -Won apilevel-check"
26+ withoutCoverage = "--cfg=\"coverage=false\""
27+ withCoverage = "--cfg=\"coverage=true\""
28+ [profile.test]
29+ 
30+[target.aarch64-linux-ohos]
31+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/compiler/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/aarch64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/aarch64-linux-ohos\" -L \"${DEVECO_CANGJIE_HOME}/build/linux_ohos_aarch64_llvm/openssl\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
32+[target.aarch64-linux-ohos.bin-dependencies]
33+ path-option = ["${AARCH64_LIBS}", "${AARCH64_MACRO_LIBS}", "${AARCH64_KIT_LIBS}"]
34+ package-option = {}
35+ 
36+[target.x86_64-linux-ohos]
37+ compile-option = "-B \"${DEVECO_CANGJIE_HOME}/compiler/third_party/llvm/bin\" -B \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/sysroot/usr/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/clang/15.0.4/lib/x86_64-linux-ohos\" -L \"${DEVECO_OH_NATIVE_HOME}/llvm/lib/x86_64-linux-ohos\" -L \"${DEVECO_CANGJIE_HOME}/build/linux_ohos_x86_64_llvm/openssl\" --sysroot \"${DEVECO_OH_NATIVE_HOME}/sysroot\""
38+[target.x86_64-linux-ohos.bin-dependencies]
39+ path-option = ["${X86_64_OHOS_LIBS}", "${X86_64_OHOS_MACRO_LIBS}", "${X86_64_OHOS_KIT_LIBS}"]
40+ 
41+[target.x86_64-unknown-windows-gnu.bin-dependencies]
42+ path-option = ["${X86_64_LIBS}", "${X86_64_MACRO_LIBS}"]
43+ package-option = {}
@@ -0,0 +1,14 @@
1+/**
2+ * Created on 2025/2/26
3+ */
4+package ohos_app_cangjie_MMKV_test
5+ 
6+import std.unittest.expectEqual
7+import std.unittest.testmacro.Expect
8+ 
9+func unittest() {
10+ let a = "abc"
11+ let b = "b"
12+ @Expect(a.contains(b))
13+ @Expect(a, a)
14+}
@@ -0,0 +1,17 @@
1+package ohos_app_cangjie_MMKV_test
2+ 
3+import std.unittest.TestClass
4+import std.unittest.TestPackage
5+import std.unittest.TestSuite
6+import std.unittest.UnitTestCase
7+import std.unittest.common.Configuration
8+import std.unittest.testmacro.Test
9+import std.unittest.testmacro.TestCase
10+ 
11+@Test
12+class TestExample00 {
13+ @TestCase
14+ func test00(): Unit {
15+ unittest()
16+ }
17+}
@@ -0,0 +1,36 @@
1+package ohos_app_cangjie_MMKV_test
2+ 
3+internal import ohos.base.LengthProp
4+internal import ohos.component.Column
5+internal import ohos.component.Row
6+internal import ohos.component.Text
7+internal import ohos.component.CustomView
8+internal import ohos.component.CJEntry
9+internal import ohos.component.loadNativeView
10+internal import ohos.component.FontWeight
11+internal import ohos.state_manage.SubscriberManager
12+internal import ohos.state_manage.ObservedProperty
13+internal import ohos.state_manage.LocalStorage
14+import ohos.state_macro_manage.Entry
15+import ohos.state_macro_manage.Component
16+import ohos.state_macro_manage.State
17+ 
18+@Entry
19+@Component
20+class MMKVView {
21+ @State
22+ var message: String = "Hello World"
23+ 
24+ func build() {
25+ Row {
26+ Column {
27+ Text(this.message)
28+ .fontSize(50)
29+ .fontWeight(FontWeight.Bold)
30+ .onClick {
31+ evt => this.message = "Hello Cangjie"
32+ }
33+ }.width(100.percent)
34+ }.height(100.percent)
35+ }
36+}
@@ -0,0 +1,9 @@
1+package ohos_app_cangjie_MMKV_test
2+ 
3+import ohos_app_cangjie_MMKV_test.unittest_support.registerTestSuite
4+ 
5+private let register = testsuite()
6+ 
7+func testsuite() {
8+ registerTestSuite {TestExample00()}
9+}
@@ -0,0 +1,28 @@
1+package ohos_app_cangjie_MMKV_test
2+ 
3+internal import ohos.base.AppLog
4+internal import ohos.ability.Want
5+internal import ohos.ability.UIAbility
6+internal import ohos.ability.LaunchParam
7+internal import ohos.ability.LaunchReason
8+internal import ohos.window.WindowStage
9+ 
10+class MainAbility <: UIAbility {
11+ public init() {
12+ super()
13+ registerSelf()
14+ }
15+ 
16+ public override func onCreate(want: Want, launchParam: LaunchParam): Unit {
17+ AppLog.info("MainAbility OnCreated.${want.abilityName}")
18+ match (launchParam.launchReason) {
19+ case LaunchReason.START_ABILITY => AppLog.info("START_ABILITY")
20+ case _ => ()
21+ }
22+ }
23+ 
24+ public override func onWindowStageCreate(windowStage: WindowStage): Unit {
25+ AppLog.info("MainAbility onWindowStageCreate.")
26+ windowStage.loadContent("MMKVView")
27+ }
28+}
@@ -0,0 +1,247 @@
1+package ohos_app_cangjie_MMKV_test.unittest_support
2+ 
3+import ohos.test_runner.TestRunner
4+import ohos.hilog.HilogChannel
5+import ohos.ability_delegator_registry.AbilityDelegatorRegistry
6+import std.unittest.XmlReporter
7+import std.fs.Path
8+import std.fs.Directory
9+import std.collection.filter
10+import std.fs.FileInfo
11+import std.collection.first
12+import std.fs.File
13+import std.collection.forEach
14+import std.convert.Parsable
15+import ohos.bundle_manager.BundleManager
16+import ohos.bundle_manager.BundleFlag
17+import std.collection.any
18+import std.collection.HashMap
19+import std.unittest.TestClass
20+import std.collection.ArrayList
21+import std.unittest.TestSuite
22+import ohos.ability.Want
23+import std.process.Process
24+import ohos.ui_test.UITest
25+import std.unittest.common.Configuration
26+ 
27+@When[coverage == "true"]
28+foreign func __gcov_dump(): Unit
29+ 
30+@When[coverage == "true"]
31+public func gcov_dump(coveragePath: String): Unit {
32+ let logger = HilogChannel(0, 0, "CangjieTest")
33+ logger.info("execute test with coverage")
34+ unsafe { __gcov_dump() }
35+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
36+ abilityDelegator.print("GENERATE_COVERAGE: ${coveragePath}")
37+}
38+ 
39+@When[coverage == "false"]
40+public func gcov_dump(coveragePath: String): Unit {
41+ let logger = HilogChannel(0, 0, "CangjieTest")
42+ logger.info("execute test without coverage")
43+}
44+ 
45+public func registerTestSuite(testClassInstanceGenerator: () -> TestClass): Unit {
46+ OpenHarmonyTestRunner
47+ .testClassInstanceGenerators
48+ .add(testClassInstanceGenerator)
49+}
50+ 
51+public class OpenHarmonyTestRunner <: TestRunner {
52+ private static let logger = HilogChannel(0, 0, "CangjieTest")
53+ public static let testSuiteMap = HashMap<String, TestSuite>()
54+ public static let testClassInstanceGenerators = ArrayList<() -> TestClass>()
55+ private static func getDetailedInfoOf(e: Exception): String {
56+ let sb = StringBuilder("exception: ${e.toString()}\n")
57+ e.getStackTrace() |>
58+ forEach {
59+ s => sb.append(" at ${s.declaringClass}.${s.methodName}(${s.fileName}:${s.lineNumber})\n")
60+ }
61+ sb.toString()
62+ }
63+ 
64+ private func executeTestClass(testClassName: String): Unit {
65+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
66+ logger.info("EXECUTE_CLASS_START: \"${testClassName}\"")
67+ abilityDelegator.print("EXECUTE_CLASS_START: \"${testClassName}\"")
68+ let testSuite = match (OpenHarmonyTestRunner
69+ .testSuiteMap
70+ .get(testClassName)) {
71+ case None => return
72+ case Some(testClassInstanceGenerator) => testClassInstanceGenerator
73+ }
74+ 
75+ testSuite
76+ .runTests()
77+ .reportTo(XmlReporter(Path("/data/storage/el1/base")))
78+ 
79+ let testReportXmlFileName = "test-default.${testClassName}.xml"
80+ match (Directory.readFrom("/data/storage/el1/base/tests") |>
81+ filter {fileInfo => fileInfo.name == testReportXmlFileName} |> first) {
82+ case None => logger.error("${testReportXmlFileName} not found.")
83+ case Some(testReportXmlFileInfo) =>
84+ let testReportXmlFilePath = testReportXmlFileInfo.path
85+ let testReportXmlFileContent = File.readFrom(testReportXmlFilePath)
86+ let xmlReport = String.fromUtf8(testReportXmlFileContent)
87+ logger.info("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
88+ logger.info(xmlReport)
89+ abilityDelegator.print("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
90+ abilityDelegator.print(xmlReport)
91+ logger.info("EXECUTE_CLASS_END: \"${testClassName}\"")
92+ abilityDelegator.print("EXECUTE_CLASS_END: \"${testClassName}\"")
93+ reportTestReport("test-default.${testClassName}.xml")
94+ }
95+ }
96+ private func executeAllTestClasses(): Unit {
97+ for (testClassName in OpenHarmonyTestRunner
98+ .testSuiteMap
99+ .keys()) {
100+ executeTestClass(testClassName)
101+ }
102+ }
103+ private func executeTestCase(testClassName: String, testCaseName: String): Unit {
104+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
105+ abilityDelegator.print("EXECUTE_CLASS_START: \"${testClassName}\"")
106+ logger.info("EXECUTE_METHOD_START: \"${testClassName}#${testCaseName}\"")
107+ let testSuite = match (OpenHarmonyTestRunner
108+ .testSuiteMap
109+ .get(testClassName)) {
110+ case None => return
111+ case Some(testClassInstanceGenerator) => testClassInstanceGenerator
112+ }
113+ let configuration = Configuration()
114+ configuration.setByName("filter", "${testClassName}.${testCaseName}")
115+ testSuite
116+ .runTests(configuration)
117+ .reportTo(XmlReporter(Path("/data/storage/el1/base")))
118+ 
119+ let testReportXmlFileName = "test-default.${testClassName}.xml"
120+ match (Directory.readFrom("/data/storage/el1/base/tests") |>
121+ filter {fileInfo => fileInfo.name == testReportXmlFileName} |> first) {
122+ case None => logger.error("${testReportXmlFileName} not found.")
123+ case Some(testReportXmlFileInfo) =>
124+ let testReportXmlFilePath = testReportXmlFileInfo.path
125+ let testReportXmlFileContent = File.readFrom(testReportXmlFilePath)
126+ let xmlReport = String.fromUtf8(testReportXmlFileContent)
127+ logger.info("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
128+ logger.info(xmlReport)
129+ abilityDelegator.print("XML_REPORT_CONTENT_LENGTH: ${xmlReport.size}")
130+ abilityDelegator.print(xmlReport)
131+ logger.info("EXECUTE_METHOD_END: \"${testClassName}#${testCaseName}\"")
132+ abilityDelegator.print("EXECUTE_CLASS_END: \"${testClassName}\"")
133+ reportTestReport("test-default.${testClassName}.xml")
134+ }
135+ }
136+ private func reportTestReport(testReportName: String): Unit {
137+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
138+ let abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
139+ let bundleName = abilityDelegatorArguments.bundleName
140+ abilityDelegator.print("REPORT_TEST_RESULT: /data/app/el1/100/base/${bundleName}/tests/${testReportName}")
141+ }
142+ public override func onPrepare(): Unit {
143+ logger.info("OpenHarmonyTestRunner onPrepare")
144+ for (testClassInstanceGenerator in OpenHarmonyTestRunner.testClassInstanceGenerators) {
145+ let testClassInstance = try {
146+ testClassInstanceGenerator()
147+ } catch (e: Exception) {
148+ logger.error("exception occurred during testsuite generation:\n${getDetailedInfoOf(e)}")
149+ continue
150+ }
151+ let testSuite = testClassInstance.asTestSuite()
152+ let testSuiteName = testSuite.name
153+ logger.info("Registering TestSuite ${testSuiteName}")
154+ OpenHarmonyTestRunner
155+ .testSuiteMap
156+ .add(testSuiteName, testSuite)
157+ }
158+ }
159+ public override func onRun(): Unit {
160+ logger.info("OpenHarmonyTestRunner onRun")
161+ let abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
162+ let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
163+ UITest.setup()
164+ logger.info("bundleName = ${abilityDelegatorArguments.bundleName}")
165+ logger.info("abilityDelegatorArguments.testRunnerClassName = ${abilityDelegatorArguments.testRunnerClassName}")
166+ logger.info("abilityDelegatorArguments.testCaseNames = ${abilityDelegatorArguments.testCaseNames}")
167+ logger.info("abilityDelegatorArguments.parameters = ${abilityDelegatorArguments.parameters}")
168+ logger.info("Started running tests.")
169+ abilityDelegator.print("OpenHarmonyTestRunner onRun")
170+ let parameters = abilityDelegatorArguments.parameters
171+ let bundleName = abilityDelegatorArguments.bundleName
172+ let bundleInfo = BundleManager.getBundleInfoForSelf(
173+ BundleFlag
174+ .GET_BUNDLE_INFO_WITH_HAP_MODULE
175+ .getValue() | BundleFlag
176+ .GET_BUNDLE_INFO_WITH_APPLICATION
177+ .getValue())
178+ let moduleName = match (parameters.get("-m")) {
179+ case None =>
180+ abilityDelegator.finishTest("Module name was not specified.", -1)
181+ return
182+ case Some(moduleName) =>
183+ if (bundleInfo.hapModulesInfo |> any {hapModuleInfo => hapModuleInfo.name == moduleName}) {
184+ moduleName
185+ } else {
186+ abilityDelegator.finishTest(
187+ "No module with name \"${moduleName}\" was found in bundle ${bundleName}.", -1)
188+ return
189+ }
190+ }
191+ let abilityName = match (bundleInfo.hapModulesInfo |> filter {hapModuleInfo => hapModuleInfo.name == moduleName} |>
192+ first) {
193+ case None =>
194+ abilityDelegator.finishTest("No module with name \"${moduleName}\" was found in bundle ${bundleName}.",
195+ -1)
196+ return
197+ case Some(hapModuleInfo) =>
198+ let mainElementName = hapModuleInfo.mainElementName
199+ logger.info("mainElementName = ${mainElementName}")
200+ mainElementName
201+ }
202+ let want = Want(bundleName: bundleName, moduleName: moduleName, abilityName: abilityName)
203+ abilityDelegator
204+ .startAbility(want)
205+ .get()
206+ let timeOut = match (parameters.get("-s timeout")) {
207+ case None => None<Int64>
208+ case Some(timeOutText) => match (Int64.tryParse(timeOutText)) {
209+ case None =>
210+ abilityDelegator.finishTest("The parameter \"timeout\": ${timeOutText} is invalid", -1)
211+ return
212+ case Some(timeOutValue) => timeOutValue
213+ }
214+ }
215+ spawn {
216+ try {
217+ match (parameters.get("-s class")) {
218+ case None => executeAllTestClasses()
219+ case Some(text) => text.split(",", removeEmpty: true) |>
220+ forEach {
221+ t: String =>
222+ let pair = t.split("#", removeEmpty: true)
223+ let testClassName = match (pair.get(0)) {
224+ case None => return
225+ case Some(text) => text.trimAscii()
226+ }
227+ match (pair.get(1)) {
228+ case None => executeTestClass(testClassName)
229+ case Some(testCaseName) => executeTestCase(testClassName, testCaseName)
230+ }
231+ }
232+ }
233+ } catch (e: Exception) {
234+ logger.error("an exception occurred:\n${getDetailedInfoOf(e)}")
235+ }
236+ var coverage = false
237+ Process
238+ .current
239+ .setEnv("GCOV_PREFIX", "/data/storage/el1/base/cov")
240+ gcov_dump("/data/app/el1/100/base/${bundleName}/cov/")
241+ 
242+ abilityDelegator.finishTest("Test finished.", 0)
243+ }
244+ }
245+}
246+ 
247+let _ = TestRunner.registerCreator("OpenHarmonyTestRunner") {OpenHarmonyTestRunner()}
@@ -0,0 +1,26 @@
1+{
2+ "module": {
3+ "name": "MMKV_test",
4+ "type": "feature",
5+ "description": "$string:module_desc",
6+ "mainElement": "EntryAbility",
7+ "deviceTypes": [
8+ "default"
9+ ],
10+ "deliveryWithInstall": true,
11+ "installationFree": false,
12+ "srcEntry": "ohos_app_cangjie_MMKV_test.MyAbilityStage",
13+ "abilities": [
14+ {
15+ "name": "EntryAbility",
16+ "srcEntry": "ohos_app_cangjie_MMKV_test.MainAbility",
17+ "description": "$string:EntryAbility_desc",
18+ "icon": "$media:layered_image",
19+ "label": "$string:EntryAbility_label",
20+ "startWindowIcon": "$media:startIcon",
21+ "startWindowBackground": "$color:start_window_background",
22+ "exported": true
23+ }
24+ ]
25+ }
26+}
@@ -0,0 +1,8 @@
1+{
2+ "color": [
3+ {
4+ "name": "start_window_background",
5+ "value": "#FFFFFF"
6+ }
7+ ]
8+}
@@ -0,0 +1,16 @@
1+{
2+ "string": [
3+ {
4+ "name": "module_desc",
5+ "value": "module description"
6+ },
7+ {
8+ "name": "EntryAbility_desc",
9+ "value": "description"
10+ },
11+ {
12+ "name": "EntryAbility_label",
13+ "value": "label"
14+ }
15+ ]
16+}
@@ -0,0 +1,7 @@
1+{
2+ "layered-image":
3+ {
4+ "background" : "$media:background",
5+ "foreground" : "$media:foreground"
6+ }
7+}
@@ -0,0 +1,2 @@
1+{
2+}
@@ -91,7 +91,8 @@ MMKV 是一个基于 mmap 的高性能 key-value 存储库,主要用于解决
91 91 
92### 接口说明92### 接口说明
93 93 
94-主要类和函数接口说明详见 [API](./doc/feature_api.md)94+- 仓颉(Cangjie)主要类和函数接口说明详见 [API](./doc/feature_api.md)
95+- HarmonyOS ArkTS 面向对象接口(基于本 HAR + 仓颉桥接封装,含集成步骤与可运行范例 `entry`)详见 [ArkTS API](./doc/arkts_api.md)
95 96 
96## 使用说明97## 使用说明
97 98